A remote host whose login shell is tcsh came up on 1.48.0, when the local
sh rewrote ${SHELL:-/bin/sh} to the Codeman host's shell. Since #770 the
launch reaches the far end verbatim and runs tcsh itself, and tcsh refuses
-i -l -c, -l -c and -i -l, so every stock launch left a dead pane.
The login flags are now picked on the far end, in the shape of
loginShellArgs(): set -- -i -l, cleared for csh and tcsh, then
exec "${SHELL:-/bin/sh}" "$@". The text is one brace group, so
cd <path> && { ...; } still starts nothing when the cd fails. bsd-csh takes
every flag combination, csh is listed because it is tcsh on macOS and the BSDs.
The version probe keeps its one statement: the login shell parses it, not
/bin/sh, so fish and tcsh refuse it either way, as they did on 1.48.0.
Measured with the real builder on a real sshd in a throwaway Debian container
(tcsh 6.24.07, bsd-csh, fish 3.6, bash) and through a fake sshd with
tcsh 6.24.10, fish, zsh, dash and bash, a real Codeman server on tcsh included.
The tcsh disclosure comes out of the docs and the changeset. They keep the two
limits 1.48.0 had too: tcsh has no export, so the PATH bootstrap only prints an
error, and the tmux probe needs a command executable, which Debian lacks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6.4 KiB
Remote SSH Sessions
Point a case at another machine and the agent runs there, with the same dashboard, mobile UI, and autonomy features. Your laptop becomes a window onto a session living on the remote host.
Like Docker, this is a location overlay on a case, not a run mode. All eleven run modes work remotely. See Core Concepts.
Why bother
The agent runs where the work is: a build server, a NAS, a GPU box, a machine reachable only through a jump host. Your laptop can sleep, change networks, or close, and the run continues.
Setting it up
Add Case → Remote:
| Field | Notes |
|---|---|
| Host | Hostname or IP. |
| Username | The SSH user. |
| Port | Defaults to 22. |
| Identity file | ~ and $HOME are expanded for you. |
| Jump host | The -J equivalent, [user@]host[:port]. |
| SOCKS proxy | For hosts reachable only through a proxy. |
| Extra SSH options | Any KEY=VALUE options your normal connection needs. |
| Remote path | The working directory on that machine. |
Hosts are saved and reusable, so a second case on the same machine is just a path. Host
profiles can also carry per-run-mode launch command overrides, for when the binary lives
somewhere unusual on that host. An override runs on the remote in your login shell, so a $
in it expands there, not on the Codeman machine.
The remote host needs tmux. Codeman probes for it when you link the host rather than failing later at launch.
The remote login shell can be bash, zsh, dash, fish, csh or tcsh. A tcsh session reads .tcshrc
and not .login, so set the PATH that finds tmux and your agent CLI in .tcshrc. The tmux check
also needs a command executable under tcsh: macOS and the BSDs ship one, Debian and Ubuntu do
not, so linking a tcsh host there stops at could not verify tmux. nushell and elvish cannot run a
remote session at all.
What actually runs
The agent lives inside a dedicated tmux server on the remote host, and Codeman fronts it
with a local tmux pane running ssh.
That two-layer arrangement is what makes it durable: a dropped SSH connection, a network change, or a closed laptop kills the local pane, not the remote session. Reconnecting lands back in the same live conversation.
The remote session name is deliberately chosen so that a Codeman running on the target host will not adopt it as one of its own. Two Codemans, one host, no interference.
Auto-reconnect
A watcher with bounded backoff notices a dead SSH pane and quietly reattaches to the still running remote session. On by default; the kill switch is in App Settings → Agents & CLIs → Remote auto-reconnect.
Intentional kills are never revived. Closing a session means closing it. Neither is a clean
exit inside the pane (Ctrl-D, exit, Ctrl-C at the CLI's prompt): that tears the remote
tmux session down, and the watcher revives a session only when that durable session is
verifiably still alive. Only a transport drop is reconnected.
Discover and attach
Codeman can list the codeman-* sessions already running on a host, whether that machine's
own Codeman started them or another operator did, and attach to one.
The distinction that matters:
| Session | On tab close |
|---|---|
| Launched | Killed, like any local session. |
| Attached | Detached, never killed. |
Attaching to someone else's session and closing your tab must not end their run, so it does not. Several clients can attach the same remote session at different window sizes without clamping each other, and discovery shows a shared badge with the client count.
This list holds Codeman's own sessions. A tmux session someone started by hand on the host is found by the home screen instead: see Opened outside Codeman.
Files
Previews, downloads and text reads in a remote case go over the same ssh connection the
session uses, so a clicked path opens the file on the machine the agent is on, Range
seeking included. Nothing is copied to the Codeman host, apart from the diffs the
Edit history panel keeps, which works for remote Claude
sessions too. Editing, Office previews, thumbnails, the file tree and the tail viewer are not
available remotely and answer a clear 400 rather than a misleading 404. Details in
Working With Files.
Security
Every SSH command line in Codeman flows through one builder that shell-escapes every user-supplied field: identity paths, jump hosts, proxy commands, and extra options. That is the entire injection surface, and it is deliberately a single function rather than string concatenation spread across the codebase.
Host, path, and identity fields are schema-validated on top of that.
Codeman does not store SSH passwords. Use keys, as you would for any other automation.
Gotchas
- The remote host needs tmux. Probed at link time, so you find out immediately.
- The local working directory is meaningless for a remote session, and is not used.
- Run flows must go through the quick-start path for remote cases. This matters if you are driving Codeman over the API: the plain session-create endpoint validates the working directory locally and has no case concept, so it will reject or misroute a remote case.
- Latency is SSH latency. Local echo helps the typing feel, but a slow link is a slow link.
- Transcript-backed features follow the transcript. Subagent windows and similar surfaces read files on the machine where the agent runs.
Read next
- Core Concepts - overlays versus run modes.
- Docker Cases - the other overlay.
- Security - the wider model.
docs/remote-sessions.md- the full design.