Early · macOS and Linux
Let coding agents run free.
Keep your machine out of reach.
Solitary runs coding agents in disposable virtual machines: no host mounts, secrets whitelisted per cell, a default-deny network, and a deliberate way to move work in and out.
curl -fsSL https://solitary.balakin.io/install.sh | shOr brew install balakin/solitary/solitary. Needs Lima 2.0 — other ways to install.
The problem
A mounted project is not a boundary. It is a way in.
Most tools that sandbox a coding agent hand it a container and mount your project directory into it. An agent that can write to that directory can write to .git/hooks/pre-commit, a package.json script, a Makefile, an .envrc, a .vscode/tasks.json, or a lint plugin resolved from the local tree.
Then you run npm test or git commit on your machine, and the payload runs as you. No escape was necessary. You executed it yourself.
The model
A cell is a VM with a container inside it.
The boundary and the toolset are different things, so you can replace one without disturbing the other.
Your host
Starts machines, holds the secrets, displays results. Never mounted into a cell.
The VM
The boundary
The boundary. A hypervisor, not a shared kernel — built so that root in the container is not root on your machine.
The container
The toolset, and replaceable. Change the image, run up again: same disk, same secrets, different tools.
Nothing is mounted from the host, ever. Clone, build, lint, test and review inside the cell. Core concepts →
See it running
Every cell, and what its network is doing.
solitary dashboard╭─────────────────────╮╭──────────────────────────────────╮
│ cells ││ claude │
│ › ● claude running ││ image build:./Containerfile │
│ ○ demo stopped ││ machine 4 cpus · 4GiB · 40GiB │
╰─────────────────────╯│ ports all reach host localhost │
│ network 2 allowed │
│ vpn up · handshake 12s ago │
│ secrets 2 of 2 set │
╰──────────────────────────────────╯
↑↓ move · ⏎ shell · u up · s stop · e secrets · d rm · q quitsolitary dashboard · t ╭─────────────────────╮╭─────────────────────────────────────────────────╮
│ cells ││ traffic · claude │
│ › ● claude running ││ 12:07:53 query api.github.com ×2 │
╰─────────────────────╯│ 12:07:53 resolved api.github.com → 140.82.121.6 │
│ 12:07:53 refused example.com │
│ 12:07:53 denied 1.1.1.1:443 │
╰─────────────────────────────────────────────────╯
↑↓ scroll · G live · / filter · b refused only · c clear · esc backThe traffic view reads the machine's own log, so a cell cannot see — let alone edit — what is recorded about it. It is also the fastest way to find what an allow list is missing: a refused line names it.
What you control
Authority arrives in named pieces.
Default-deny egress
Limit outgoing connections to addresses resolved for allowed domains. Host and local addresses are refused unless allowed; names sharing an IP share access.
A VPN with a kill switch
Route application traffic through WireGuard. If the tunnel goes down, application egress stops. The VM resolver can still reach configured DNS upstreams.
Secrets whitelisted per cell
A cell receives only the secrets it declares. The source file stays on the host; selected values enter the container and remain available when you rebuild.
Ports are the way in
Run a dev server in the cell and open it in your own browser. Name the ports you want forwarded, and only those reach the host.
An explicit hand-off
The cell publishes; the host collects. Files come out by name, never executable, and never over something already there.
Cells are disposable
Destroy a cell and build a clean one from the same definition. The disk goes with it; the definition and the host-held secrets stay.
A cell in practice
Define it once. Share it. Rebuild it clean.
The definition says what a cell needs, not the values it receives. Publish it and whoever copies it supplies their own credentials and their own tunnel — so an environment can be reviewed in a pull request before anyone runs it.
Shareable workflows →image: ghcr.io/you/agent:latestsecrets: # only these names are passed in GITHUB_TOKEN:ports: # only these reach the host - 8080network: # and it reaches nothing else allow: - github.com - registry.npmjs.org vpn: ./vpn.conf # optional: all of it, through this tunnelHonest limits
This does not make an agent harmless.
Isolation is built to stop a compromise of your machine, and it is not infallible: a bug in the hypervisor, Lima, the kernel or solitary itself can break it. Every such bug is a vulnerability to fix, but none can be ruled out — keep them all up to date, and give a cell only what you could afford to lose.
Even where it holds, isolation does not stop an agent misusing the authority you granted it: it can still push to any repository the token you whitelisted can reach, and reach anything you allowed. A cell with no allow list reaches the whole internet. The image you run is trusted code.
A secret passed to a cell lives inside that cell. Give one only the credentials it needs, and treat a cell's output as data rather than as something to run.
Limitations and trade-offs →