Skip to content
Chock

Comparison

How other harnesses sandbox their agents

We compared Chock with five other harnesses by reading their source code, not their documentation, at the commit beside each name and on the date beside that. We did it for a reason: in the one case where we compared the documentation against the code, the documentation was wrong twice and the code corrected it both times.

Every entry says what we read in the source and nothing beyond it, and a row nobody has read says so rather than guessing. Chock's own column is checked against the Chock repository, and it has been wrong and corrected three times.

What is comparedChockthis projectClaude Code2.1.260, sandbox-runtime 0.0.74, read 2026-10-02Codexca46606, read 2026-10-02Zed5a9b955, read 2026-09-05OpenCode012c2f5 (v1.18.11), read 2026-08-21oh-my-pi9350b799, read 2026-08-21
Where a tool call runs
In a microVM guest with a kernel of its own. The sandbox is built inside the guest rather than replaced by it, and the process running the guest is sandboxed in turn.
On your kernel.
On your kernel.
On your kernel.
On your kernel.
On your kernel.
Tool calls inside the operating system's sandbox
Every one of them.
Off by default, and their documentation says so. Switched on it holds shell commands alone: Read, Edit, Write, WebFetch, hooks, MCP servers and language servers run in the harness beside your files.
Shell commands, under bubblewrap on Linux and Seatbelt on macOS.
The terminal and the fetch tool. Reading, writing and deleting a file run in process.
None. Their SECURITY.md calls the permission prompt a user interface feature with no isolation behind it.
None anywhere in the tree.
Language servers, MCP servers and plugins
Sandboxed, the same as every other tool call.
Outside it. Their documentation puts it plainly: a deny read entry does not stop the Read tool, and the domain allowlist does not limit WebFetch.
An MCP server is an ordinary child process, started by a plain command with no sandbox around it. The executor path passes sandbox: None outright.
An MCP server and a language server each get an ordinary child process, outside the sandbox.
Unsandboxed. A plugin is handed a shell and the cleartext token.
An extension runs in process, their words, beside every credential.
Where a tool call writes
A throwaway copy of your project, with .git/objects and .git/hooks read only inside it.
Your real tree. With the sandbox off, which is how it ships, a prompt is the only thing between a command and the disk.
A writable root in your real tree, so a write lands on your own files. Inside it, .git, .agents, .codex and .aws are read only.
Your real working tree.
A git worktree, which is what they mean by their sandbox.
A copy on write layer for subagents. It is built for concurrency, and containment is not its purpose.
What a tool call can read
A minimal mount tree. The credential store is never mounted, and the environment is scrubbed.
The whole disk, read only: a bind of / on Linux and allow file-read* on macOS. Against that, a named credential can be replaced by a sentinel and swapped back at the egress proxy, so the real value never enters the sandbox. Chock has nothing equivalent.
/ is readable in both of the modes not called dangerous, and no read deny list ships, so ~/.ssh, ~/.aws and its own auth.json are readable. Shutting any of that means writing a deny list yourself.
The mount is the whole root, read only. Nothing scrubs the environment, so every token in the parent shell arrives with it.
The whole machine.
The whole machine.
The system call filter, on Linux
A denylist. 31 calls killed, 3 more refused by their arguments, and a test that names every call which must be blocked.
Four rules: a unix socket call and the three io_uring calls. ptrace, unshare, mount, keyctl and bpf are permitted, and a missing filter binary logs a warning and runs the command anyway.
A filter shaped around the network: ptrace, the two process_vm calls, the three io_uring calls and the socket calls, with socket() held to AF_UNIX. It is installed only when the network is off or a managed proxy stands in front of it, so network access with no proxy runs under no filter at all.
A denylist of 6 calls. None on macOS, and none at all on an architecture it does not know.
None.
None.
A kernel module holding the filesystem, on Linux
Landlock, and it is mandatory. On a kernel without it the tool call fails. Below ABI 5 it drops the rights that kernel lacks without saying so, and the mount set is what bounds them.
None.
Not any more. Landlock is legacy there and bubblewrap holds the filesystem instead, failing closed at three points.
None. Landlock enforcement was written twice and deleted both times before it reached their main branch.
None.
None.
Turning the sandbox off
There is no switch. A layer that fails to apply fails the tool call.
It ships off. Switched on, unsandboxed commands are still permitted by default and the system prompt tells the model to retry a refusal with the flag that disables the sandbox.
Every shell tool schema carries an escalation field, and an allow rule in execpolicy runs a command outside the sandbox with no prompt, kept on disk after one yes.
The Linux failure path offers a Run Unsandboxed button, so fail open is a choice a person can make.
There is nothing to turn off.
There is nothing to turn off.
What a rule is matched against
The act a call performs, and the directory the binary came from. A git shim does read the argument vector, and the documentation says the shim stops a mistake and does not stop an attack.
Paths and hosts. In auto mode a classifier model approves, reading the same transcript that carries the tool output it is judging.
Paths, globs included, expanded into mounts before the call runs and failing closed on a glob at the root. An escalation can be reviewed by a guardian model and nobody else.
Regular expressions over the command text, with chained commands parsed across 14 shells. A wrong parse is a way through.
The tool's input: the command text, the path, the URL.
A regular expression denylist of dangerous commands. Writing the argument into a variable first walks past it.
What the policy permits when nothing is configured
Reading, writing and running a command need no prompt, because the sandbox and the throwaway copy are what answer for them. Every act that leaves them is a separate name in the table, and asks.
The whole disk to read and write, and the whole network, with a prompt in front of them.
Reading /, and egress unfiltered: the proxy that would hold it per host and per port sits behind a switch that ships off.
It asks per tool call, and matches the rule against the command text.
It asks, and a non interactive run refuses everything unless you pass a flag.
Reading, writing and running a command are approved with no prompt, on your machine.

HoldsIn partNot there

Why the source and not the manual

One harness documents its own hole: a deny rule on its shell tool misses the same command run through its eval tool. No summary says so. The method has also corrected us twice, most recently about Chock's own system call filter.

Two projects did leave Landlock

Zed (#58355, 2026-06-09) and Codex (#13996, merged 2026-03-12) both moved to bubblewrap. Landlock grants rights and cannot take one back, so a writable repository cannot have its .git carved out again. Neither pull request calls Landlock weak: every stated reason is about what it can express.

Chock met the same limit and answered it with a bind mount, which is the mechanism Codex moved to. The difference is sequencing rather than insight: the mount namespace was Chock's first layer, so Landlock never had to carry the filesystem alone.

Rules match the act, not the command text

Three of the five decide a shell command by matching its text. GTFOBins lists dozens of ordinary programs that walk straight through such a rule, so one permitting find permits everything. Chock keys on the act and on where the binary came from.

What this table does not say

All five have years of work behind them and do far more than Chock, which is pre-alpha. This table compares one thing: what a tool call can reach. They were built as coding tools that later added a sandbox. Chock started at the sandbox and the record, and the rest is being built behind them.

The status page says what is built and what is not.

What we read

  • Claude Code at 2.1.260, sandbox-runtime 0.0.74, read 2026-10-02
  • Codex at ca46606, read 2026-10-02
  • Zed at 5a9b955, read 2026-09-05
  • OpenCode at 012c2f5 (v1.18.11), read 2026-08-21
  • oh-my-pi at 9350b799, read 2026-08-21

A project is in the table only where we can name the commit we read. Claude Code's sandbox is a package of its own, sandbox-runtime, so that is the version beside its name.