Isolation Classes
Every headless agent declares an isolation class in its agent.yaml. The class is the security boundary the workload runs behind. On Kubernetes it maps to a runtimeClassName — isolation is a tier on the pod, not a separate backend (ADR 0009).
The decision tree
Pick the least-privileged class the workload can tolerate. Walk it top to bottom and stop at the first match:
Is the code first-party and signed? ── yes ─▶ TRUSTED (runc)
│ no
Does it load packages from the internet,
or drive a browser, or run user/LLM-generated code? ── yes ─▶ UNTRUSTED (gVisor + egress-deny)
│ no
Is the *input* adversarial / hostile? ── yes ─▶ HOSTILE (Kata microVM)
│ no
────────▶ STANDARD (gVisor, default)The classes
| Class | RuntimeClass | Use when |
|---|---|---|
trusted | runc | Signed first-party code only. Runs on shared nodes. No untrusted package loading, no LLM-generated code path. |
standard | gvisor | Default. Your own code on shared, gVisor-isolated nodes. Used when the class is left unset. |
untrusted | gvisor + egress-deny | Loads internet packages (PyPI/npm), drives a browser, or runs LLM-generated code. Adds an egress allowlist and seccomp deny-default. |
hostile | kata-qemu (or kata-fc) | Adversarial workloads. Full-kernel microVM via Kubernetes on a dedicated node pool with no co-tenancy. |
wasm | crun+wasm | WebAssembly workloads (or in-process Wasmtime on trusted hosts). |
devcontainer | gvisor | Long-lived workspace: a persistent pod + PVC that survives across calls. |
Defaults & forced classes
- An unset class resolves to
standard. - Marketplace bundles and LLM-generated bundles are forced to
untrusted— they cannot opt down. trustedrequires the signing key; a bundle without it cannot claimtrusted.
The fail-closed gate
This is the load-bearing invariant. Untrusted and hostile code never runs in a bare pod:
- A node may satisfy
untrustedonly if it advertises thegvisorRuntimeClass. - A node may satisfy
hostileonly if it advertiseskata-qemu/kata-fc. - A node that lacks the required hardened RuntimeClass fails closed — the run is refused. It is never downgraded to
runc.
The requirement is enforced in two places: the manager (which builds the pod spec) and the scheduler's node-capability filter (which only places onto nodes advertising the needed RuntimeClass).
gvisor / kata RuntimeClasses, untrusted and hostile workloads are refused — correct, but operationally visible. The data-plane installer provisions them and a preflight check surfaces the gap before traffic.gvisor intercepts syscalls in user space — a minority of workloads hit an unimplemented syscall. Declare hostile (full kernel via Kata) for those, or get a runc-on-trusted exception via the admin override.In a spec
Demo 02 (web-scraper) declares untrustedbecause it pulls third-party packages, and pairs it with an egress allowlist:
spec:
isolation: untrusted
network: allowlist_domain
egress_rules:
- pattern: "*.wikipedia.org"
http_methods: ["GET"]
rate_bps: 1048576 # 1 MiB/sEgress enforcement and secret vending for untrusted workloads are covered in Identity & secrets.