Sandbox API

Long-lived sandboxes

Create one isolated workspace, run many commands, checkpoint it, and release it safely.

Use a long-lived sandbox when one workflow needs the same filesystem across many commands. Create the box once instead of paying pod creation and setup on every turn.

NeedUse
Interactive agent/dev loopLong-lived sandbox
Re-run tests in the same workspaceLong-lived sandbox verify
One short snippetCodeEdit
Benchmark task, verifier, pass@kHarbor datapoint
Free-form one-shot agent instructionAgent Runtime

Not for pass@k

Pass@k attempts must be independent. Do not attach multiple attempts to the same sandbox or carry files, conversation state, or answers between them.

Lifecycle

The service runs one gVisor pod plus a PVC in the sandbox-jobs namespace. Kubernetes objects are runtime truth; GCS checkpoints hold durable workspace bytes. Redis is a lookup and quota store, not the durable workspace.

Create

curl -sS -X POST \
  -H "X-Api-Key: $SANDBOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "profile": "sandboxes-default",
    "autostop_sec": 1800,
    "ttl_sec": 14400
  }' \
  "$SANDBOX_GATEWAY_URL/sandbox/sandboxes/v1"

The response contains sandbox_id. Keep it for every later call. A create or start consumes one L2 concurrency slot for the authenticated tenant:project; when the project is full, the API returns 429 and Retry-After.

You may bootstrap from an HTTPS archive, a permitted GCS prefix, or a prior snapshot:

{
  "profile": "sandboxes-default",
  "source": {
    "archive_url": "https://storage.example/task.zip"
  }
}

The source is fetched once. Later exec calls use the PVC; they do not download the archive again.

Exec in the same workspace

curl -sS -X POST \
  -H "X-Api-Key: $SANDBOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"command":["bash","-lc","python -m pytest -q"],"timeout_sec":300}' \
  "$SANDBOX_GATEWAY_URL/sandbox/sandboxes/v1/$SANDBOX_ID/exec"

Each exec uses the same pod and filesystem until the sandbox is stopped, destroyed, expires, or is reaped for idleness.

Continue with an agent

For an ungraded interactive loop, a native Sandbox agent can attach to the running session:

{
  "agent_id": "opencode",
  "model": "wandb/moonshotai/Kimi-K2.5",
  "instruction": "Continue from the current files and fix the next failure",
  "sandbox_id": "your-sandbox-id"
}

Submit that body to POST /sandbox/agent/v1/runs with the same tenant/project credential. The agent uploads its instruction/config into the existing workspace, runs there, and leaves the sandbox alive.

The session image must already contain the selected agent CLI. An attached run cannot rebuild a running sandbox image. Only catalog agents with integration: sandbox support this path.

sandbox_id with pass_at_k > 1 is rejected. Use Harbor for independent, graded attempts.

Stop, start, and destroy

# Release compute and checkpoint the workspace.
curl -sS -X POST -H "X-Api-Key: $SANDBOX_API_KEY" \
  "$SANDBOX_GATEWAY_URL/sandbox/sandboxes/v1/$SANDBOX_ID/stop"

# Restore the workspace and acquire quota again.
curl -sS -X POST -H "X-Api-Key: $SANDBOX_API_KEY" \
  "$SANDBOX_GATEWAY_URL/sandbox/sandboxes/v1/$SANDBOX_ID/start"

# Remove the runtime; the service checkpoints before deletion.
curl -sS -X DELETE -H "X-Api-Key: $SANDBOX_API_KEY" \
  "$SANDBOX_GATEWAY_URL/sandbox/sandboxes/v1/$SANDBOX_ID"

Default auto-stop is 30 minutes on dev. Always destroy a sandbox you no longer need; idle sessions hold storage and, while running, one project concurrency slot.

Warm sessions vs warm nodes

These solve different waits:

PoolHoldsBenefit
Session warm poolReady pod + PVC for a profileFaster create/claim
Node warm placeholdersEmpty gVisor node capacityAvoids node scale-up

Neither makes model reasoning faster. They reduce time before the agent can start work.

Tenancy

Ownership comes from the authenticated principal, not a caller-supplied tenant header. List/get/exec/files/stop/start/destroy only expose sandboxes in the same tenant:project. Platform callers acting for a project must carry that scope on every request.

See Caching and reuse for the boundary between workspace reuse and independent evaluation.

Operations (cluster)

Attached agent runs on the harbor-worker call the sandboxes API through the in-cluster gateway (SANDBOX_GATEWAY_URL=http://sandbox-gateway:8780) using the worker’s SANDBOX_API_KEY from sandbox-runtime, with per-job X-Sandbox-Tenant-Id / X-Sandbox-Project-Id headers. After deploy, confirm:

kubectl -n sandbox exec deploy/sandbox-harbor-worker -- printenv SANDBOX_GATEWAY_URL