Sandboxing
The runtime can wrap tool invocations in an OS-level sandbox that restricts filesystem access to the workspace and removes access to the parent process’s secrets. This is distinct from the autonomy system and command allow-list: those are policy layers that decide whether a tool may run; the sandbox is a mechanism layer that confines what a running tool can reach if it does run.
Sandbox settings live on a risk profile. Each agent points at a risk profile via agents.<alias>.risk_profile; the agent’s sandbox enable/backend are read from that profile.
sandbox_enabled = false (or sandbox_backend = "none") disables sandboxing for tools running under this profile. See the canonical Minimal working example for how a risk profile slots into the rest of the config.
Auto-detection
sandbox_backend = "auto" picks the best available backend at startup:
| Platform | Preferred order |
|---|---|
| Linux | Landlock (kernel 5.13+) → Bubblewrap → Firejail → Docker → none |
| macOS | Seatbelt (sandbox-exec, native) → Docker → none |
| Windows | AppContainer (experimental) → Docker → none |
| Any | Docker (if daemon reachable) → none |
To force a specific backend, set sandbox_backend to one of the literal values listed above.
What the sandbox confines
Filesystem
- Read access: restricted to the workspace,
/usr,/lib,/etc(read-only), and explicitly-listed extra paths. - Write access: restricted to the workspace and
/tmp. - Forbidden paths: anything listed in
[risk_profiles.<alias>].forbidden_paths.
Network
By default, sandboxed tools have full network egress but no inbound listening. Per-backend caveats:
- Landlock does not control network, it is filesystem-only.
- Bubblewrap and Firejail can block network when configured.
- Docker container network mode follows
[runtime.docker].networkwhen[runtime].kind = "docker".
Tool-specific network gates (browser, HTTP, web_fetch) live on those tools’ own config blocks ([browser].allowed_domains, [http_request].allowed_domains, [web_fetch].allowed_domains).
For http_request, private/local targets remain blocked by default. Use [http_request].allowed_private_hosts to allow only named private/local hosts such as localhost or 10.0.0.1 while keeping [http_request].allowed_domains non-empty; allowed_domains = [] still disables requests. The existing [http_request].allow_private_hosts = true setting remains a broader compatibility opt-in.
Environment
The sandbox passes through only the env vars listed in [risk_profiles.<alias>].shell_env_passthrough. Inherited secrets do not reach sandboxed tools unless explicitly passed.
Process limits
Per-tool wall-time timeouts live on the tool’s own config block ([shell_tool].timeout_secs, etc.). Docker-specific limits (memory, CPU) live on [runtime.docker] when the agent’s runtime kind is set to docker:
Per-backend notes
Landlock
The Linux-native path. Zero setup, kernel-enforced, very low overhead. Requires kernel 5.13+.
Limitations:
- No network confinement: Landlock only controls filesystem access.
forbidden_pathsis enforced via path-based rules, not inode-based, so a clever symlink can sometimes escape (we resolve links before handing to Landlock to mitigate this).
Bubblewrap (bwrap)
User-namespace-based sandbox from Flatpak. Confines filesystem and can block network. Requires bubblewrap installed.
Debian/Ubuntu
sudo apt install bubblewrap
Arch
sudo pacman -S bubblewrap
Fedora
sudo dnf install bubblewrap
Firejail
SUID-based sandbox. Older but widely available.
sh
sudo apt install firejail
Firejail’s default profile is fairly permissive; ZeroClaw applies a custom profile. Pass extra args with firejail_args on the risk profile.
Docker
Works anywhere Docker does. The Docker runtime kind ([runtime] kind = "docker") runs each shell invocation in an ephemeral container; see the [runtime.docker] block above for image and resource controls.
sh
docker build -t zeroclaw-sandbox:local dev/sandbox/ # build the bundled toolkit image
Pros: strong isolation, works on any OS. Cons: per-invocation container startup cost (100–500 ms). Best for production deployments where the overhead is acceptable.
Seatbelt (macOS)
Native macOS sandbox (sandbox-exec). Profiles are SBPL: ZeroClaw bundles one for tool runs. Works on macOS 10.11+.
Limitation: some CLI tools (older git, some Homebrew-linked binaries) don’t cooperate with Seatbelt’s file-access rules. If you see “Operation not permitted” errors from the agent’s shell calls on macOS, the tool needs broader filesystem access: consider switching to Docker.
none
No sandboxing. Tools run with the full privileges of the ZeroClaw service user. This is what YOLO mode enables. Loud, obvious, intentional.
Troubleshooting
- “Sandbox backend unavailable” on startup: check
zeroclaw service statusand the journal; the auto-detect logs which backends it tried. - Tools working on dev, failing in service: the service user often differs from the CLI user. Verify both have whatever sandbox-adjacent permissions are needed (Landlock: nothing; Bubblewrap: userns enabled; Docker: service user in
dockergroup). - Slow tool invocations on the Docker runtime: first invocation pulls the image, subsequent are fast. Pre-pull with
docker pull <image>.
Code reference
- Detection:
crates/zeroclaw-runtime/src/security/detect.rs - Backends:
crates/zeroclaw-runtime/src/security/sandbox/(one file per backend) - Schema:
RiskProfileConfigandDockerRuntimeConfigincrates/zeroclaw-config/src/schema.rs