The isolation ladder: what each tier stops, and what escapes

Lesson 2 of 5 in Sandboxing and Computer Use: Isolation for What You Cannot Pre-Approve.

“Sandboxed” is not a boolean. It is a ladder, and every rung is a different answer to one question: what has to fail for the code to get out?

Climb it from the bottom. Each step up costs latency, money, and operational complexity, and buys a wider class of failure you no longer have to reason about. Nobody should pay for the top rung on every workload — but everybody should know which rung they are standing on, because the answer is usually “lower than the architecture diagram implies.”

The isolation ladder

  1. Ephemeral microVM or one-shot VM — separate kernel, one session, then destroyed

    The code runs behind a hardware virtualization boundary with its own kernel, and the whole environment is discarded when the session ends. A shared-kernel bug is no longer your problem, and cross-session contamination is structurally impossible rather than carefully avoided. This is the tier managed agent substrates document for their own session isolation, and the right default for agent-written code and browsers on the open web.

  2. Hardened container — non-root, read-only root, dropped capabilities, seccomp, default-deny network

    A container with the defaults reversed: unprivileged user, user namespaces, read-only root filesystem, all capabilities dropped, a syscall filter, cgroup CPU/memory caps, and no network unless allowlisted. This stops the overwhelming majority of real-world sandbox escapes, which are misconfigurations rather than kernel exploits. What it cannot stop is a kernel bug — you still share one with the host — or a mount you handed in yourself.

  3. Default container — namespaces and cgroups, everything else as shipped

    Namespaces hide the host filesystem and process table; cgroups cap resources. Genuinely useful — and routinely overestimated. Shipped defaults often mean root inside the container, a writable root filesystem, full capabilities, host-wide egress, and the cloud metadata endpoint one HTTP request away. A mounted container socket or a bind-mounted host path turns the boundary into decoration.

  4. OS process restrictions — separate low-privilege user, jail, syscall filter, rlimits

    Run the interpreter as a dedicated unprivileged user in a restricted directory, with resource limits and no shell. Cheap, fast, and enough to stop clumsy damage and runaway loops. It does not stop reading anything that user can read, probing localhost and the metadata service, or exhausting shared resources the limits did not cover. Treat it as hygiene on top of a stronger tier, not as the tier.

  5. No isolation — the agent’s own process, on the agent’s own host

    The tier you get by default when someone writes subprocess.run(...) in the tool handler — and the tier most local coding agents run at right now. The generated program inherits the full identity of whatever launched it: cloud role, SSH keys, browser cookies, cached tokens, source trees, .env files. There is no containment story; there is only trust in the model and in every piece of content that reached its context window.

What each rung actually enforces — and what still gets out
TierBoundary enforced byWhat still gets outWhat it costs you

No isolation

Nothing. Convention and the model’s good behaviour.

Everything the launching identity can reach: files, keys, cloud role, internal network, persistence on the host.

Nothing up front. The entire bill arrives at once.

Process restrictions

The OS user model, a syscall filter, resource limits.

Anything readable by that user, localhost services, the cloud metadata endpoint and the wider network, most side channels.

Near zero. A few milliseconds and some packaging discipline.

Default container

Kernel namespaces and cgroups — with shipped defaults.

Shared-kernel exploits, privileged-container escapes, mounted sockets and host paths, credentials injected as env vars, unrestricted egress.

Small: image build and a container runtime you already run.

Hardened container

The same kernel primitives with defaults inverted, plus network policy.

Kernel vulnerabilities, anything you deliberately mounted or allowlisted, noisy-neighbour effects on shared nodes.

Real engineering: profiles to write, breakage to debug, policy to maintain.

Ephemeral microVM / VM

Hardware virtualization — a separate kernel per session — plus destruction at end of session.

Hypervisor-level bugs (rare), and anything you handed in: injected secrets, allowlisted egress, mounted data.

Startup latency and per-session compute. Managed services price this per second.

AWS — AgentCore

Runtime gives you the microVM-per-session substrate above: a 15-minute inactivity timeout and an 8-hour maximum session lifetime are the documented defaults, with a newer Instances substrate for sessions up to 14 days on EC2 capacity providers with persistent volumes that re-attach on resume. Longer lifetime, weaker ephemerality — a deliberate trade, and one you should make consciously.

Code Interpreter is the purpose-built sandbox: agents write and execute code in isolated sandbox environments, with pre-built Python, JavaScript and TypeScript runtimes, session-based file storage, persistent session state for multi-step work, and configurable network modes — an isolated sandbox, or controlled public network access. Inline uploads go to 100 MB, up to 5 GB via Amazon S3 from inside the sandbox, and actions are logged to AWS CloudTrail.

That network-mode switch is the whole lesson in one API parameter: isolation is a setting, and somebody has to choose it.

Azure — Microsoft Foundry

Foundry splits agents into prompt agents (declarative, fully managed) and hosted agents (your container, run by Foundry). Hosted agents run in per-session VM-isolated sandboxes, each with a persistent filesystem ($HOME and /files), which is what enables scale-to-zero with stateful resume: the platform provisions on version create and deprovisions at idle timeout.

Two Foundry specifics matter for containment. Network egress controls are in preview and configured inside the same guardrail policy — and they apply to hosted agents only. And network isolation is an account-creation decision: BYO virtual network needs a dedicated subnet delegated to Microsoft.App/environments (/27 or larger) with private endpoints, and network injection cannot be added or changed after the Foundry account is created.

Foundry also offers a code interpreter as a platform tool, reachable through the Responses API or grouped into a Toolbox behind one managed MCP-compatible endpoint. Treat the isolation properties documented for hosted-agent sandboxes as being about hosted agents — check the current tool docs before assuming they transfer.

Self-hosted

Nobody hands you a rung; you build one. A workable minimum for running agent-written code on your own infrastructure:

  • One session, one container, one lifetime. Create on demand, destroy on completion or timeout, never reuse. A reaper that kills orphans is not optional — non-deterministic agents produce orphans.
  • Non-root, read-only root filesystem, all capabilities dropped, seccomp profile, a writable tmpfs for scratch.
  • Default-deny egress, with an explicit allowlist and the cloud metadata endpoint blocked outright.
  • cgroup CPU, memory, pid and disk caps, plus a wall-clock kill.
  • No secrets in the environment. If the code needs an API call, proxy it from outside the box.

Then be honest in your threat model: you are on a shared kernel. If your workload is multi-tenant, or the content is genuinely hostile rather than merely untrusted, pay for the virtualization boundary.

Interactive checkpoint quiz (2 questions) — open this page in a browser to take it.