Repository navigation
Security Model
Adi edited this page Oct 18, 2026
·
5 revisions
Threat model. The model inside the sandbox may be confused, compromised or prompt-injected (for example, by text in the repo or in an issue). It must not be able to:
- reach the network;
- read or modify anything on the host beyond its own scratch workspace;
- escalate privileges;
- talk to other agents;
- choose a different model or spend unbounded compute;
- get code executed outside the sandbox through the results it returns.
| Layer | Enforces |
|---|---|
| Agent container | See the full list below. |
| Per-task gateway | See the full list below. |
| Launcher | Clients choose which task and model to run, never how. See the full list below. |
| MCP server | Unprivileged (your uid, read-only rootfs, no caps), 127.0.0.1 only, bearer token on every request; only repos under AGENTBOX_REPOS_ROOT. |
| Result collection | The agent controls its .git (hooks, core.fsmonitor, core.hooksPath, filters in config). agentbox never runs git in it: it uses a trusted shadow git dir and a temporary index over the workspace files, with core.hooksPath=/dev/null and core.fsmonitor=false. |
Agent container:
-
NetworkMode none: onlyloexists. - Read-only root filesystem.
-
CapDrop ALL,no-new-privileges, default seccomp and AppArmor. - Runs as uid/gid 64000, which need not exist on the host. If a container escape ever happened, that identity owns nothing.
- Private IPC, tini as init.
- Limits: 512 pids, 8 GiB memory (no swap), 4 CPUs,
nofile4096. - tmpfs
/tmpand$HOME. - Mounts exactly its workspace, its gateway socket dir and, optionally, a read-only venv.
- The image has no package manager, no
suorsudo, and no setuid binaries. - Optional gVisor:
AGENTBOX_AGENT_RUNTIME=runsc.
Per-task gateway:
- One unix socket per task, mounted into that agent only.
- Allows only
POST /v1/messagesand/v1/messages/count_tokens, and only with the task's one-time token. - Rewrites
modelto the task's model. - Strips unsupported fields.
- Meters input and output tokens from the (streamed) responses and enforces a per-task budget (default 20M tokens; answers 403 once exhausted).
- Never forwards the agent's credentials or arbitrary headers.
Launcher:
- Workspace and socket paths are derived from the task id (
^[0-9a-z-]{1,64}$), resolved, and checked to be inside the data dir. - The workspace must be owned by the agentbox user.
- The command must be
claude .... - Only an env allowlist passes through.
- Venvs must live under configured roots.
- A hard deadline kills the agent even if the MCP server dies.
- The chown helper mounts only the one workspace and uses
chown -R -h, which never follows symlinks.
scripts/smoke-test.sh checks a live agent container:
-
NetworkMode noneand onlylo; - read-only rootfs;
-
CapDrop [ALL]andCapAdd []; -
no-new-privileges; - runs as the agent uid, from both
docker inspectandid -uinside; - exactly 2 mounts.
Behavioural probes inside the agent image:
-
CapEff=0,NoNewPrivs=1,Seccomp=2; - DNS and HTTPS fail;
- writing to
/usrfails; - no Docker socket;
- no
apk,suorsudo; - no setuid files.
Git-hook attack test. An agent was told to install pre-commit, post-commit and post-index-change
hooks, set core.hooksPath, and set core.fsmonitor to a command that drops marker files. After
collection, no hook marker existed anywhere. The fsmonitor marker appeared only inside the sandbox,
where the agent ran git itself. Nothing ran in the agentbox container.
-
The launcher holds
docker.sock, which is root-equivalent. Mitigations: it has no network, a minimal API, input validation, and it never parses model output. Audit it. -
The MCP server uses host networking so it can reach
127.0.0.1model servers. It holds no secrets besides its own token. It mounts your repos root read-write, which it needs to fetch result branches. -
Model servers run with GPU access on the host network, bound to
127.0.0.1. Another local user could call them. They are not reachable from agents except through a gateway. - The agent can read whatever is in the repo you delegate. Don't delegate repos containing secrets you wouldn't show the model. It cannot send them anywhere, but its output (diff and report) comes back to you.
-
Shared-kernel containers. For stronger isolation, use gVisor (
runsc) or a VM runtime. -
Single-user design. Everything binds to
127.0.0.1. Don't expose port 8765.
Tutorials
How-to guides
- Install
- Delegate from Claude Code
- Write good specs
- Python environments
- Add a local model
- Connect remote APIs
- Run under gVisor
- Operate
- Troubleshoot
Reference
Explanation