> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fased.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Sandboxing

# Sandboxing

Fased can run **tools inside Docker containers** to reduce blast radius.
This is **optional** and controlled by configuration (`agents.defaults.sandbox` or
`agents.list[].sandbox`). If sandboxing is off, tools run on the host.
The Gateway stays on the host; tool execution runs in an isolated sandbox
when enabled.

This is not a perfect security boundary, but it materially limits filesystem
and process access during unexpected or unsafe tool calls.

## What gets sandboxed

* Tool execution (`exec`, `read`, `write`, `edit`, `apply_patch`, `process`, etc.).
* Optional sandboxed browser (`agents.defaults.sandbox.browser`).
  * By default, the sandbox browser auto-starts when the browser tool needs it.
    This ensures CDP is reachable.
  * Configure auto-start with `agents.defaults.sandbox.browser.autoStart` and
    `agents.defaults.sandbox.browser.autoStartTimeoutMs`.
  * By default, sandbox browser containers use a dedicated Docker network
    (`fased-sandbox-browser`) instead of the global `bridge` network.
    Configure with `agents.defaults.sandbox.browser.network`.
  * Optional `agents.defaults.sandbox.browser.cdpSourceRange` restricts
    container-edge CDP ingress with a CIDR allowlist.
  * noVNC observer access is password-protected by default. Fased emits a
    short-lived token URL that resolves to the observer session.
  * `agents.defaults.sandbox.browser.allowHostControl` lets sandboxed sessions
    target the host browser explicitly.
  * Optional allowlists gate `target: "custom"`: `allowedControlUrls`,
    `allowedControlHosts`, `allowedControlPorts`.

Not sandboxed:

* The Gateway process itself.
* Any tool explicitly allowed to run on the host (e.g. `tools.elevated`).
  * **Elevated exec runs on the host and bypasses sandboxing.**
  * If sandboxing is off, `tools.elevated` does not change execution because
    tools are already on host. See [Elevated Mode](/tools/elevated).

## Modes

`agents.defaults.sandbox.mode` controls **when** sandboxing is used:

* `"off"`: no sandboxing.
* `"non-main"`: sandbox only **non-main** sessions. Use this when normal chats
  should stay on host.
* `"all"`: every session runs in a sandbox.
  Note: `"non-main"` is based on `session.mainKey` (default `"main"`), not agent
  id. Group/channel sessions use their own keys, so they count as non-main and
  will be sandboxed.

## Scope

`agents.defaults.sandbox.scope` controls **how many containers** are created:

* `"session"` (default): one container per session.
* `"agent"`: one container per agent.
* `"shared"`: one container shared by all sandboxed sessions.

## Workspace access

`agents.defaults.sandbox.workspaceAccess` controls **what the sandbox can see**:

* `"none"` (default): tools see a sandbox workspace under `~/.fased/sandboxes`.
* `"ro"`: mounts the agent workspace read-only at `/agent`; this disables
  `write`, `edit`, and `apply_patch`.
* `"rw"`: mounts the agent workspace read/write at `/workspace`.

Inbound media is copied into the active sandbox workspace (`media/inbound/*`).
Skills note: the `read` tool is sandbox-rooted. With `workspaceAccess: "none"`,
Fased mirrors eligible skills into the sandbox workspace (`.../skills`) so
they can be read. With `"rw"`, workspace skills are readable from
`/workspace/skills`.

## Custom bind mounts

`agents.defaults.sandbox.docker.binds` mounts additional host directories into
the container.
Format: `host:container:mode` (e.g., `"/home/user/source:/source:rw"`).

Global and per-agent binds are **merged**. Under `scope: "shared"`, per-agent
binds are ignored.

`agents.defaults.sandbox.browser.binds` mounts additional host directories into
the **sandbox browser** container only.

* When set (including `[]`), it replaces `agents.defaults.sandbox.docker.binds` for the browser container.
* When omitted, the browser container falls back to
  `agents.defaults.sandbox.docker.binds`.

Example (read-only source + an extra data directory):

```json5 theme={"theme":{"light":"min-light","dark":"min-dark"}}
{
  agents: {
    defaults: {
      sandbox: {
        docker: {
          binds: ["/home/user/source:/source:ro", "/var/data/myapp:/data:ro"],
        },
      },
    },
    list: [
      {
        id: "build",
        sandbox: {
          docker: {
            binds: ["/mnt/cache:/cache:rw"],
          },
        },
      },
    ],
  },
}
```

