Skip to content

feat(ssh): persistent SSH workspaces — one connection, many clean commands (PRD 0013) - #485

Merged
ralyodio merged 1 commit into
mainfrom
worktree-ssh-workspaces
Sep 3, 2026
Merged

feat(ssh): persistent SSH workspaces — one connection, many clean commands (PRD 0013)#485
ralyodio merged 1 commit into
mainfrom
worktree-ssh-workspaces

Conversation

@ralyodio

@ralyodio ralyodio commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Implements PRD 0013 — persistent SSH workspaces (moshcode ssh / /ssh): one authenticated OpenSSH connection per named target, and a clean command channel for every call.

What

  • src/ssh.mjs — the whole feature, zero dependencies, OpenSSH on PATH is the implementation:
    • named targets in ~/.moshcode/ssh/targets.json (host or ~/.ssh/config alias, port, cwd, persist) — written atomically at 0600, secret-shaped fields dropped on read
    • one ControlMaster per target behind a hashed socket in a 0700 dir (~/.moshcode/ssh/control, or $TMPDIR/moshcode-ssh-<uid> when the home path would overflow sun_path)
    • open / check / close via ssh -O check / -O exit — no PID tracking; stale sockets are detected, unlinked, reopened, and a mid-command transport failure is retried once
    • exec: argv → cd -- '<cwd>' && K='v' exec 'cmd' 'args' with POSIX quoting, -T (no PTY) by default, --tty, raw --stdin, --cwd, --env, --timeout, --sh for a deliberate pipeline; --json returns { ok, transportOk, code, signal, stdout, stderr, durationMs } where ok is the command's verdict and transportOk is ssh's (exit 255)
    • moshcode ssh <name> hands the terminal to ssh over the same master, landing in the target's cwd
    • put / get over scp on the same socket; put lands as a temp file and is renamed
    • shell — a namespaced remote tmux session with send / read / kill / list; says so when tmux is missing and exec keeps working
    • bench <name> measures fresh vs multiplexed on the user's own host (loopback here: ~96ms → ~12ms median)
  • CLI, pit, help, completion through the canonical schema (SSH_VERBS, PIT_COMMANDS); README command table regenerated; new ## SSH workspaces README section
  • moshscript: sshOpen, sshCheck, sshExec (returns the same object as --json), sshClose, sshPut, sshGet, sshShellSend/Read/Kill, plus ssh(...) as a cliVerb
  • PRD filed as prd/0013-persistent-ssh-workspaces.md (0012 was already billing)

Two OpenSSH facts that shaped it (both found by running it)

  1. -M together with -o ControlMaster=yes is read as a second request for master mode, i.e. ask mode — every mux client then gets "Master refused session request: Permission denied". The PRD's conceptual -MNf invocation has this flaw; the implementation uses -o ControlMaster=yes -N -f and no -M.
  2. ControlMaster=auto on the client is the native stale-socket recovery: a dead socket is unlinked and the client becomes the new master. exec runs with auto.

Tests

  • test/ssh.test.mjs (35 tests, no network): names, registry + permissions, socket hashing + long-home fallback, durations, quoting, argv for master/exec/attach/scp, keepalive probing, transport-vs-command classification, debug redaction, check/open/close lifecycle, exec cwd/env/stdin/timeout/retry/auth-failure/no-ssh, CLI JSON shapes and exit codes, shell verbs
  • test/ssh-sshd.test.mjs (13 tests): an ephemeral non-root sshd on loopback with generated keys and a private ssh_config (skips cleanly where sshd is absent) — open/check/close, printf/failing command/stdin bytes/cwd/env/--sh, timeout, 6 parallel CLI execs on one master, killed-master recovery, auth failure, nothing-listening, changed host key refused, put/get, CLI end to end, bench, remote tmux state persistence, tmux-missing
  • Full suite: 2860 tests, 0 failures locally.

Security posture

Never passes StrictHostKeyChecking=no, never stores a password or key, never auto-accepts a host key, BatchMode=yes only when stdin is not a terminal (or --batch), debug output summarises the remote command by byte count so --env values are never logged, stdin is never logged.

🤖 Generated with Claude Code

https://claude.ai/code/session_01RcmWsBrbzQpfTTz71wfbkw

…mands (PRD 0013)

`moshcode ssh` / `/ssh`: named targets, one OpenSSH ControlMaster per target
behind a hashed 0700 socket, open/check/close via `ssh -O`, and `exec` as a
no-PTY command channel with real argv quoting, raw stdin, per-call cwd/env,
timeout, and a --json object that separates the command's verdict (`ok`,
`code`) from the transport's (`transportOk`). Interactive attach, scp put/get
on the same socket, a remote tmux `shell` with send/read/kill, and `bench`.
moshscript gets sshOpen/sshExec/sshClose and friends returning the same shape.

Two OpenSSH facts found by running it: `-M` with `-o ControlMaster=yes` is
read as ask mode (every mux request "Permission denied"), so the master is
`-o ControlMaster=yes -N -f`; and `ControlMaster=auto` on a client is the
native stale-socket recovery, so exec runs with it.

Tests: 35 unit tests with a fake runner, 13 against an ephemeral non-root
sshd on loopback (skipped where sshd is absent).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RcmWsBrbzQpfTTz71wfbkw
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

ThreatCrush Security Scan

3 finding(s) in the 10 file(s) this pull request changes.

MEDIUM: 3

Severity Rule Location
MEDIUM sql-string-concatenation src/cli-schema.mjs:162
MEDIUM sql-string-concatenation src/cli-schema.mjs:534
MEDIUM sql-string-concatenation src/cli-schema.mjs:660
88 pre-existing finding(s) elsewhere in the repository — **HIGH/CRITICAL**: 8 | **MEDIUM**: 70 | **LOW**: 10

Not introduced by this pull request. The full set is in the Security tab.

Severity Rule Location
HIGH js-ssrf-outbound-request apps/pwa/public/sw.js:45
HIGH tls-verification-disabled apps/pwa/src/lib/moshpit-gateway.mjs:299
HIGH sh-remote-script-execution install.sh:86
HIGH sh-remote-script-execution install.sh:90
HIGH sh-remote-script-execution install.sh:202
HIGH sh-remote-script-execution install.sh:213
HIGH sh-remote-script-execution install.sh:219
HIGH tls-verification-disabled src/dns.mjs:766
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:139
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:153
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:179
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:373
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:377
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:422
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:671
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:867
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:869
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:928
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:974
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:1044

…and 68 more. Full results in the Security tab.

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio marked this pull request as ready for review September 3, 2026 16:37
@ralyodio
ralyodio merged commit ac221f7 into main Sep 3, 2026
6 checks passed
@ralyodio ralyodio mentioned this pull request Sep 3, 2026
ralyodio added a commit that referenced this pull request Sep 3, 2026
`moshcode ssh` — persistent SSH workspaces (#485, PRD 0013): named targets,
one OpenSSH ControlMaster per target, `exec` as a no-PTY command channel with
a --json verdict that separates the command's exit from the transport's,
interactive attach, scp put/get, a remote tmux shell, and moshscript helpers.


Claude-Session: https://claude.ai/code/session_01RcmWsBrbzQpfTTz71wfbkw

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
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.

1 participant