Skip to content

Agent and tool sandboxes

A box contains more than one process. The agent runs in the agent’s sandbox. Each tool and each local MCP server it starts runs in its own sandbox, with its own grants. Box’s shell, its Python, the egress gateway, and the MCP broker run in the box’s trusted process, outside every sandbox. Credentials belong to the box: every process in it holds every credential binding. Knowing where an action happens tells you what controls it.

The workload…ExampleContained byDecided by policy as
Uses its own file toolsThe harness edits src/main.rsThe agent’s [agent.filesystem] grantsNot decided
Runs a shell command Box implementsgrep -rn TODO srcBox’s shell, in the box’s trusted processshell:exec, then fs:* per file
Runs Pythonpython3 analyze.pyMonty, in the box’s trusted processfs:* per file
Starts a host binarygit commitThe tool’s sandbox, with its grantsshell:spawn
Calls a local MCP serverA tools/call to docsThe server’s sandbox, with its grantsshell:spawn at start, then mcp:call
Makes an HTTP requestThe model API, curl, fetch()The egress gatewaynet:connect and http:request

The policy sees the interpreters, the gateway, and the MCP broker. It doesn’t see inside a process’s own system calls: those are bounded by its sandbox grants alone.

The agent’s sandbox is the narrowest:

  • Files. The paths [agent.filesystem] grants, plus a small runtime minimum, such as /System/Library and /dev/null on macOS. Under your home, an ungranted path appears not to exist. The agent’s workspace is enterable, not readable, until a list grants it.
  • Programs. The agent’s own command and the interpreters its scripts name, its exec entries, and Box’s shell and Python aliases. Other programs go through Box’s shell, which raises a decision.
  • Shells and Python. bash, zsh, sh, python, and python3 come first on the agent’s PATH, and each one is an alias for Box’s interpreter.
  • Network. The egress gateway and Box’s telemetry port on loopback, and the broker’s socket, run/box.sock, which the aliases use. [agent] refuses a network table, so the agent can’t leave the gateway.
  • Environment. What [agent] env sets, plus HOME, PATH, credential placeholders, and the variables Box adds for the proxy, the CA, and telemetry. Box reserves loader and interpreter variables such as LD_*, DYLD_*, PYTHON*, and NODE_OPTIONS, so a table can’t set them.

When a shell:spawn permit starts a tool, Box builds a sandbox for that one launch from the tool’s [tool.<name>] table. It ends when the program does:

  • Files. The tool’s own six lists plus a wider runtime minimum, such as /usr/lib and /etc/ssl. Inside its grants the tool’s file access raises no decision. On macOS it also sees what exists across your home: it can test that a path exists and read its metadata. Contents stay behind its read grants, and the box directory and credential stores stay hidden.
  • Startup services. On macOS it can read the net.* system settings and open a routing socket at startup. Neither is outbound access.
  • Programs. On macOS a tool’s sandbox can run any binary it can reach, and load code it writes into its writable grants. An Apple /usr/bin stub, such as git, make, cc, or python3, hands off to the Command Line Tools, and that hand-off fails in a tool’s sandbox. Put the Command Line Tools binary itself on the path, as Write policy for shell commands shows.
  • Network. Through the egress gateway, under the same policy as the agent, unless [tool.<name>.network] sets contain_egress = false. Then it connects directly, with the arguments the agent passes, and with no gateway decision, credential injection, or traffic record.
  • Credentials. Every binding the box declares, the same as the agent.
  • No route back to Box. A tool’s sandbox has no aliases and can’t connect to the broker’s socket. A bash or python3 inside it is the host OS’s shell or Python, running in the tool’s sandbox, and raises no decision.

One permit covers the tool’s whole process tree. A git permit also covers the hooks git runs, and an npm permit covers the scripts in package.json. Treat a tool permit as trust in everything that tool will execute.

A local stdio MCP server runs in its own sandbox like a tool, after a shell:spawn permit, with the grants in [mcp.<name>]. Its [mcp.<name>.filesystem] table takes the same six lists as a tool’s, and refuses metadata and exec. A server with no grants still runs contained, and it holds every credential binding the box declares. A server with network.contain_egress = false connects directly to any IP address, loopback included, but to no other pathname socket, such as another box’s broker socket.

Box’s MCP broker decides each request the agent sends. For a local server, initialize, ping, server/discover, subscriptions/listen, and notifications pass undecided; every other method, including tools/list, raises mcp:call. A remote MCP server is reached through the gateway, so its initialize is decided as mcp:call, and its traffic is decided as net:connect and http:request as well.

A local server can send its own request to the agent, such as roots/list or sampling/createMessage. The agent’s reply is decided as mcp:call, with method set to the method of the request it answers, so a policy that permits only named methods must name these too. A reply to ping passes undecided.

mcp:call bounds what the agent asks the server to do. It doesn’t bound what the server does with its own grants. Box doesn’t list an MCP server’s grants in the startup disclosure, so read its [mcp.<name>] table.

You decide which sandbox runs a program by where you declare it:

Declared asRuns inDecision
[tool.<name>]Its own sandboxshell:spawn per start
[tool.<name>] with network.contain_egress = falseIts own sandbox, with direct network accessshell:spawn per start, and no net:*
An exec entry in [agent.filesystem], started through Box’s shellA new sandbox that runs with the agent’s own filesystem lists and a tool’s runtime reach, always through the gatewayshell:spawn per start
An exec entry, run directly by the agentThe agent’s sandboxNot decided
[mcp.<name>] with type = "stdio"Its own sandboxshell:spawn, then mcp:call

Prefer a tool table when a program needs files the agent shouldn’t have. Prefer an agent exec entry when a program should reach no more paths than the agent’s lists grant. It still runs with a tool’s runtime reach: on macOS it gets a tool sandbox’s broader exec, its larger runtime minimum, and path checks across your home. The startup report shows its grants under [agent], but not that wider reach. To keep a credential from a program, run it in another box.