← Field notes

How the sandbox is held in

A sandbox task runs your agent's commands on Cloudflare while the model, its keys and every permission stay on your Mac. These are the walls around it, and what each one stops.

A coding agent that can run commands can also run the wrong ones. It can install a package with a hostile install script, follow a prompt injected into a README, or send a file somewhere it should not go. On your own laptop, every one of those runs with your keys, your SSH agent and your network.

A muxcode sandbox task moves the commands off the laptop and keeps everything else on it. This post walks through where the line sits, and the walls that hold it.

Two halves, one task

The agent loop stays on your Mac. The model, its keys, the conversation and the permission gate never leave it. The sandbox receives only what the loop has already decided to run: a read, an edit or a command, over HTTPS. It never sees a model, and nothing of muxcode is installed in it.

Your Mac
Agent loopThe model and its keys
Permission gateDecides what may run
mux sandbox
Task filesKept while the task sleeps
ShellEveryday commands, no network
ContainerToolchains, started when needed
The internet
registry.npmjs.org✓
crates.io✓
github.com fetch✓
github.com push✕
any other host✕
No model traffic goes to the sandbox, and nothing of muxcode's is installed there.
  1. MethodRequestAnswerRule
  2. GET registry.npmjs.org/zod forwarded download-read
  3. GET static.crates.io/crates/serde forwarded download-read
  4. POST github.com/acme/ledger.git forwarded git-fetch
  5. POST github.com/acme/ledger.git refused read-only-methods
  6. PUT registry.npmjs.org/zod refused read-only-methods
  7. GET paste.example.com/raw refused host-allowlist
The gateway's log: a fetch and a download go through; a push, a publish and an unknown host do not.

So the question “what can the sandbox reach?” has a short answer. It can reach its own files, and the package hosts on one list, for reading only.

The files

Each task gets a workspace of its own, cloned from your branch onto a fork that belongs to that task alone. Every path a request names must sit under /workspace or the agent’s home directory; anything else is refused, whatever the command asks for.

The repository names are never taken from a request. The sandbox builds each one from the workspace your key belongs to, so a request cannot name another workspace’s fork, even by guessing it.

Two backends

A command runs first in a light shell that starts at once and has no network at all. When a program the command names is not in that shell, the whole command moves to a Linux container with real toolchains: Rust, Node, Go, Python, the JDK and PHP. Both see the same files.

The container starts only when a command needs it, and every command is stopped after 30 minutes.

The network

The container reaches the internet through one gateway, and the gateway answers four ways:

  • A GET or HEAD to a host on the list is forwarded.
  • A git fetch from GitHub is forwarded.
  • A git push, an npm publish, or any other write is refused.
  • A host that is not on the list is refused.

The list is the package registries a project’s checks need: npm, crates, Go, Maven and Gradle, Packagist, PyPI, GitHub and the Debian mirrors. Authorization and cookie headers are removed from every request, and none is added. A redirect comes back to the container and goes through the same check, so an allowed host cannot bounce a request to one that is not. Every decision is logged with the host, the method and the rule that answered it.

That is the point of reads only: a hostile install script can download, but it has nowhere to send what it finds.

The environment

Only the variables the app sends for the task reach a command. HOME, PATH and the temporary directory always come from the sandbox, so a request cannot point a command at a different toolchain. Git tokens are scoped to one repository, last 15 minutes, and never travel in a URL.

Viewers in a workspace cannot start a sandbox. Running commands in a repository takes a member who can change it.

A task’s life

A task starts from your branch and works on its fork. After 20 idle minutes its compute stops; the files stay, and the next command wakes it. When the work is done, the change is committed to a branch on the fork for you to fetch and land. Nothing reaches your repository until you choose to land it. A task stopped for 14 days deletes its files and its fork.

What this adds up to

The worst a sandbox task can do is make a mess of its own fork. It cannot reach your keys, because they are not there. It cannot push, publish or send your code out, because the gateway reads only. And it cannot touch another workspace, because every name it uses is built from yours.

The sandbox is included with Pro and Team. See the plans, or read how muxcode works for the side that stays on your Mac.