Agent sandboxes

Run an agent's own workload in a per-agent sandbox (a gVisor pod on Kubernetes, a Firecracker microVM, or a container) whose only way out is its own egress proxy with a default-deny allow-list.

After reading this page you will be able to:
  • Turn agent sandboxes on for one agent
  • Give a sandbox its egress allow-list
  • Review the manifests a sandbox gets before it runs
  • Read what a sandbox reached and was refused in the audit log

Code mode already runs its child in a jail with no network at all (see Limits and defaults). An agent workload that must reach the network itself, such as a customer's agent container or a skill's script, runs in an agent sandbox instead: its own isolation boundary, and its own egress proxy next to it that it cannot get around.

Agent sandboxes are off by default. While they are off, and for every agent not listed, nothing changes: agents run as they always have, and no extra infrastructure runs. A sandbox exists only while its run does and is deleted when the run ends.

Backends

The platform picks the strongest backend the host has, or the one you name. A backend you name that the host lacks refuses rather than running something weaker.

BackendWhereIsolationHow egress is fenced
k8sA Kubernetes cluster, including k3s on GravitongVisor (runsc) through a RuntimeClass, where the cluster has oneTwo NetworkPolicies: the agent pod reaches only its proxy's port, with no DNS and no metadata service; the proxy reaches DNS and public addresses only
firecrackerA host with /dev/kvm and firecracker-containerdA microVMnftables: the VM subnet reaches only the proxy's port
dockerA host with DockergVisor when the daemon has runsc, else runcAn --internal network whose only other member is the proxy container

Graviton t4g and c7g instances are not metal and expose no /dev/kvm, so on the production k3s cluster the sandbox uses the k8s backend. gVisor runs on arm64.

The proxy is its own pod on Kubernetes, not a sidecar: a sidecar shares the agent's network namespace, and a NetworkPolicy cannot tell two containers of one pod apart.

The egress proxy

Each sandbox gets its own proxy, and it refuses by default:

  • A host that is not on the sandbox's allow-list is refused before any DNS lookup, so a sandbox cannot use name resolution to send data out.
  • An allowed name that resolves to a private, loopback, link-local (the cloud metadata service) or other non-public address is refused, and the connection is pinned to the address that was checked.
  • Only ports 80 and 443 unless an entry names its port (db.example.com:5432).
  • The instance's egress policy still applies on top, under the agent's scope. A sandbox allow-list can narrow what your organization allows, never widen it.

Allow-list entries are host names or *.suffix wildcards. A bare * is refused.

Turn it on

  1. On Kubernetes, apply deploy/k3s/sandbox/agent-sandbox.yaml once (a namespace, a default-deny policy and the rights to create and delete sandbox pods). For gVisor, install runsc on the nodes and apply runtimeclass-gvisor.yaml.
  2. Review the plan for an agent:
    curl -X POST "$BASE/api/admin/platform/agent-sandbox/plan" -H "Authorization: Bearer $TOKEN" \
      -d '{"agent": "helper", "command": ["python", "job.py"]}'
  3. Turn it on and give the agent its allow-list. Adding a host loosens the sandbox, so it needs governance.weaken and a reason:
    curl -X PUT "$BASE/api/admin/platform/agent-sandbox" -H "Authorization: Bearer $TOKEN" \
      -d '{"enabled": true, "agents": {"helper": {"allow": ["api.vendor.com"]}}, "reason": "CHG-1042"}'
  4. Run a workload: POST /api/admin/platform/agent-sandbox/run with agent, command and optional env. The response has the exit status, the output's tail and every egress decision.

Set require_runtime_class: true to refuse to run on a cluster without the gVisor RuntimeClass, instead of falling back to the node's runtime behind the same policies.

Audit

Every launch, refusal, failure and teardown writes an agent_sandbox.* row. Every egress refusal writes agent_sandbox.egress.deny, and the first connection to each allowed host writes agent_sandbox.egress.allow. A configuration change writes agent_sandbox.config.update with before and after, marked as a weakening when it loosens anything.