Skip to content

Sandbox Specification

Adi edited this page Oct 19, 2026 · 4 revisions

Reference. The exact container settings the launcher uses. They are built in code (agentbox/launcher.py); callers can't change them.

Agent and check containers

Setting Value
Image agentbox/agent:local (Chainguard wolfi-base; Claude Code; no apk, su, sudo or setuid files)
User AGENTBOX_AGENT_UID:AGENTBOX_AGENT_GID (64000:64000)
Network NetworkMode: none: a private namespace with only lo. NetworkDisabled is deliberately unset (under gVisor it removes lo too).
Root filesystem read-only
Capabilities CapDrop: [ALL]; none added
Security options no-new-privileges:true; Docker default seccomp and AppArmor
Init tini (Init: true)
IPC private
Limits pids 512 · memory 8 GiB, swap 0 · 4 CPUs · nofile 4096
tmpfs /tmp 2 GiB (1777) · /home/agent 1 GiB (owned by agent uid, 0700)
Mounts (agent) worktrees/<id> → /workspace (rw) · sockets/<id> → /run/gw (rw, contains only llm.sock) · optional venv → /opt/venv (ro)
Mounts (check) worktrees/<id> → /workspace (rw) · optional venv (ro). No socket.
Command (agent) agent-shim → claude … (must start with claude)
Command (check) bash -c <command>
Env allowlist ANTHROPIC_AUTH_TOKEN, CLAUDE_CODE_MAX_OUTPUT_TOKENS, AGENT_NO_GUIDE, MAX_THINKING_TOKENS; plus model ids, venv PATH, PYTHONDONTWRITEBYTECODE=1
Deadline agent: timeout_minutes (max 4 h); check: max 1 h. Killed by the launcher even if agentbox dies.
Logs json-file, 20 MB × 2
Runtime AGENTBOX_AGENT_RUNTIME: runsc (gVisor) is recommended and needs --host-uds=open; unset means runc. Validated by the launcher at startup.
Labels agentbox.role (agent or check), agentbox.tid, agentbox.model

Git-helper containers (seconds per repo operation)

The agent image, run as your uid with no network, CapDrop: [ALL], no-new-privileges, a read-only root, a 64 MB tmpfs /tmp, 64 pids, 1 GiB memory and the runc runtime. Each runs one fixed script:

  • probe the folder;
  • snapshot a commit;
  • deliver an agent/<id> branch;
  • delete such a branch;
  • check a venv.

It mounts exactly what that operation needs:

  • the one repo (read-only for snapshots, read-write for delivery);
  • for a worktree, also its common .git dir;
  • the task's shadow dir.

Mounts use the Docker Mounts API, so a missing path is an error, never created. The helper exits with an error unless the folder is owned by you.

Ownership-handoff container (seconds per task)

The same image, run as root with CapAdd: [CHOWN, DAC_OVERRIDE, FOWNER] only. It has no network, a read-only root, no-new-privileges and pids 16. It mounts only the one workspace and runs chown -R -h <uid>:<gid> /w (never follows symlinks) and chmod -R u+rwX /w. It first checks that /w is the exact device:inode the launcher validated, so a path swapped after the check is refused. It always uses runc: it executes no untrusted code.

vLLM backend containers

NetworkMode: host (binds 127.0.0.1:port), all GPUs, models_dir → /models (ro), vllm_cache → /cache, journald logging (tag agentbox-<model-id>), auto-remove, plus the memory, shm_size / ipc_host, env, entrypoint and args from models.yaml.

Service containers (compose)

agentbox, agentbox-launcher, llm-bridge and ollama all run as your uid with a read-only root, cap_drop: ALL and no-new-privileges, with memory and pids limits.

  • The launcher uses network_mode: none and has the docker group plus the socket.
  • The others use host networking and bind to 127.0.0.1.

Clone this wiki locally