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.

ProfileFilesystemNetworkUse when
nonehost, openopenlocal dev only — never for untrusted code
untrusted-code-readread-only checkoutmodel API only (broker)the agent only needs to read + call the model
untrusted-code-writethrowaway writable copydefault-deny allowlistthe 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.

none is 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. A none stage always raises an unconfined_sandbox preflight warning, and a deployment that runs untrusted code should set POND_REQUIRE_SANDBOX=true, which refuses none outright (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:

backendBoundaryNotes
dockershared-kernel containerfine for trusted hosts
gvisoruserspace kernel (runsc)stronger, runs on most hosts
firecrackermicroVM (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 commanddeploy/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.