Security notes:

* Binds bypass the sandbox filesystem: they expose host paths with whatever mode
  you set (`:ro` or `:rw`).
* Fased blocks dangerous bind sources, including `docker.sock`, `/etc`, `/proc`,
  `/sys`, `/dev`, and parent mounts that would expose them.
* Sensitive mounts such as secrets, SSH keys, and service credentials should be
  `:ro` unless write access is required.
* Combine with `workspaceAccess: "ro"` if you only need read access to the
  workspace; bind modes stay independent.
* See [Sandbox vs Tool Policy vs Elevated](/gateway/sandbox-vs-tool-policy-vs-elevated)
  for how binds interact with tool policy and elevated exec.

## Images + setup

Default image: `fased-sandbox:bookworm-slim`

Build it once:

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
scripts/sandbox-setup.sh
```

Note: the default image does **not** include Node. If a skill needs Node (or
other runtimes), either bake a custom image or install via
`sandbox.docker.setupCommand` (requires network egress + writable root +
root user).

Sandboxed browser image:

```bash theme={"theme":{"light":"min-light","dark":"min-dark"}}
scripts/sandbox-browser-setup.sh
```

By default, tool sandbox containers run with **no network**.
Override with `agents.defaults.sandbox.docker.network`.

Security defaults:

* `network: "host"` is blocked.
* `network: "container:<id>"` is blocked by default (namespace join bypass risk).
* Break-glass override: `agents.defaults.sandbox.docker.dangerouslyAllowContainerNamespaceJoin: true`.

Docker installs and the containerized gateway live here:
[Docker](/install/docker)

## setupCommand (one-time container setup)

`setupCommand` runs **once** after the sandbox container is created (not on every run).
It executes inside the container via `sh -lc`.

Paths:

* Global: `agents.defaults.sandbox.docker.setupCommand`
* Per-agent: `agents.list[].sandbox.docker.setupCommand`

Common pitfalls:

* Default `docker.network` is `"none"` (no egress), so package installs will fail.
* `docker.network: "container:<id>"` requires `dangerouslyAllowContainerNamespaceJoin: true` and is break-glass only.
* `readOnlyRoot: true` prevents writes; set `readOnlyRoot: false` or bake a custom image.
* `user` must be root for package installs (omit `user` or set `user: "0:0"`).
* Sandbox exec does **not** inherit host `process.env`. Use
  `agents.defaults.sandbox.docker.env` (or a custom image) for skill API keys.

## Tool policy + escape hatches

Tool allow/deny policies still apply before sandbox rules. If a tool is denied
globally or per-agent, sandboxing doesn’t bring it back.

`tools.elevated` is an explicit escape hatch that runs `exec` on the host.
`/exec` directives only apply for authorized senders and persist per session. To
hard-disable `exec`, use tool policy deny. See
[Sandbox vs Tool Policy vs Elevated](/gateway/sandbox-vs-tool-policy-vs-elevated).

Debugging:

* Use `fased sandbox explain` to inspect effective sandbox mode, tool policy, and fix-it config keys.
* See [Sandbox vs Tool Policy vs Elevated](/gateway/sandbox-vs-tool-policy-vs-elevated)
  for the "why is this blocked?" mental model.

## Multi-agent overrides

Each agent can override sandbox + tools:
`agents.list[].sandbox` and `agents.list[].tools` (plus `agents.list[].tools.sandbox.tools` for sandbox tool policy).
See [Multi-Agent Sandbox & Tools](/tools/multi-agent-sandbox-tools) for precedence.

## Minimal enable example

```json5 theme={"theme":{"light":"min-light","dark":"min-dark"}}
{
  agents: {
    defaults: {
      sandbox: {
        mode: "non-main",
        scope: "session",
        workspaceAccess: "none",
      },
    },
  },
}
```

## Related docs

* [Sandbox Configuration](/gateway/configuration#agentsdefaults-sandbox)
* [Multi-Agent Sandbox & Tools](/tools/multi-agent-sandbox-tools)
* [Security](/gateway/security)
