feat(docker): localhost sshd → agentbox open sshfs + Finder/Codex launchers#175
Conversation
…aunchers
Every docker box now runs an always-on OpenSSH daemon bound to container :22,
published only on 127.0.0.1:<ephemeral> and key-authed by a per-box ed25519 key
(minted under ~/.agentbox/boxes/<id>/ssh/, installed into the box's
authorized_keys). This powers a live-mount `agentbox open` and lets the Codex app
add a box as an SSH connection.
- Image: install openssh-server + agentbox-sshd-start (root, exec -d, idempotent),
staged via stage-runtime.mjs. No EXPOSE 22 (publish via -p; OrbStack keeps 6080).
- Provider: publish :22, mint key, install authorized_keys, launch sshd on
create + re-resolve the ephemeral host port on start (mirrors the VNC/web ports).
New packages/sandbox-docker/src/ssh.ts (setUpBoxSshd/launchSshdDaemon/
installAuthorizedKey). mintSshKey lifted to @agentbox/sandbox-core (shared with
Hetzner). Record lands on top-level BoxRecord.ssh {host,user,identityFile,port};
syncAgentboxSshConfig emits a Port line.
- open: `agentbox open` sshfs-mounts /workspace for SSH-capable providers
(SSH_MOUNT_PROVIDERS = docker/hetzner/daytona); vercel/e2b fail fast with a
readable pointer to `agentbox download`; legacy docker (no sshd) keeps the rsync
fallback. `--in codex` now works for docker.
- Finder launcher: `finder` is a first-class open target (open --targets --json),
gated to SSH_MOUNT_PROVIDERS, surfaced in the Hub "Apps" card and the Tray
"Open In…" submenu.
- doctor: optional (warn-not-fail) sshfs + macFUSE checks.
- Docs updated (features.md, access-your-box/cli/local-docker mdx).
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
… box Address two Cursor Bugbot findings on the SSH move: - readState now backfills top-level `box.ssh` from a legacy `box.cloud.ssh` on read, so an already-created box (e.g. Hetzner) keeps its managed ~/.agentbox/ssh/config alias — syncAgentboxSshConfig only reads `box.ssh` and would otherwise drop it until the box's next start. - `agentbox open` / `--in codex` on an already-running docker box now refreshBoxSshd() before mounting: a `docker restart` (or docker daemon restart) outside `agentbox start` reallocates the ephemeral -p 0:22 host port, leaving the recorded Port stale. Extracted the start-path sshd re-resolve into a shared, idempotent refreshBoxSshd() in lifecycle.ts. Tests: readState backfill (+ no-clobber) in state.test.ts.
|
bugbot run |
| /* best-effort */ | ||
| } | ||
| } | ||
| return box; |
There was a problem hiding this comment.
Stale SSH after port loss
Low Severity
refreshBoxSshd returns unchanged when publishedHostPort is null, without clearing a persisted box.ssh or re-syncing config. If the container no longer has an SSH publish (or resolution fails) while state still holds an old loopback port, ~/.ssh/config keeps a dead Port until something else rewrites state.
Reviewed by Cursor Bugbot for commit 112d28c. Configure here.
…odex legacy msg Second Cursor Bugbot pass: - create + refreshBoxSshd only record `box.ssh` / re-sync ~/.ssh/config when sshd actually bound (`up`), not merely when Docker published the -p 0:22 host port — avoids advertising an alias that points at a dead loopback port. - `agentbox open --unmount` is handled up front (provider-independent), so a legacy docker box no longer falls through to the rsync path instead of tearing down its sshfs mount. - `--in codex` on a docker box that predates the in-box sshd now fails with a clear "recreate the box" message instead of a confusing "sshd may have failed". (The re-flagged "missing cloud.ssh migration" is already handled by the readState backfill added in the previous commit + its tests.)
|
bugbot run |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 6ac4225. Configure here.
| * changes across restart, and `agentbox open`/start re-syncs `~/.ssh/config`). | ||
| */ | ||
| export const PERSISTENT_SSH_PROVIDERS: readonly string[] = ['hetzner']; | ||
| export const PERSISTENT_SSH_PROVIDERS: readonly string[] = ['docker', 'hetzner']; |
There was a problem hiding this comment.
Codex offered for legacy docker
Medium Severity
Adding docker to PERSISTENT_SSH_PROVIDERS makes open --targets and the Hub treat every docker box as Codex-eligible, but openInCodex rejects pre-sshd docker boxes via sshEnabled. Legacy boxes still show an Open in Codex control that fails at click time.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 6ac4225. Configure here.


