Skip to content
Documentation

Sandbox

Running shell commands in a resource-capped container.

By default, a command the agent runs is a command on your machine, with your user and your environment. The sandbox changes that: shell calls execute inside a disposable container with capped resources and no network.

$ forge --sandbox docker "run the test suite and fix what fails"

Two modes

--sandboxbehaviour
none
The default. Commands run directly in the workspace, with your toolchain and your credentials.
docker
Each shell call runs in a container. Requires a working Docker daemon; Forge exits with code 2 if it cannot reach one.

Set it per run with --sandbox, per repository in forge.toml, or per shell with FORGE_SANDBOX. Only the shell tool is affected - file edits are governed by the workspace root, and reads never leave it.

Container defaults

settingdefault
image
python:3.12-slim - Override for a prepared toolchain.
cpus
2.0 - CPU quota for the container.
memory
2g - Hard memory cap.
network
disabled - --network=none unless enabled.
pids
512 - Process-count ceiling.
capabilities
--cap-drop=ALL - Plus --security-opt=no-new-privileges.

The caps exist for a specific failure: a loop that runs a runaway build, exhausts memory, or forks until the machine stops responding. A container that hits a limit dies and returns an error the agent has to read, instead of taking your laptop with it.

Networking is off

Containers start with --network=none. Nothing the agent runs can reach the internet, your VPN, or a service on localhost.

This is the setting most likely to surprise you. A test suite that hits a live API fails in the sandbox, and dependency installs fail too - so prepare an image with your dependencies already in it, or enable networking and accept what that means.

Privileges are dropped

Containers run with --cap-drop=ALL and --security-opt=no-new-privileges, so a command cannot acquire capabilities it did not start with.

Choosing an image

The default image is python:3.12-slim, which is enough for a Python project and wrong for most others. Point it at an image that has your toolchain - ideally the one your CI already uses, so a passing run means the same thing in both places.

forge.toml
# forge.tomlsandbox = "docker" [sandbox_options]image = "node:22"memory = "4g"

If the agent reports that a command is missing, the image is the first thing to check - the command exists on your machine and not in the container.

What the sandbox does not do

Worth being precise about, because a partial boundary described as a complete one is worse than no boundary at all.

  • The workspace is still writable. That is the point - the agent has to be able to edit your code. The container bounds what a command can reach, not whether your files can change.
  • It is Docker's isolation, not more. Forge inherits the container runtime's boundary and its weaknesses. A container escape is a container escape.
  • Only shell calls are sandboxed. Filesystem and git tools run on the host, governed by the workspace root and the permission policy.
  • The model call goes out regardless. Disabling container networking does not make the run offline; the agent still talks to your provider from the host.

When to turn it on

  • Unattended runs, especially with --yolo - this is the pairing that makes it sane.
  • Untrusted code: a dependency audit, a stranger's pull request, an unfamiliar repository.
  • Any task where a wrong rm would ruin your afternoon.
  • CI, where the container is the environment anyway.

For everyday work in a repository you trust, none with the default approval mode is the pragmatic choice - you keep your toolchain, and destructive calls still stop for you.