Skip to main content
SSHRuntime connects to a machine running sshd and executes every captured line on it. Mirage does not start, size, or stop the machine; anything you can already ssh into works, with nothing to install on it. See the Sandbox overview first for routing and captures.

Skip the FUSE

The other providers need Mirage and FUSE inside the sandbox to serve the workspace’s mounts. SSH is the one provider whose machine usually is the fileserver: the ssh resource already mounts that machine’s directory over SFTP. Mount it at a prefix equal to its remote absolute path, and the two sides agree about every path with no FUSE, no remote Mirage install, and no path rewriting anywhere:
The agent lists and greps /home/user/proj through the workspace (SFTP), and python3 /home/user/proj/train.py runs on the box, where that path is a real file the interpreter opens natively. One spelling of the path works on both sides because it names the same file on the same machine. Whether the remote directory is a plain disk, an NFS export, or itself a FUSE mount does not matter to either side. Nothing checks the alignment for you: a mount at a prefix that is not the remote path (or on a different host) fails only when a captured line misses a file. To expose a friendlier name, align from the remote side (place or symlink the directory at that path on the box) rather than renaming the mount: a captured line ships verbatim, and no rewrite could reach the paths a program reads from its own code or config. For mounts the machine cannot see natively (S3, Slack, …) the general recipe still applies: serve them on the machine with Mirage + FUSE at the same prefixes.

Connect from TypeScript

The config carries the ssh resource’s fields minus root: host (required, also the address unless hostname overrides it), port, username, identityFile, timeout, and the shared env. Auth is keys or an agent, never a password: with no identityFile, SSH_AUTH_SOCK authenticates, and username defaults to the local user. ssh2 is the transport and loads on first use (pnpm add ssh2). Unknown config fields fail at construction.

How lines run

The first captured line opens one SSH connection, and every line after it is one exec channel on that connection: real byte stdin, separated stderr, the remote command’s own exit code. SSH exec has no docker-style -w/-e, so the session cwd and the merged environment ride the command itself as cd 'cwd' && env 'K=V' ... sh -c 'line', every piece single-quoted. The remote login shell only needs to be POSIX-compatible.
Host keys are not verified (ssh2 never checks them), so treat the network path to the machine as trusted. And as with every provider, a Mirage timeout stops waiting without killing the remote process; see Resource limits.