---
title: Sandbox
summary: "required, fixture, and host: where tool closures run, and what lock eligibility requires."
canonical: https://docs.caveman.so/docs/agent-sdk/sandbox
license: MIT
capability: agent-sdk
updated: 2026-08-30T15:03:57+02:00
basis: inferred
---

# Sandbox

> required, fixture, and host: where tool closures run, and what lock eligibility requires.
<DocSchema slug="agent-sdk/sandbox" />

Every agent has a sandbox mode. The default is `required`. The mode is set on the definition, not guessed at run time.

| Mode | Where tool closures run | Writes | Lock eligible |
|---|---|---|---|
| `required` | Isolated, network-denied Node workers, from a staged source graph | Allowed inside the worker | Yes, when the rest of the build passes |
| `fixture` | Host process. Trusted tests only | Blocked before execute | For fixture corpora |
| `host` | This process, no worker | Execute | No |

Host mode is an explicit opt-in. It is never the default, and it is never isolation. A host-mode child under a `required` parent is refused, so a subagent cannot walk out of its root's containment.

`compile` refuses a host-mode agent with `cave_host_sandbox_lock_ineligible` before any search run. Live coding sessions therefore cannot produce a Cave Build. Coding agents that need a lock compile against fixture corpora with a contained sandbox.

## Required mode

Before provider traffic, the framework copies the project-relative source graph into a per-run immutable staging directory, imports tools only from that snapshot, and tears the staging down when the stream settles. `RunOptions.entryPath` must point at the module that exports the definition. The CLI fills this in. Programmatic `run()` of a tool-using required-sandbox agent without `entryPath` is refused.

The worker starts from a fixed environment baseline (`LANG`, `LC_ALL`, `PATH`, `TZ`, plus a fixture marker), not a spread of the parent env. One runtime-owned provider capability may be requested: `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, or the Google pair (`GEMINI_API_KEY` / `GOOGLE_API_KEY` as aliases). Signing, deployment, bootstrap, database, loader, and ambient secret names are denied.

Network egress is blocked at the OS: a network namespace on Linux (`unshare --net`), `sandbox-exec (deny network*)` on macOS. In-process monkeypatching is defence in depth, not the boundary. Requesting unbounded egress fails closed with `cave_sandbox_network_egress_unbounded`. There is no scoped-egress mechanism yet.

Linux unix-domain sockets are not covered by a network namespace. That gap is documented in the package threat model; do not treat required mode as a complete host firewall.

## Windows

Ordinary agent runs, runtime startup, and explicit `sandbox: "host"` coding tools work on native Windows through `cmd.exe`. `sandbox: "required"` is fail-closed on native Windows because the package has no verified OS network-isolation boundary there. Use WSL2 for production sandboxed tools. `doctor` reports `cave_sandbox_os_network_isolation_unavailable` and the WSL2 remedy. Do not replace required sandbox with fixture mode in production.

## Fixture mode

Trusted tests may set `sandbox: "fixture"`. Closures run in the host process, which is why writes are blocked: a test that needs to observe a write belongs on a contained worker or an explicit host-mode agent, not in fixture.

## What it will not do

`sandbox: "host"` is not a synonym for isolation. Required mode is not available on native Windows. Fixture mode is not a production fallback. A passing doctor sandbox check is a containment probe, not a proof that every OS gap is closed.
