Skip to content

Sandboxes ​

An agent outside a sandbox can do what you can: write anywhere you can, reach any host, read your cloud credentials and past transcripts. A sandbox narrows that to what the workflow names. awf holds it with one of two providers: srt, Anthropic's sandbox-runtime on your own machine, or docker, a container. Use one when an agent's input could steer it somewhere you don't want, such as a repository you didn't write or an issue anyone can file, or when an agent must not see what another one wrote.

A shared sandbox and a private one ​

ts
const box = await wf.sandboxes.open({
  key: "work",
  write: ["."], // the working directory; everything else stays read-only
  network: ["registry.npmjs.org"], // beyond each harness's own model API
  srt: {}, // the provider: srt on this machine, or docker: {} for a container
});
const coder = await wf.agents.open({
  key: "coder",
  runtime: "codex",
  sandbox: box,
});
const tester = await wf.agents.open({
  key: "tester",
  runtime: "codex",
  sandbox: box,
});

The coder and the tester run in one sandbox, under srt. Both can write the working directory, which is everything they need to fix and run the tests, and reach npm's registry besides their model. Nothing else is writable, and nothing else under your home directory is visible.

ts
// An inline spec: a sandbox of the reviewer's own. It reads the working directory, writes
// nothing, and reaches only its model.
const reviewer = await wf.agents.open({
  key: "reviewer",
  runtime: "codex",
  sandbox: { srt: {} },
});

The reviewer gets a sandbox of its own, given inline. { srt: {} } is the strictest one: it reads the working directory, writes nothing, and reaches only its model.

The whole workflow, and the test that checks what each agent was given, are in examples/site/guide/sandboxes.ts and the .test.ts beside it.

What a spec says ​

FieldWhat the agents inside get
readhost paths they can read, at the same path inside, beyond the working directory
writehost paths they can write; "." is the working directory
networkdomains they can reach beyond their harness's model API: registry.npmjs.org, or *.npmjs.org for its subdomains
socketshost unix sockets they can connect to, such as a browser or a database you run; srt only
webSearchtrue keeps the harness's web search, which is off inside otherwise
srt or dockerwhich provider holds it: required, exactly one

wf.sandboxes.open also needs a key, which names it in the run's record, and takes a cwd, which defaults to the workflow's working directory. An inline spec takes neither. Relative paths are from the sandbox's cwd, and ~ is your home.

srt or docker ​

srt is Anthropic's sandbox-runtime. Agents run on your machine with your toolchain: the same bun, node and compilers you use, minus your home directory and whatever the spec doesn't name. Each agent is wrapped on its own. It runs on macOS only for now; on Linux, use docker.

docker runs the agents in one container per sandbox, as your user, from awf's image or the one you name with docker: { image }. Paths you name are mounted at the same path, and traffic leaves only through a proxy that allows the spec's domains.

ts
await wf.sandboxes.open({ key: "build", write: ["."], docker: {} });
await wf.sandboxes.open({ key: "lint", write: ["."], srt: {} });

Every spec names its provider; there is no default. The two give the agents a different machine, your toolchain or an image, so what works under one can fail under the other, and the workflow says which it was written for. A provider that isn't installed, or can't be set up, fails the sandbox with the reason; awf never moves it to the other. What each needs on your machine is in Install.

TUI agents work under both. An srt agent's pane opens in the run's Herdr as usual. A docker sandbox runs its own Herdr inside the container. The run adds a tab to its workspace showing that Herdr's panes, and prints the command that opens them; awf run --no-watch leaves the tab out, and the command still works. Either way, a sandboxed agent can't take pane, which places a pane in the run's Herdr; the agent is refused if it names one, under awf run --sandbox too. --workspace and --keep-panes don't reach it.

Shared or private ​

A sandbox from wf.sandboxes.open is shared by every agent you pass it to. An inline spec is always a private sandbox, recorded as agent:{key}. A fork runs in its parent's sandbox.

Agents in one sandbox can read each other's files, including the private folder awf gives each agent for its harness's settings and copied login, such as codex's. Under docker they also share a container and a user, so one can read another's environment variables, a login token included. Agents in different sandboxes see none of each other's.

So put agents whose work must be independent in separate sandboxes: a reviewer and what it reviews, or two attempts at the same task you want to compare.

Inside a sandbox ​

  • A fresh settings folder. Each sandboxed agent's harness starts from a folder of its own holding its login and nothing else of yours. Your harness settings, skills, memory and past transcripts aren't there. The repository's own AGENTS.md is still read, and the skills the workflow names are copied in.
  • No permission prompts. The sandbox holds the agent, so the harness's prompts are turned off.
  • No web search unless webSearch: true. claude's and codex's search runs on the model provider's servers, past any network rule, so it could carry what the agent read out of the sandbox. claude's WebFetch goes with it. cursor keeps its web tools either way, and pi has none.
  • git works. An agent in a writable worktree can commit, but can't change its hooks or config, which your git runs later.
  • Logins work differently inside a sandbox for some harnesses; Install says what each needs.

Sandbox a whole run: awf run --sandbox ​

You can put every agent of a run in one sandbox without touching the workflow:

sh
awf run --sandbox box.json review.ts

box.json is an inline spec, such as {"read": ["/data/request.md"], "srt": {}}, naming its provider like any other, with relative paths from --cwd. Every agent runs in it, at the run's working directory. A workflow that opens a sandbox of its own, or gives an agent one, is refused:

text
every agent in this run runs in the sandbox awf run --sandbox gave it; a workflow cannot open its own

This is how you hold workflows you're comparing to the same reach, without trusting each one to ask for it.

Catches ​

  • It refuses, never loosens. A spec a provider can't hold, a provider that isn't installed, a path it won't expose: the agent doesn't open, and the error names why. Nothing runs with less confinement than you asked for.
  • Some paths are never allowed. Your home directory itself or anything holding it, ~/.ssh, ~/.aws, ~/.gnupg, ~/.docker, ~/.kube, your harnesses' own state, and awf's run and machine directories. Name a project under ~ instead.
  • An agent's working directory must be inside its sandbox's reach. What a sandbox can see is fixed when it opens, so an agent whose cwd lies outside it is refused.
  • docker needs awf's image built once. The first docker sandbox stops without it, and the error gives the docker build command (Troubleshooting).
  • The workflow isn't sandboxed. A sandbox holds agents. Your workflow code runs on the host with your authority (Security).
  • A sandboxed agent still uses your login. It runs on your subscription like any other agent.

The record of a run, out.json, lists every sandbox once: its provider, the spec with paths resolved, every domain reachable in the end, and each agent it held.

See also ​