Run Claude Code and Codex on a remote server
The repository lives on a build box. The interface lives on your laptop. Remote Workspaces joins the two over one hardened SSH stream—and there is nothing to install on the server.
Remote Workspaces lets a Pro workspace browse, read, edit and run Claude Code and Codex on a machine you reach over SSH. Crowsnest still binds to 127.0.0.1 and still uploads nothing. It sends a version-matched, SHA-256-verified helper down the connection you opened, so the server needs no Crowsnest install, no package manager, and no daemon.
The agent belongs where the code is.
Plenty of real work does not happen on the laptop. The repository is on a Linux build box with the right toolchain, the dataset is on a machine with the GPU, or the service only compiles inside a network you reach by jump host. The agent has to run there, because that is where its edits, its tests, and its failures mean something.
The usual answer is a terminal multiplexer over SSH. It works, and it costs you the entire reason to use a workspace interface: scrollback is not a browsable gallery, a stopped session is not resumable history, and a mistake three turns back is not editable. You end up with the agent in the right place and the tooling in the wrong one.
Remote Workspaces splits those two decisions apart. The control plane stays local—the gallery, the reader, byte-preserving repair, the account picker—while execution and history live on the machine you chose.
The helper travels with the connection.
The first design question was what the server has to have. Any answer involving pip, uv, or a system package makes the feature a deployment project, and it puts an installer inside a security boundary that exists precisely to avoid running arbitrary things on someone else's machine.
So the helper is not installed. It is delivered:
Crowsnest builds a helper from its own installation
The local app packages the matching version of its dependency-free remote helper as a zipapp. It is always the version that shipped with the app you are running, so the two ends cannot drift apart.
It opens one non-interactive SSH process
A fixed argument list, no shell, and your existing OpenSSH configuration, keys and agent. Crowsnest stores no password and no private key.
The bootstrap verifies before it executes
A short Python bootstrap reads a length-and-digest header, checks the SHA-256 of the bytes it received, and refuses a cache directory that is a symlink. Only then does it execute.
The helper caches itself by content
It is stored under ~/.cache/crowsnest/helpers on the server, named by digest, at mode 0600. A reused copy is re-verified and replaced if its bytes are wrong.
Both ends speak bounded JSONL over stdio
The helper never opens a socket and never accepts a shell command. It answers a fixed protocol: enumerate sessions, read, search, mutate, and run the provider's own agent process.
The server's requirements are therefore short: SSH access you already have, Python 3.9 or newer, and whichever agent runtimes you want to use there. The connection reports which of those it actually found rather than assuming.
What the connection refuses.
This is the eighth—and narrowest—exception to a rule Crowsnest otherwise keeps absolutely: it makes no network call. It is worth being precise about how little it buys.
| Concern | What is allowed | What is refused |
|---|---|---|
| Destination | An OpenSSH alias, hostname, or user@host you type, plus an optional port. | Arbitrary SSH options, key paths, commands, and any form of host discovery or scanning. |
| Trust | Normal host-key verification, in BatchMode, using your agent. | Silent first-use key acceptance. Establishing trust stays your explicit terminal action. |
| Transport | One stdio stream to one helper process. | Port forwarding, agent forwarding, X11, TTY allocation, local commands, and multiplexing. |
| Remote paths | Opaque, connection-scoped references issued by the helper's own enumeration. | A path from the browser being treated as authority. A working directory is accepted only if that exact value came from that connection. |
| Stored data | A label, a destination, and an optional port. | Passwords, private keys, remote transcripts, prompts, or remote paths. Disconnecting destroys every reference. |
Nothing here changes where conversations go. Remote transcripts stay on the remote machine, local ones stay local, and neither reaches Crowsnest or any third party. The SSH destination is a machine you already had, reached with credentials Crowsnest never sees.
Connecting takes one field.
- Open Settings → Remote WorkspacesThe pane is part of Pro. It states what it will do before it asks for anything.
- Enter a destinationAn alias from your ~/.ssh/config, a hostname, or user@host—with a port if it is not 22. Save and connect are one action.
- Read what it foundThe row reports which agent runtimes are ready on that machine. A failed connection shows what ssh itself said, so an unresolvable host looks different from a refused key.
- Pick the machine like any otherRemote projects join the gallery and search. Remote accounts appear in the ordinary launcher, tagged with their location, so account identity stays machine-scoped.
- Approve a fresh project when you need oneA connected server can adopt an existing directory below its home before either agent has made history there.
A disconnected machine never silently falls back to a local account. If the target is gone, the work does not quietly run somewhere else.
What is not remote yet.
A capability list is only useful if it is also a list of what is missing. As of 0.1.6:
- Live chat depends on the remote runtime. Codex live chat runs through the Codex CLI installed there. Claude live chat is offered only when the optional Claude Agent SDK is importable by the remote Python; without it, that provider is browsing and history only. The connection row tells you which case you are in.
- Crew, UI Studio, preview servers and the browser tools stay local. Each one provisions worktrees, binds ports, or drives a browser—every one of those widens execution and needs its own boundary decision before it crosses a machine.
- There is no offline remote history. Remote transcripts are not cached locally, deliberately: caching them would move conversations onto a second machine, which is exactly the thing the product promises not to do.
- Real-host interoperability is the open follow-up. The protocol, the bootstrap, the refusals and the failure paths are covered by tests at both ends, including a bogus host and a hostile alias. Broad validation across real server configurations is ongoing; if your setup does not work, that is worth an email rather than a shrug.
Sources and related field notes
Behavior described here was checked against the released 0.1.6 source on August 30, 2026—the SSH invocation and bootstrap in remote.py, capability discovery in remote_helper.py, and the Remote Workspaces pane itself. The hardening options are OpenSSH's own; see the ssh_config manual for what each one does.
Published and source-reviewed August 30, 2026 against Crowsnest 0.1.6. Provider runtimes evolve independently; linked first-party documentation is the current source for their native capabilities.