What
Every docker box now runs an always-on OpenSSH daemon (container
:22, published only on127.0.0.1:<ephemeral>, key-authed by a per-box ed25519 key). This gives docker boxes a real SSH surface, which powers:agentbox open <box>— live sshfs-mount of/workspaceat~/.agentbox/mounts/<box>/+ Finder reveal (was an rsync copy).agentbox open <box> --in codex— add the box to the Codex app as an SSH connection.How
openssh-server+agentbox-sshd-start(root,exec -d, idempotent; writes a hardenedsshd_config.ddrop-in,ssh-keygen -A). Staged viastage-runtime.mjs. NoEXPOSE 22(publish via-p; OrbStack keeps its single EXPOSE on 6080).packages/sandbox-docker/src/ssh.ts): mint per-box key → installauthorized_keys→ launch sshd on create; re-resolve the ephemeral host port on start (mirrors the VNC/web port handling).mintSshKeylifted to@agentbox/sandbox-core(shared with Hetzner). Target recorded on top-levelBoxRecord.ssh {host,user,identityFile,port};syncAgentboxSshConfigemits aPortline.SSH_MOUNT_PROVIDERS(docker/hetzner/daytona). Vercel/E2B (no SSH) fail fast with a readable pointer toagentbox download; legacy docker (no sshd) keeps the rsync fallback.finderis now a first-classopen --targets --jsonentry (providers: SSH_MOUNT_PROVIDERS), consumed by the Hub card + Tray menu (Tray change shipped separately tomadarco/agentbox-tray).sshfs+macFUSEchecks.Security: sshd is loopback-only (verified
127.0.0.1:<port>, not0.0.0.0), key-only auth, no root login,AllowUsers vscode.Verified
Live docker box: sshd up, port on
127.0.0.1only,ssh <box>logs in + reads/workspace, SFTP transport works, restart re-resolves the port + re-syncs~/.ssh/config,--in codexopens the deep link, destroy cleans up the alias + key. Hub API round-tripsfinder; Tray relaunched with the item. Full monorepo build green;pnpm lint+ affected vitest pass.Note
Medium Risk
Changes the default
openpath and adds container sshd plus SSH config/state migration; exposure is limited to loopback key-only auth, with legacy docker and explicit failures for providers without SSH.Overview
Docker boxes now ship an in-box OpenSSH daemon (loopback-published, per-box ed25519 key) so
agentbox openlive-mounts/workspacevia sshfs at~/.agentbox/mounts/<box>/instead of rsyncing a snapshot. Legacy docker boxes withoutsshEnabledstill use the old export path; Vercel/E2B fail fast with a pointer toagentbox download.SSH state moves to top-level
BoxRecord.ssh(with optionalport),syncAgentboxSshConfigwritesPortfor docker, and reads backfillcloud.ssh.--in codexand persistent-SSH gating now include docker;finderis a first-class Open In target (SSH_MOUNT_PROVIDERS) in the CLI, Hub, and tray surfaces.Supporting changes: shared
mintSshKeyin@agentbox/sandbox-core,refreshBoxSshdon start/open, optional doctor checks for sshfs / macFUSE, image staging foragentbox-sshd-start, and doc updates for the new default open behavior.Reviewed by Cursor Bugbot for commit 6ac4225. Configure here.