An AI agent runs commands, edits files, calls tools and reads whatever it is pointed at, often for minutes with nobody watching. A sandbox sets in advance the worst thing it can touch when it goes wrong.
Evidence labels:
- Tested: reproduced in a local test.
- Docs: taken from the project’s documentation or source.
- Reported: taken from a third-party write-up or search summary, not checked at the source.
What is sandboxing?
A sandbox restricts what a program can see and do. Something the program cannot switch off enforces it: the kernel, a hypervisor or another machine. Asking an agent to behave is a prompt, not a sandbox.
A sandbox is a policy plus a mechanism. The policy covers five things:
| Restriction | The question it answers | Examples |
|---|---|---|
| Filesystem | What can it read and write? | Only the project folder, with git hooks read-only |
| Kernel and privileges | Which system calls and capabilities can it use? | Seccomp filters, dropped capabilities, no root |
| Network | Where can it connect? | No network, or an allowlist of domains |
| Identity | What can it authenticate as? | No SSH agent, scoped tokens, credentials held by a proxy |
| Resources and lifetime | How much, and for how long? | CPU and memory limits, a VM deleted afterwards |
The mechanism sets the strength. A permission prompt is enforced by a person clicking “allow”, an OS sandbox by the kernel, a VM by a hypervisor, a separate machine by distance.
Why agentic work needs it
A chatbot answers. An agent acts. Four properties make that a security problem:
- Real tools at machine speed. A shell, a git remote, a database connection. A mistake happens before anyone can look.
- Everything it reads can be an instruction. Models do not reliably separate data from instructions, so a web page, issue, README, dependency or tool result can steer the agent. This is prompt injection.
- Approvals do not scale. People habituate and click “allow”. Anthropic reports that sandboxing cut permission prompts by 84% in internal testing. A sandbox is what makes autonomy affordable.
- Behaviour is not deterministic. Design for the worst case, not the expected one.
What can go wrong
Three threats need different amounts of isolation:
- Honest mistakes.
rm -rfin the wrong directory, a force push, a migration against the wrong database. Almost any boundary handles these. - Prompt injection. The agent reads content that contains instructions and follows them. This is the realistic attack.
- A hostile or badly misbehaving model. Less likely. It sets the upper bound on boundary strength.
Simon Willison calls the dangerous combination the lethal trifecta: access to private data, exposure to untrusted content, and a way to communicate externally. With all three, whoever controls one piece of content the agent reads can steer it into sending data out. A sandbox cannot remove the need to read content. It limits what the agent can reach (filesystem, identity) and where it can send (network).
Things to be scared of (& how to turn it into a metric)
This section covers five popular ways agentic work becomes insecure. None needs a kernel exploit, because each uses permissions the agent already has. A prompt injection or an honest mistake can set off any of them.
It ends with a scoring system, 0 to 5 per way and 25 in total. The next section uses it to compare the popular ways of running a coding agent.
Credential theft
Needs no exploit and leaves no trace.
The agent can read anything its user can: SSH keys, cloud CLI tokens (~/.aws,
~/.config/gcloud, ~/.azure), API keys in environment variables and .env
files, git credential helpers, tokens in shell history. Taking them is a file
read. The repository does not change, so a diff shows nothing.
Forwarding host identity into a sandbox is a quieter variant. An SSH agent socket keeps keys on the host, but anything inside the sandbox can ask the agent to sign with every key it lists (reported, Hannecke). The theft is use, not copying, and lasts as long as the socket does.
What reduces it:
- Do not mount or forward credentials into the sandbox.
- Use short-lived, narrowly scoped tokens, so a leaked one costs little.
- Deny egress by default, so what is read cannot leave.
- Keep the real credential outside the sandbox and inject it at a proxy. Docker Sandboxes do this (reported).
- Protected-path lists only help partly. Claude Code write-protects credential
stores such as
~/.sshand~/.aws(docs), which stops changes, not reads. Keeping them out of reach is stronger.
Toolchain poisoning
Malicious code in scripts, lockfiles and git hooks that outlives the session.
The agent leaves something behind that a trusted component runs later, outside the
sandbox: a git hook or core.fsmonitor setting, a package.json install script,
a Makefile target, a CI workflow, a lockfile entry pointing at another source, a
virtualenv interpreter, an editor task or project setting. Nothing runs until a
person or pipeline runs it, so the damage lands later and with different
privileges.
This is the pattern behind the reported configuration-based sandbox escapes
(CVE-2026-25725, Pillar Security’s research) and the .git swap shown later
(tested). Review can miss it: ignored files, untracked files and .git do not
appear in a normal diff.
What reduces it:
- Write-protect hook, configuration and startup paths. Claude Code does. Codex
keeps
.git,.agentsand.codexread-only (reported). - Run builds and tests inside the sandbox, not on the host.
- Review configuration and lockfile changes, not only source.
- Verify
.gitbefore host git runs in a worktree the agent could write to. - Run CI from fresh checkouts with no secrets.
Lateral reach
LAN access, and access to host services or local-network services.
A sandbox’s network is often wider than expected. Reachable targets include
services on the host (databases, dev servers, local model endpoints, the Docker
socket), cloud metadata endpoints, LAN machines (printers, NAS devices, internal
dashboards) and anything the host reaches over a VPN. A host-only network such as
Apple container’s --internal blocks the internet but not services on the host
(reported, Hannecke). Pillar Security reported Docker socket access affecting
Codex CLI, Cursor and Gemini CLI (reported). Claude Code’s proxy refuses loopback,
link-local and metadata addresses (docs).
What reduces it:
- Default-deny egress, including private address ranges.
- Block cloud metadata addresses.
- Never mount the Docker socket into a sandbox.
- Bind host services only to what needs them, and require authentication.
- On a cluster, use network policies.
Environment persistence
Package caches, shell startup files, cache volumes, temp directories and other footholds.
A foothold survives if the sandbox does, or if the agent can write somewhere
shared: shell startup files (.bashrc, .zshrc, .profile), package caches and
global installs, scheduled jobs, named volumes reused by later sessions, shared
temp directories. Persistent sandboxes make this the default. A Docker Sandboxes
VM persists per workspace until deleted (reported), and so does Apple’s
container machine (docs).
What reduces it:
- Ephemeral sandboxes: the modified cache dies with the VM (Hannecke).
- A read-only root filesystem with a tmpfs for scratch space.
- No shared cache volumes, or read-only ones.
- Rebuilt images and protected startup files.
Approval fatigue
Approvals that people click through, and channels that skip approval.
A permission prompt is a human control, and humans habituate. If an agent asks ten
times a day, the eleventh “allow” is muscle memory, and an injected request can
look exactly like a legitimate one. Sandboxing reduces prompts (Anthropic reports
84% fewer in internal testing). But every approval that lifts the boundary turns
fatigue into full access: Codex’s retry without the sandbox, Claude Code’s
dangerouslyDisableSandbox. Approval-free modes (--yolo, bypassPermissions)
remove the gate. ACP adds another variant: the agent can ask the client for file or
terminal access, and in a test the client served a terminal command without
asking the user (tested).
What reduces it:
- A boundary that holds without approvals, so the rare prompt gets read.
- No in-band way to ask for more: turn off Claude Code’s escape hatch where
possible, and strip
fsandterminalcapabilities from an ACP client. - A hard boundary, not prompts, for unattended runs.
Seven questions to ask of any setup
- What is mounted, and can the agent write there?
- Which host sockets cross the line, and what identity travels with them?
- Which capabilities and system calls survive the defaults?
- Where can outbound traffic go?
- Who patches the guest kernel?
- What is still there after the sandbox stops?
- What will the host do on the agent’s behalf if it asks?
Which questions decide each fear
The hypervisor appears in none of the five fears, as Hannecke notes. Configuration decides each one:
| Fear | Decided mainly by questions |
|---|---|
| Credential theft | 1 (what is mounted) and 2 (sockets and identity) |
| Toolchain poisoning | 1 (writable mounts), 3 (what can run) and 7 (what the host does for the agent) |
| Lateral reach | 2 (sockets) and 4 (where traffic can go) |
| Environment persistence | 1 (what is mounted) and 6 (what survives) |
| Approval fatigue | 7 (what the host does for the agent) |
How setups are scored
Each setup gets 0 to 5 per fear, 25 at most. Higher is safer. Scores of 4 and 2 sit between the anchors. A 0 means the channel is handed to the agent, such as a forwarded SSH agent or a mounted Docker socket.
| Fear | 5 | 3 | 1 |
|---|---|---|---|
| Credential theft | Credentials held outside the boundary. Deny-by-default egress | Some in reach, such as the agent’s own login. Egress restricted by an allowlist | Everything the user can read. Open egress |
| Toolchain poisoning | Writes go to a disposable copy. Hooks, config and startup paths unreachable | Known hook and config paths protected. Project scripts and lockfiles writable | Writes anywhere the user can |
| Lateral reach | Default-deny network, private ranges included. No host services reachable | Internet restricted. Host or LAN partly reachable | Full host network and LAN |
| Environment persistence | Environment destroyed after each session | Persistent, with protected startup files and limited write paths | Startup files, caches and temp directories writable on the user’s machine |
| Approval fatigue | The boundary holds with no approvals, and the agent has no in-band way to ask for more | Prompts are rare, but one approval lifts the boundary | A person’s click is the main control, or approvals are off with no boundary |
The scores are a judgment from documented defaults with approvals on, not measurements. Setups that were not tested are scored conservatively.
Popular methods
Eight setups cover how agents are run day to day. The diagrams show where the agent runs, what encloses it, and where model credentials and network traffic sit. Each setup is scored on the five fears above, 25 at most. Setups run from the lowest score to the highest.
Every setup assumes the agent is used through a terminal, an exec into a
container, or the tool’s own app. No editor sits between the user and the agent, so
the ACP channel described under special considerations is absent from these scores.
- A dashed outline marks an isolation boundary.
- A red outline marks a machine with no boundary around the agent.
- Red arrows mark ways out of a boundary or channels into the host.
Pi, Oh My Pi and OpenCode in a terminal (5/25)
Things it gets right
- Pi. Its docs point to isolation you bring: a Gondolin extension, plain Docker or OpenShell.
- Oh My Pi. The README says destructive tools pause for approval (the flags were not checked).
- OpenCode. Per-tool
allow,askordenyrules for bash, edit, webfetch and external directories, with command patterns and per-agent overrides. - Adding a boundary. Run the agent under OpenShell (its README tutorial runs OpenCode) or in a container.
Things it gets wrong
- No boundary. The agent runs on the machine, as the user. The three agents share one pattern: at most an application-level permission layer.
- Pi. No built-in permission system. It “runs with the permissions of the user and process that launched it”.
- Oh My Pi. Its “task isolation” copies workspaces for convenience and is not a sandbox.
- OpenCode. No sandbox setting was found.
- Model access. The login sits where the same user can read it. Where any of the three stores it was not found.
- Network. Unrestricted.
Extra notes
- Oh My Pi is a fork of Pi. Its README lists OpenAI Codex as an OAuth provider, so a ChatGPT subscription can serve as the model.
- OpenCode defaults differ between sources, so check the version.
Codex or Claude, local runner (13/25)
Things it gets right
- A real sandbox for what it covers. Shell commands start inside an OS sandbox (Seatbelt on macOS).
- Network. Codex: off in
workspace-write. Claude Code: a proxy whose allowlist starts empty.
Things it gets wrong
- Covered surface. Shell commands only. In Claude Code, file tools, MCP servers and hooks run outside the sandbox (docs). Reads and writes made by the file tools are limited only by the user’s own permissions and the approval prompt.
- Way out. Approval is the boundary. Codex can retry a failed command without the
sandbox, with approval. Claude Code’s
dangerouslyDisableSandboxprompts in default modes. An approved command then runs with the user’s full access, and the prompt shows the command, not what it can reach.--yoloandbypassPermissionsremove the sandbox, the approvals or both. - Model access. A subscription login stored on the machine. Codex caches it in
~/.codex/auth.jsonor the OS keyring (reported).
Extra notes
- The app and the terminal are front ends for one setup: an engine on the same machine, as the same user. The Codex app is reported to share the CLI’s defaults.
- Claude’s desktop app also has Cowork, reported to run in a Linux VM.
- Score: credentials 2, toolchain 3, lateral reach 3, persistence 3 and approvals 2. The agent can ask to leave the sandbox and the covered surface is narrow.
NemoClaw (14/25)
Things it gets right
- Inherited from OpenShell. Landlock, seccomp, a deny-by-default proxy, binary hashing and credential placeholders. The agent never receives the inference provider key.
- Baseline policy. Operator approval for blocked hosts.
Things it gets wrong
- Presets widen it. The Personal tier lets any binary reach public and private
addresses on ports 80 and 443 with no HTTP inspection. Package presets (
npm,pypi,brew) allow installing code that may be malicious. Thegithub,jira,slackandtelegrampresets give write access to external systems. - Credentials. Provider keys live on the host in
~/.nemoclaw/credentials.json, mode 600, in plaintext (reported, GitHub issue). Tool credentials such as GitHub or Slack tokens have no placeholder mechanism (reported analysis). - Limits (docs). Policy is not enforced for an agent started outside the managed entrypoint. A same-UID agent can signal peer processes. Agents can change their own config. Raw filesystem writes bypass the scanners, and base64 or hex secrets evade regex scanning.
- Plugins.
openclaw plugins installin the Dockerfile installs without signature verification (reported). - Vulnerabilities. The same bulletin reportedly lists a high-severity NemoClaw flaw. It was not read.
Extra notes
- An open-source reference stack (Apache 2.0) that runs agents in OpenShell sandboxes. OpenClaw is the default agent. It adds onboarding, managed inference and a baseline network policy.
- Scored against the baseline policy. The Personal tier would drop lateral reach to 2.
- Alpha.
Microsoft Quicksand (17/25)
Things it gets right
- Isolation. A VM with its own kernel and a qcow2 overlay disk.
- Network by default.
MOUNTS_ONLYis the default: user-mode networking withrestrict=on, which “blocks all outbound connections from the guest to the real network”.NONEhas no network device at all. - No root and no Docker. It “needs no root privileges or Docker”.
- Approvals. The library has no prompts and no in-band way to widen access. The caller’s code picks the network mode and the mounts.
- State.
save,checkpointandrevertkeep and roll back state. - 9p mounts. They have a read-only option.
Things it gets wrong
- Forwards are not restricted. A
guestfwdrule connects the guest to any host or hostname the host can reach, andhostfwdlistens on127.0.0.1by default.FULLremoves the restriction. The docs describe no domain allowlist and no inspection of traffic. - Credentials. Not addressed. No injection and no placeholder scheme was found, so a secret the sandboxed code needs sits inside the VM, or reaches it through a forward the operator builds. The guest agent token is passed on the kernel command line.
- Files. Host directories share through an SMB3 server Quicksand ships (CIFS, the
default, mounted as guest with
sec=none). A read-only option for CIFS was not found. Writes land on the host. - Control channel. The host reaches the guest agent through a forwarded loopback port. The documented command line also opens a QMP socket on loopback. Whether either is authenticated beyond the token was not checked.
- Attack surface. QEMU is a large user-space device emulator, which is a wider
attack surface than a minimal microVM monitor. That is general knowledge and was not
checked against this project’s device list. The README states no threat model, and
SECURITY.mdis the standard reporting policy. - Persistence.
save=auto-saves on stop. Whether the overlay is deleted on exit by default was not found.
Extra notes
- An async Python API to launch, control and snapshot QEMU virtual machines, with a focus on sandboxing AI agents. A library from Microsoft’s GitHub organization (MIT), at version 0.13 to 0.14 in October 2026.
- Runs on x86_64 and ARM64 on macOS, Linux and Windows, using HVF, KVM or WHPX when available. Without them QEMU falls back to software emulation, 10 to 50 times slower.
- Prebuilt images include
quicksand-agentand a desktop image with a browser.
OpenShell (18/25)
Things it gets right
- Filesystem. Landlock, Linux 6.2 or later. Read-only:
/usr,/lib,/etc,/var/log. Read-write: the working directory and/tmp. Everything else is inaccessible. Startup fails if the baseline cannot be enforced. Locked at creation. - Process. Drops privileges, disables core dumps, and applies two seccomp filters
that block mounts,
pivot_root,bpf,ptrace,io_uring_setup,setnsand module loading. - Network. Deny by default. A proxy checks each connection by host, port and calling binary, pins each binary’s SHA256 on first use, and terminates TLS with an ephemeral CA. A veth pair in a network namespace routes all traffic through the proxy, so a process that ignores proxy variables still cannot bypass it.
- Credentials. Held by providers. The agent gets an opaque placeholder, which the proxy replaces only for authorized hosts, ports and paths.
- Independent test. A test of v0.1.2 reported that default-deny egress, binary matching and Landlock held, with logging gaps on the VM driver.
Things it gets wrong
- Weak L7 defaults. Endpoints without
protocol: restallow any method and path.auditmode, the default, logs violations and forwards them. - Still open. Data sent to any allowed endpoint. Application output, which is not scrubbed. Approvals, which widen the policy. The shared kernel, unless the sandbox runs on a VM.
- Vulnerabilities (reported). NVIDIA’s August 2026 bulletin lists two critical sandbox escapes (CVE-2026-65093 and CVE-2026-65083, CVSS 9.9), a path traversal that bypasses L7 policy (CVE-2026-65092), and command injection in the gateway and exec handler. The sources disagree on some scores.
Extra notes
- NVIDIA’s open-source runtime (Apache 2.0). A gateway creates sandboxes on Docker, Podman or host virtualization, or on Kubernetes. Linux, macOS on Apple silicon, and Windows through WSL 2 (experimental).
Codex or Claude, cloud runner (19/25)
Things it gets right
- Isolation. An isolated VM or container with a fresh checkout. No local files, SSH keys or LAN.
- Claude Code on the web (docs). An Anthropic-managed VM per session. GitHub credentials stay on Anthropic’s servers and a proxy attaches them. Network access defaults to a trusted allowlist, with none, custom and full available.
- Codex cloud (reported). An isolated container. Internet is off by default while the agent runs, with an optional allowlist.
Things it gets wrong
- Claude Code on the web (docs). Environment variables are readable by the agent. MCP connector traffic goes through Anthropic’s servers. The Anthropic API stays reachable with network access off.
- Codex cloud (reported). Internet is available during setup.
- Remaining exposure. Credentials and tools given to the environment, its network policy, and the output that gets reviewed. The isolation technology was not found documented.
Extra notes
- The app or terminal starts and reviews the task. The agent runs in the vendor’s infrastructure.
CubeSandbox cluster (19/25)
Things it gets right
- Isolation. Each sandbox is a KVM microVM with its own guest kernel. The VMM runs under a minimal seccomp allowlist.
- Network. CubeVS is an eBPF switch with one TAP device per sandbox and no shared bridge. Private and link-local ranges are always denied, which covers the host’s network and other sandboxes. CubeEgress is a per-host L7 proxy with domain, path and method rules. A request that matches no rule is denied, and traffic on ports without an L7 rule falls back to L3 and L4 policy.
- Credentials. Injected as headers at CubeEgress, so the sandbox never sees them.
- Cluster design. A stateless control plane: CubeAPI (Rust) and CubeMaster (the scheduler), with Redis holding metadata and events. Multi-node deployment is supported.
- Snapshots. CubeCoW snapshots and clones, auto-pause for idle sandboxes, and cross-node pause and resume through S3 (preview).
Things it gets wrong
- Internet on by default.
AllowInternetAccess=falseinstalls a blanket deny. - Credential injection. The template must carry the proxy’s CA. Injection over plain HTTP can leak a credential if DNS is poisoned.
- Snapshots. A compromised sandbox’s state can be preserved.
- Management plane defaults. Services bind to
0.0.0.0, CubeMaster has no authentication, Cubelet’s gRPC is plaintext, and CubeAPI accepts every request until an auth callback is set. Bind to a private NIC and firewall it. - Kubernetes. Deployment is preview, and its docs were not read.
Extra notes
- An open-source sandbox service from Tencent Cloud (Apache 2.0), compatible with the E2B SDK. The agent loop runs elsewhere and calls the API to create sandboxes that execute its code and tools. The score covers the sandbox, not the harness.
- Each compute node runs a Cubelet, which clones a template with CubeCoW and boots the VM through CubeShim and CubeHypervisor (RustVMM and KVM). It needs KVM.
Agent Substrate (21/25, projected)
The defaults score 17/25. The runtime setting alone does not change the score, because the metric does not grade a hypervisor against a user-space kernel. The gains come from compensating for the limitations Substrate lists.
Things it gets right
- Isolation. The WorkerPool runtime is set to
microvm(CHV, Kagent PR #2918, merged in September 2026), not the gVisor default. This moves the boundary from a user-space kernel to a hypervisor. Substrate states that “a linux container is not sufficient or supported”. Each actor has a private network namespace and its own storage partition, and is treated as hostile: “there is no meaningful security boundary inside an actor”. - Blast radius. One WorkerPool and node pool per tenant or trust level. A compromised guest then reaches only actors of the same trust level.
- Network. Default deny, with an explicit hostname allowlist in each EgressPolicy. All TCP goes through an egress proxy that authenticates the actor by certificate and does not trust an actor-supplied hostname or SNI. UDP is blocked at the node. DNS goes to an internal resolver with its own allowlist. Actors cannot reach each other by default, and an authenticating proxy covers actor-to-actor calls.
- Credentials. The actor never holds a secret. It sends a placeholder header, and the egress gateway swaps in a credential from a provider (Kubernetes Secrets by default) under a default-deny namespace policy. Injection is over HTTPS only, and the provider’s RBAC is scoped to the namespaces it needs. Each actor gets a one-hour workload certificate.
- Persistence. A fresh actor per task, and no resume of a suspect actor’s snapshot. The operator encrypts the snapshot volume.
- Approvals.
requireApprovalonly on destructive tools, with the boundary independent of it. - Output. Treated as untrusted until reviewed.
Things it gets wrong
- Allowed hosts and DNS. Both still carry data out.
- Listed limitations. Node blast-radius containment and inter-namespace network policy are future work, UDP is described as blocked in one page and as passthrough in another, DNS bypasses the proxy while filtering is in progress, storage is unencrypted, restore-time re-authorization is future work, and actor-to-actor authorization is left to the operator. The settings above compensate for these.
- Cluster tools. The tools Kagent ships (Kubernetes, Helm, Argo, Prometheus) act on the cluster by design.
- Hypervisor bugs. They remain possible.
Extra notes
- Agents run as actors on Kubernetes worker pods, with suspend, resume and snapshots. A caller such as Kagent starts them through a gRPC API, behind AgentGateway for JWT and per-tool authorization on A2A and MCP. Runtimes: Go or Python ADK, Codex, Claude, or a custom one.
- Pre-1.0. The settings above are a projection, and the radar’s outline shows the score with defaults.
- Substrate is the execution layer. Orchestrators such as Kagent run on top of it.
Best we can do (24/25, projected)
The sandboxed-agent setup, with a change for each deduction it takes. Nothing here has been built or tested as a whole. Each part says what is known.
- MicroVM per thread, deleted afterwards.
container run --rm, one per thread, no shared cache volumes. Persistence 5. - A work copy, not the live worktree. The VM mounts a copy of the project with
no host
.git. The host pulls a patch from the copy, you review it, and you apply it. This removes the.gitswap (tested) and puts hook, config and startup paths out of reach. Hook, CI and config paths in a patch are flagged for explicit review, and so are lockfile and install-script changes. Toolchain poisoning 5. - An ACP guard. Needed only when an editor is in the path. A process between Zed and the container that strips
fs,terminal,authandelicitationfrominitialize. It fails closed: any agent request outside an allowlist (session/update,session/request_permission) is dropped and answered with an error. The agent keeps its own shell inside the VM. The guard has no in-band way to ask for more power. Approval fatigue 5. The guard was written but not run end to end with Zed. - One way out, held by the host. An
--internalnetwork, which has no route to the internet. Traffic goes to a proxy on the host. The proxy allows the model relay, the gateway and a package mirror, logs every request, and adds the gateway token to allowed requests, so the VM holds no credential. Credential theft 5. This holds only if nothing else on the host forwards traffic, which the next item enforces. - A per-thread gateway identity. A short-lived JWT scoped to read tools by default, minted by a host broker and injected by the proxy. Send and write tools sit behind a narrower role.
- A host that answers on two ports.
--internaldoes not hide the host: the VM can still reach any host service that listens on the container network. So host services bind to loopback, which the VM cannot reach. The proxy and the model relay bind to the container network address only, and the relay authenticates callers. Firewall rules (pfon macOS) on the container interface drop all other traffic to the host. No Docker socket, SSH agent or home-folder mount. Verify from inside the VM: scan the host address and confirm only the proxy and relay ports answer. Lateral reach 5 by design, 4 until that check passes. - OpenShell for the network and credential parts. It provides, as one unit, the
network namespace that forces traffic through a deny-by-default policy proxy
(which closes the
--internalgap), binary-scoped egress rules and credential placeholders. It does not provide the ACP guard or the patch gate, and it shares the host kernel unless it runs on a VM. This combination was not tested. - Pinned images. A fixed image digest, rebuilt on a schedule. Builds and tests run inside the VM. CI runs from fresh checkouts with no secrets.
What stays open:
- Allowed destinations. The model relay, the gateway and the mirror are still channels. Anything the agent can read in the work copy can be sent through them.
- The patch. Prompt injection can still steer the agent, so the diff can carry poisoned code. The review gate depends on a person reading it.
- The VM boundary. Hypervisor and tooling bugs are real. Docker Sandboxes had escapes in virtio-fs and a socket relay (reported), and SharedRoot was a mount-level failure.
- Untested parts. The guard with Zed, the
pfrule, the proxy’s token injection and the patch gate.
Ranking
Columns, in order: credential theft, toolchain poisoning, lateral reach, environment persistence, approval fatigue.
| Setup | Cred | Tool | Lat | Pers | Appr | Total |
|---|---|---|---|---|---|---|
| Pi, Oh My Pi, OpenCode (terminal) | 1 | 1 | 1 | 1 | 1 | 5 |
| Codex or Claude, local | 2 | 3 | 3 | 3 | 2 | 13 |
| NemoClaw | 3 | 2 | 4 | 2 | 3 | 14 |
| Microsoft Quicksand | 3 | 3 | 4 | 3 | 4 | 17 |
| OpenShell | 4 | 3 | 5 | 3 | 3 | 18 |
| Codex or Claude, cloud | 3 | 3 | 5 | 4 | 4 | 19 |
| CubeSandbox cluster | 3 | 4 | 5 | 3 | 4 | 19 |
| Agent Substrate, hardened (projected) | 4 | 4 | 5 | 4 | 4 | 21 |
| Best we can do (projected) | 5 | 5 | 4 | 5 | 5 | 24 |
--yolo and bypass modes move the local setup toward the unsandboxed rows. Editor
use through ACP adds a channel into the host that these scores exclude. It is
scored separately under special considerations.
Special considerations
Some setups add a threat that the scores above leave out.
ACP
The Agent Client Protocol (ACP) connects an editor (the client) to an agent. The client starts the agent as a subprocess and speaks JSON-RPC over stdio. Zed is the example client here. Other editors implement the same protocol. Three facts hold in every variant:
- Capabilities. The client advertises what it will do for the agent. Zed
advertises
fs,terminal,authandelicitation(tested). An agent can use them to ask the client to read files, write files and run commands. - Where requests run. The client serves them in its own context, on the host. A test with an agent in a VM showed a terminal command running on the host (tested, indirect evidence).
- Approvals. The agent decides whether to send
session/request_permission. Zed’stool_permissionsrules appear to apply only to its own agent (reported).
The variants differ in what encloses the agent.
Without a sandboxing client (5/25)
Things it gets right
- Codex adapter. Whether Codex’s own sandbox applies under the
codex-acpadapter was not tested, so it may still help.
Things it gets wrong
- No boundary from the client. Oh My Pi has none.
- Model access. An OAuth login stored on the host by the agent. Codex caches it in
~/.codex/auth.json(reported). Where Oh My Pi stores it was not found. - Network. Unrestricted.
- Second channel. The agent can ask the client to read, write and run things.
Whether
codex-acpdoes so was not tested.
Extra notes
- Agents: Oh My Pi, or Codex through the
codex-acpadapter, which runs as a local subprocess. A ChatGPT subscription is one of the adapter’s authentication methods.
With a sandboxing client (12/25)
Things it gets right
- OS sandbox. The client launches the agent under Seatbelt on macOS or bubblewrap on Linux.
- Filesystem. Writes are denied unless a path is allowlisted.
- Network. Through a host proxy with a domain allowlist. An empty list means no network.
Things it gets wrong
- Reads. Allowed unless a path is denied (docs).
- Proxy. It does not inspect traffic, and domain fronting may bypass it (docs).
- Model access. The agent needs an allowlisted model endpoint and a login it can read from inside the sandbox.
- Channel gap.
fsandterminalrequests are served by the client, outside the sandbox. This follows from a test with an agent in a VM. It was not tested with a wrapper. - Kernel. Shared with the host.
Extra notes
- Zed has no setting for this (not found). The
agent_serverscommand can be a wrapper instead, such assrtfromsandbox-runtime, which wraps any command. - Not tested with Zed.
With a sandboxed agent (13/25)
Things it gets right
- VM. Oh My Pi runs in an Apple container VM with its own kernel. The only mount is the git worktree.
- Model access. The container holds no API key. A relay on the host (the Oh My Pi auth provider and model server) holds the Codex subscription login and serves model requests.
- Tools. An authenticated gateway, with the MCP servers federated behind it.
Things it gets wrong
- Channels. A test showed the agent asking Zed to run a terminal command, which ran on the host.
- Network.
containerhas no egress allowlist.--internalremoves the route to the internet, not to the host (reported). Restricting the rest needs a host proxy and host firewall rules.
Extra notes
- Zed speaks ACP to the agent over stdio.
The three variants are scored with the ACP channel included, on the same five
fears. Through a terminal or an exec into the same container, the channel does not
exist, and the sandboxed agent would not lose points for it. The ACP guard in the
best-we-can-do setup applies only when an editor is in the path.
| Variant | Cred | Tool | Lat | Pers | Appr | Total |
|---|---|---|---|---|---|---|
| ACP, no sandboxing client | 1 | 1 | 1 | 1 | 1 | 5 |
| ACP, sandboxing client | 2 | 2 | 3 | 3 | 2 | 12 |
| ACP, sandboxed agent | 2 | 2 | 2 | 4 | 3 | 13 |
Conclusion
A setup’s name says little about its safety. What counts is what encloses the agent, which channels cross that boundary, and what the agent holds inside it.
- Nothing scores 25. The lowest setups run the agent as the user with no boundary (5/25). The built-in sandboxes of Codex and Claude Code cover shell commands only, and one approval lifts them (13/25). Isolated clusters and VMs with injected credentials reach 19 to 21, and the best projected setup reaches 24.
- Credentials and egress decide the most. A VM that holds a key, or can reach the internet freely, leaks everything inside it. The setups at the top keep keys out of the sandbox and route all traffic through one proxy that injects them.
- Approvals are not a boundary. A setup that depends on a person reading prompts loses points on approval fatigue and lateral reach. A boundary that holds with no approvals, and gives the agent no in-band way to ask for more, scores higher.
- Channels into the host matter as much as the sandbox. ACP’s
fsandterminalrequests, a mounted.git, a forward or a shared folder each bypass an otherwise good boundary. - Review what comes back. Poisoned code can pass through any sandbox in a patch, so output stays untrusted until a person has read it.
How to choose:
| Situation | Approach that fits |
|---|---|
| Quick interactive work on trusted code | Codex or Claude Code’s built-in sandbox, with network off or allowlisted |
| Unattended approval-free mode on a developer machine | A VM per container, a narrow mount, an egress proxy, no credentials inside, and no channel for the agent to ask the host |
| Opening an untrusted repository or dependency | A disposable VM holding only a copy of the code, no credentials, and deny-by-default egress |
| Many agents on a team | A cluster setup such as Agent Substrate with hardened settings, behind a gateway |
The scores are judgments from documented defaults, not measurements. Projected setups were not built or tested as a whole, and several products were assessed from their documentation alone. Treat a score as a way to compare designs, and check each claim before relying on it.