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

# Workspace sandbox

> OS-level filesystem + network enforcement for the bash tool — separate from policy enforcement.

When the `bash` tool executes a command it runs inside an OS-level sandbox
configured from the agent's workspace. This is filesystem + network enforcement
at the kernel level — a separate concern from [policy enforcement](/concepts/policy-decision),
which decides *whether* a tool may be invoked at all.

## Runtime requirement

The `bash` tool depends on **`srt`** (Anthropic's `sandbox-runtime`). It wraps
each command in `sandbox-exec` + a Seatbelt profile (macOS) or `bubblewrap` + a
network namespace + a seccomp filter (Linux).

```bash theme={null}
npm install -g @anthropic-ai/sandbox-runtime
```

Supported on **macOS and Linux only** (Windows is unsupported). If `srt` is not
on `PATH`, `run_command` raises `SrtUnavailableError` rather than falling back to
unsandboxed execution — *fail closed by design*.

## Configuration

Tune the boundary through `LocalWorkspace`:

```python theme={null}
from hexgate.runtime import LocalWorkspace

workspace = LocalWorkspace(
    root_dir="./project",
    allowed_domains=["api.github.com", "*.pypi.org"],
    extra_read_paths=["/etc/ssl"],
    extra_write_paths=["/tmp/build"],
    deny_write_paths=[".env"],
    allow_unix_sockets=["/var/run/docker.sock"],
    allow_local_binding=False,
    extra_env={"NODE_ENV": "test"},
)
```

| Knob                  | What it controls                              | Default          |
| --------------------- | --------------------------------------------- | ---------------- |
| `root_dir`            | Workspace root; reads + writes allowed inside | required         |
| `allowed_domains`     | Hostnames the proxy forwards                  | `()` — no egress |
| `denied_domains`      | Hostnames the proxy refuses                   | `()`             |
| `extra_read_paths`    | Read-only paths beyond the workspace          | `()`             |
| `extra_write_paths`   | Writable paths beyond workspace + `/tmp`      | `()`             |
| `deny_write_paths`    | Paths the agent can never write to            | `()`             |
| `allow_unix_sockets`  | Unix sockets the agent can `connect()`        | `()` — no IPC    |
| `allow_local_binding` | Whether the agent can `bind(127.0.0.1, …)`    | `False`          |
| `extra_env`           | Env vars passed into the sandbox              | `{}`             |

Defaults add up to: no network egress, no IPC sockets, no localhost bind, reads
allowed inside the workspace and on system paths but not `$HOME`, writes allowed
only inside the workspace + `/tmp`.

`allow_unix_sockets` and `allow_local_binding` exist because they're the two ways
traffic can leave the proxy lane (Unix-domain IPC and inbound localhost).
Default-deny on both; opt in per-deployment when you actually need docker-socket
access, a local dev server, etc.

## Env scrubbing

The sandboxed child does **not** inherit the parent process's environment. Only
an explicit allowlist passes through:

* `PATH` (curated baseline including `/opt/homebrew/bin` for Apple Silicon)
* `HOME` (set to the workspace root, so cache writes land inside `allowWrite`)
* `TMPDIR`, `TERM`
* Locale keys: `LANG`, `LC_ALL`, `LC_CTYPE`, `LC_COLLATE`, `LC_MESSAGES`
* Anything operator-supplied via `extra_env`

This means parent-process secrets — `AWS_SECRET_ACCESS_KEY`, `OPENAI_API_KEY`,
`ANTHROPIC_API_KEY`, `GH_TOKEN`, `SSH_AUTH_SOCK`, etc. — **don't leak** into the
agent. Tools that legitimately need credentials should receive them through
`extra_env`, where you control exactly what's passed.

## Layering with policy + approval

| Layer        | Question                                            | Mechanism                                   |
| ------------ | --------------------------------------------------- | ------------------------------------------- |
| **Policy**   | Is this tool allowed at all for this caller's role? | adapter / `enforce_policy(...)`             |
| **Approval** | Should this specific call go ahead?                 | `enforce_policy(..., approval_handler=...)` |
| **Sandbox**  | What can the spawned shell actually do?             | OS-level via `srt`                          |

Policy decides whether the `bash` tool is callable. The approval handler inspects
each call gated by `approval_required`. The sandbox bounds reach *if a call does
run*. They're complementary — deploy whichever combination matches your threat
model.

## What the sandbox does NOT do

* **Resource limits.** No CPU/memory/fork caps. A fork-bomb runs to completion.
  Use cgroups or `ulimit` if that matters.
* **Command-string semantics.** `srt` sees `sh -c "<command>"` as an opaque arg.
  The sandbox bounds *reach*, not intent — `rm -rf <workspace>` is permitted
  because the workspace is in `allowWrite`.
* **Inside-sandbox actions.** The sandbox stops the agent from exfiltrating a
  workspace file over the network or writing outside the boundary, but doesn't
  reason about what the agent does *within* the boundary.
