Repository navigation
Security Model
Adi edited this page Oct 19, 2026
·
5 revisions
Explanation. What agentbox defends against, how each layer does it, what's been tested, and what remains.
The executor model, and the code it writes, are untrusted. It reads repositories, issues and dependencies that may contain prompt injection, and it runs whatever it decides to run. It must not be able to:
- reach the network, or exfiltrate code or secrets directly;
- read or modify anything on the host beyond its scratch workspace;
- escalate privileges or reach the Docker daemon;
- talk to other agents;
- choose a different model, or spend unbounded compute or money;
- get code executed outside the sandbox, either through the results it returns or by persuading the planner.
Out of scope: a malicious operator, other local users with Docker access (Docker access is root), and kernel 0-days. For the last, see gVisor below.
| Boundary | Enforced by |
|---|---|
| Agent ↔ network |
NetworkMode none: only lo. The single way out is the task's gateway socket. |
| Agent ↔ host | recommended: gVisor user-space kernel (runsc) · read-only rootfs · CapDrop ALL · no-new-privileges · default seccomp + AppArmor · uid 64000, which doesn't exist on the host · private IPC · pids/memory/CPU caps · tmpfs /tmp and $HOME · only its workspace, its socket dir and an optional read-only venv are mounted · no Docker socket · the image has no package manager, su/sudo or setuid files. See [[Sandbox specification |
| Agent ↔ model API | Per-task gateway: one-time token, endpoint allowlist, model pinned, per-task token budget. Server-side tools, MCP servers, hosted containers and beta headers are stripped, so no API can fetch, search or connect for the agent. |
| Agent ↔ other agents | One socket per task, mounted only into that task. Socket dirs are 0711 and the state dirs 0700. |
| agentbox ↔ Docker | Only the launcher has docker.sock. Callers choose which task and model to run, never how. Paths are derived from the task id and validated. The command must be claude … (or a check command in a socket-less sandbox). Env allowlist, venv-root allowlist, hard deadline. |
| Results ↔ host | Results are collected through a trusted shadow git dir; nothing in the agent's .git runs. See [[Collecting results safely |
| Results ↔ planner | Report, diff and check output are labelled untrusted data in tool results, MCP instructions and the delegation skill. |
| Keys ↔ everything | Provider API keys live only in llm-bridge (secrets.env). agentbox and the agents never see them. |
| Your repos ↔ agentbox | Nothing is mounted. Each repo operation runs in a short-lived git helper (your uid, no network, no capabilities, read-only root) that mounts only the one folder the planner passed. It refuses paths outside the AGENTBOX_REPOS_ROOT allowlist, folders that aren't a git repo owned by you (so a symlink to /etc gets nothing), and folders that don't exist (it never creates them). |
| Planner ↔ agentbox | MCP on 127.0.0.1 only, with a bearer token on every request and the MCP library's DNS-rebinding guard. |
scripts/smoke-test.sh checks a live agent container:
-
NetworkMode noneand onlylo; - read-only rootfs;
-
CapDrop [ALL]andCapAdd []; -
no-new-privileges; - uid 64000, from both
docker inspectandid -uinside; - exactly two mounts;
- the returned branch passes
run_checks, inside a sandbox with uid 64000 and onlylo.
Also verified:
-
Behavioural probes inside the image:
CapEff=0,NoNewPrivs=1,Seccomp=2, DNS and HTTPS fail, writing to/usrfails, no Docker socket, noapk/su/sudo, no setuid files. -
Git-hook attack: planted hooks,
core.hooksPathandcore.fsmonitornever ran outside the sandbox. -
run_checksoption injection: a branch named--upload-pack=…is rejected; nothing is created. -
tests/test_gateway_bridge.py(36 checks): token, endpoint allowlist, model pinning, budget, server-tool and beta stripping, and key injection in llm-bridge only.
- Shared kernel (without gVisor). Under runc, a kernel exploit is a container escape. gVisor is recommended and auto-enabled by setup when installed: a sandboxed process then talks to gVisor's user-space kernel, and an escape needs a gVisor bug and a host-kernel bug. See Run agents under gVisor.
-
The launcher holds
docker.sock. Its small API and validation are the boundary; audit it first. -
agentbox parses model output. It has no capabilities, a read-only rootfs and none of your repos
mounted. A compromised agentbox could ask the launcher to snapshot or deliver to any repo you own under
the allowlist, but only through fixed git operations: copy
HEAD, write or delete anagent/<id>branch. -
No disk quota on workspaces. A runaway agent can fill the disk that holds
AGENTBOX_DATA. - Remote models send your code to the provider. That's inherent; the gateway just pins where it goes.
-
The planner itself. Keep normal permission prompts in your planner session, not
--dangerously-skip-permissions, so a successful injection still needs your approval to act on the host. - Single-user design. Everything binds to 127.0.0.1. Don't expose ports 8765 or 8790.
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