8000 feat(scaling): Linux hosts over SSH as a provider by adeelahmad · Pull Request #589 · adeelahmad/lense · GitHub
[go: up one dir, main page]

Skip to content

feat(scaling): Linux hosts over SSH as a provider - #589

Merged
adeelahmad merged 4 commits into
mainfrom
claude/project-thread-azm2mb
Oct 18, 2026
Merged

adeelahmad merged 4 commits into
mainfrom
claude/project-thread-azm2mb

Conversation

@adeelahmad
@adeelahmad adeelahmad commented Oct 18, 2026 •
Copy link
Copy Markdown
Owner

Description

Before: auto scaling could add cloud machines (AWS, DigitalOcean, Hetzner) and Proxmox clones, but not Linux machines you already have.

After: a provider of kind "Linux hosts over SSH" takes a list of user@host[:port] and a private key. Each unit is the first-boot script (the mesh's install line, which makes the host a worker node) started on one of those hosts, up to "Workers on each host" per host; letting the unit go stops its process group. Nothing is created or deleted, so it costs nothing unless you give the provider a price.

How: scaling_ssh.py uses Paramiko (an opt-in ssh extra, fetched the first time an SSH provider is saved). Host keys are trusted on first use when the provider is saved or checked, recorded on the provider (check.host_keys, kept across a failed check via the new Driver.kept), and every other call refuses a host whose key changed. The first-boot script is written with umask 077 to ~/.lens-scaling/<name>.sh and deleted by the unit as it starts, since it holds the join token. Like the clouds and Proxmox, the group waits for the mesh (needs_join, can_join from #595).

Motivation and Context

Part of the auto scaling ask (remote Linux instances as worker nodes); follows #556, #573, #579 and #559.

Types of changes

  • Bug fix
  • New feature (non-breaking change which adds functionality)
  • Breaking change

Checklist:

  • The title is a Conventional Commit.
  • My code follows the code style of this project.
  • I added or updated tests (test_scaling_ssh.py: host specs, trust on first use and refusal of a changed key, start/count/stop against a stand-in that runs the real shell commands).
  • docs/scaling.md updated (driver table, "Machines elsewhere", status).
  • n/a changelog: the title says it.

Done questions (AGENTS.md)

  • Goal: another worker-node provider from the ask; merged on green.
  • Agents: same scaling API and assistant tools as the other drivers.
  • Zero setup: Paramiko is fetched on first use; nothing to install up front.
  • Fewest clicks: hosts and a key; per_host defaults to 1.
  • Configurable: fields come from the driver spec, shown in Settings › Auto scaling.
  • Light: no new default dependency; the extra loads only when an SSH provider is saved.
  • Private and secure: key and passphrase sealed like other provider secrets; TOFU host keys held to; script readable only by the user and deleted at start; threat model P-SCALING updated.
  • Cost: no paid resource; launches still wait for approval and budget caps.
  • User decides: off by default; nothing launches without an approved group.
  • Upgrades: n/a, no stored-data shape change (new keys inside the provider's check result only).
  • Honest: docs label SSH in progress, not yet tried on real machines.
  • Tests, docs and generated files (uv.lock via uv) updated.
  • Open: Paramiko is LGPL-2.1, used unmodified as an opt-in extra (AGENTS.md principle 13).
  • Reported in the thread; refine later in docs/scaling.md.
  • Reuse: Paramiko, existing driver base, user_data and sealed secrets.

Local CI (ci/local-ci.sh) on 00c753d: every job passed except FastAPI (SurrealDB server), where 1 of 1730 tests failed with a websocket HTTP 502 in test_mesh.py (this container's server). After merging main: scaling tests pass locally (44), ruff clean, requirements and threat model checks pass.

🤖 Generated with Claude Code

https://claude.ai/code/session_01ALjNbLobvPe7VwvDL8j7V3

Each unit is the first-boot script started in its own process group on
one of the provider's hosts, up to per_host each; letting it go stops the
group. Host keys are trusted on first use when an admin saves or checks
the provider, kept when a later check fails, and held to on every call.
Paramiko is the ssh extra, fetched when the first SSH provider is saved.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ALjNbLobvPe7VwvDL8j7V3
…-azm2mb

# Conflicts:
#	README.md
#	docs/requirements.md
#	docs/scaling.md
#	fastapi_backend/uv.lock
The mesh's install line landed (#591), so a group on a cloud, Proxmox or
SSH provider could launch, but a node runs no `lens worker` yet: its
units would never count as joined and only cost until given up on.
can_join now also needs the mesh to say its nodes run workers
(WORKER_NODES), and waiting_for says which of the three it waits for.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ALjNbLobvPe7VwvDL8j7V3
…-azm2mb

# Conflicts:
#	README.md
#	docs/scaling.md
#	fastapi_backend/app/domain/scaling.py
#	fastapi_backend/tests/api/test_scaling_clouds.py
@adeelahmad
adeelahmad merged commit 723c39a into main Oct 18, 2026
21 of 22 checks passed
@adeelahmad
adeelahmad deleted the claude/project-thread-azm2mb branch October 18, 2026 13:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

0