Sandbox a run
Every agent (swarm) stage must declare how it’s contained — pond fails the
stage closed if it doesn’t. This guide is the practical “which knobs do I set”;
specs/sandbox-isolation has the internals.
The sandbox lives on the stage’s executor:
"sandbox": {
"profile": "untrusted-code-write",
"tier": "trusted",
"backend": "firecracker",
"allow_hosts": ["git.acme.com"],
"overrides": { "image": "your/agent:latest", "memory": "4g", "cpus": "2" }
}
1. Pick a profile
The profile fixes the isolation posture — you can’t loosen it with overrides.
| Profile | Filesystem | Network | Use when |
|---|---|---|---|
none | host, open | open | local dev only — never for untrusted code |
untrusted-code-read | read-only checkout | model API only (broker) | the agent only needs to read + call the model |
untrusted-code-write | throwaway writable copy | default-deny allowlist | the agent must edit, build, test, or install deps |
untrusted-code-write is the full coding harness: edits land in a disposable
copy (discarded after), and the agent can pip/npm/cargo install and git
because the allowlist opens those — while everything else is blocked.
noneis an explicit opt-out, not a default. It runs the agent unconfined (host network + filesystem + the run’s credential in env) — fine only for code you trust. Anonestage always raises anunconfined_sandboxpreflight warning, and a deployment that runs untrusted code should setPOND_REQUIRE_SANDBOX=true, which refusesnoneoutright (preflight 422 + dispatch fail-closed) so nothing reaches a worker unconfined by accident.
2. Let installs through (allowlist egress)
On untrusted-code-write, egress is default-deny. Safe defaults already
allow the common registries (pypi, npm, crates) + GitHub. Add anything else —
e.g. the target’s own git remote or a private registry — with allow_hosts:
"allow_hosts": ["git.acme.com", "artifactory.acme.com"]
The model API is always reachable (via the broker). Cloud metadata and private IP ranges are always blocked, even if a host resolves to them.
3. Choose an isolation backend
backend selects how strong the boundary is. Omit it to route to any
sandbox-capable worker (sandbox.*); set it to require a specific one:
backend | Boundary | Notes |
|---|---|---|
docker | shared-kernel container | fine for trusted hosts |
gvisor | userspace kernel (runsc) | stronger, runs on most hosts |
firecracker | microVM (Kata + Firecracker) | strongest; for untrusted multi-tenant |
A worker that can’t actually enforce the requested backend won’t be offered the job — it’s fail-closed, never a silent downgrade.
Stand up a gVisor worker in one command — deploy/gvisor-worker.sh installs +
registers runsc and runs a worker advertising sandbox.gvisor (with a --check
preflight). See Deploying pond → Isolation.
Run it on a spare Linux box, not your control plane: untrusted code executes in
the runsc-isolated sibling, off your trusted network.
4. Cap resources
overrides tunes resource/image knobs (only — it can never widen the posture):
image— the OCI image the agent runs in (bake your toolchain here).memory,cpus,pids_limit,tmpfs_size— per-run limits.
Run-level budgets (wall-clock, cost ceiling) are operator settings, not stage
config — see Configuration (POND_RUN_*); they
auto-cancel a runaway run.
5. Pick a trust tier
tier decides which pool may run the stage. Untrusted-code profiles default to
trusted. Set byo to run on a bring-your-own host. See
Untrusted & BYO workers.
Minimal vs hardened
// dev: run an agent locally, unconfined
"sandbox": { "profile": "none" }
// production: untrusted code, microVM, installs allowed, capped
"sandbox": { "profile": "untrusted-code-write", "tier": "trusted",
"backend": "firecracker",
"overrides": { "image": "your/agent:latest", "memory": "4g" } }
See also: Worker pool · specs/sandbox-isolation.