Sandboxing Ai Agents On Linux

Sandboxing untrusted AI agents on Linux

An AI coding agent runs code before any human has reviewed it: shell commands, generated scripts, pip and npm installs, tool plugins. It can be steered by prompt injection hidden in a web page or a dependency, and even a well-meaning agent can rm -rf the wrong tree. AI review agents exist, but they mostly read the diff after the code has already run, and they are LLMs that the same prompt injection can fool, so they complement a sandbox rather than replace it. So what does a sandbox for that code actually have to do on Linux, and why is “minimal” so hard?

TL;DR

A Linux sandbox is a stack of partial mechanisms that all share one host kernel. Each layer stops one class of thing and nothing else, and there is no single switch. The strongest boundary is a second kernel, which means a virtual machine. That is why the serious hosted products are microVMs, and why the OS-level tools (Landlock, seccomp, bubblewrap) are best kept for code you mostly trust on a machine you control.

The mechanisms involved, in the order a launcher typically applies them. They are not ranked: each answers a different question.

Layer Question it answers What it does not stop
cgroup v2 how much? anything within quota; not a security boundary
user namespace who am I? kernel bugs reachable by unprivileged users
pid/net/ipc/uts namespaces what can I see? the shared kernel behind every view
mount namespace + pivot_root what filesystem? files under the roots you granted
capabilities + no_new_privs can I re-privilege? syscalls that need no privilege
Landlock which paths, TCP/UDP ports, unix sockets, signals? anything outside the rights it handles; most syscalls
seccomp-bpf which syscalls? anything an allowed syscall can reach

Everything here is point-in-time (October 2026). Details of products and papers move monthly, so verify specifics before relying on them.


1. The current challenges

One shared kernel is the whole problem. Namespaces, cgroups, capabilities, seccomp and Landlock all run in the host kernel. A single kernel vulnerability reachable from the sandbox is a full escape. The Linux syscall surface is enormous (hundreds of syscalls, each with many argument shapes), and features like io_uring add surface. Enabling unprivileged user namespaces, which you need to sandbox without root, itself exposes more kernel code to untrusted callers and has been a recurring source of local privilege-escalation CVEs.

Agents need capability, which fights confinement. A file-only agent is easy to box. A useful coding agent wants to install packages (network), spawn compilers and test runners (exec and many syscalls), read large parts of the filesystem, and sometimes use GPUs. Every capability you grant is attack surface you re-open. The art is least privilege per task, which is exactly what is hard to specify up front.

Network egress is the real prize, and the hardest to get right. The damaging outcomes are network actions: exfiltrating secrets, reaching internal services, hitting the cloud metadata endpoint (169.254.169.254), pulling a malicious payload. A binary on/off switch (blocking AF_INET sockets in seccomp) is too coarse for an agent that must reach a package registry. A filtering proxy is the right tool, but it needs TLS interception or a domain allow-list, plus care that the agent can’t just talk to localhost services or use DNS as a covert channel.

Secrets and ambient authority leak in surprising ways. Environment variables, inherited file descriptors, cloud instance-metadata credentials, ~/.aws, ~/.ssh, a mounted Docker socket, a GITHUB_TOKEN in the environment: none of these are “files the agent opened”, so a filesystem sandbox alone misses them. A 2026 security note on AWS Bedrock AgentCore argued that its code-interpreter microVM still exposes execution-role credentials via the metadata service; AWS considers that by design. Whoever is right, the lesson stands: isolation of the compute does not imply isolation of the credentials.

Views leak the host. Inside a container, /proc/cpuinfo, /proc/meminfo and /proc/stat show the host’s numbers unless something virtualises them (lxcfs). That is the same reason top in a container shows host CPU and steal time. Information disclosure is a weaker threat than escape, but it feeds fingerprinting and side channels.

Correctness is a sequencing minefield. The layers have prerequisite chains and failure modes. no_new_privs must be set before an unprivileged seccomp filter. A gid map needs setgroups=deny. A bind mount can’t go read-only in one step on older kernels. Landlock rights must be masked to the running ABI or restrict_self fails. Seccomp goes in last, so your own setup syscalls aren’t filtered. Each is a place to silently under-confine, and a half-applied sandbox is worse than none, because you think you’re protected. A good sandbox aborts on any layer that fails rather than running the agent half-confined.

Deny-lists have traps. A syscall deny-list is deceptively easy to under-build:

  • io_uring. It can perform socket, connect and openat operations as ring operations, without issuing those syscalls, so a naive seccomp network block is sidestepped by an agent that opens a ring and connects through it. Any deny-list that ignores io_uring is porous.
  • The x32 ABI. On x86-64, x32 syscalls carry the same AUDIT_ARCH_X86_64 token but OR 0x40000000 into the number. A filter that only compares plain numbers lets the x32 variant of every denied call through.
  • ioctl(TIOCSTI). A process sharing the controlling terminal can push bytes into the terminal’s input queue, and the parent shell runs them after the sandbox exits. That is a full escape that touches no file and opens no socket. Since Linux 6.2 the host can switch it off with sysctl dev.tty.legacy_tiocsti=0, and several distributions ship it off, but a sandbox should still call setsid() and filter the ioctl itself.
  • Arch confusion. A 32-bit (i386) calling convention uses different numbers for different calls, so a filter must check the architecture first.

Each hole is invisible if you reason about syscalls by name. This is why serious sandboxes invert to deny-by-default and lean on audited, maintained profiles instead of a hand-rolled list.

Portability and kernel-version drift. Landlock needs Linux 5.13 or newer and grows new abilities per ABI: TCP bind and connect by port (ABI 4, Linux 6.7), device ioctls (ABI 5), abstract-unix-socket and signal scoping (ABI 6, Linux 6.12), audit logging (ABI 7), pathname unix sockets (ABI 9) and UDP (ABI 10). A sandbox written for ABI 10 quietly gets less on a 6.6 LTS kernel unless it checks. Seccomp syscall numbers are per-architecture. Unprivileged user namespaces are disabled on some hardened distributions, and from Ubuntu 24.04 AppArmor restricts them (kernel.apparmor_restrict_unprivileged_userns=1), which breaks bubblewrap unless a profile grants it userns. A sandbox that works on your machine may fail open elsewhere.

Performance and density. Hosted agent platforms run many sandboxes and want sub-second cold starts and snapshot/restore. OS-level sandboxes are cheap (microseconds to milliseconds) but weak. VMs are strong but were historically slow to boot, which is the gap Firecracker plus snapshotting closed, and why microVMs became viable for this at all.


2. Where the future is moving

  • MicroVMs are becoming the default for untrusted code, not the exception. Firecracker-style microVMs give each sandbox its own kernel at roughly 100 to 150 ms cold start, and snapshot/restore (lazy page-fault-driven restore via userfaultfd) makes per-task or per-step forking cheap. The hosted market (E2B, Vercel Sandbox, AWS AgentCore) has largely standardised on this.

  • A tiered “right-sized isolation” model is winning over one-size-fits-all. Guidance is converging across 2026 write-ups: OS-level confinement (Landlock, seccomp, bubblewrap) for trusted code on trusted machines; gVisor as a middle tier; microVMs and Kata for untrusted, multi-tenant code. The interesting engineering is in choosing and composing tiers per workload, not in one perfect sandbox.

  • Landlock is maturing into a real agent primitive. It began filesystem-only (Linux 5.13). ABI 4 (Linux 6.7) added TCP bind and connect rules, ABI 6 (Linux 6.12) added abstract-unix-socket and signal scoping, and ABIs 9 and 10 added pathname unix sockets and UDP. That turns Landlock from “restrict files” into “restrict a process tree’s whole resource reach”, unprivileged and self-applied, which is ideal for a CLI agent that sandboxes itself.

  • seccomp-notify supervisors put user-space policy in the loop. Instead of a static allow/deny BPF program, the kernel can forward a syscall to a supervisor process that decides at runtime and can even virtualise the effect. The 2026 research prototype Sandlock (Wang and Zheng, arXiv:2605.26298) combines Landlock and seccomp with a seccomp-notification supervisor to get dynamic network decisions, HTTP-level access control, and TOCTOU-safe inspection of execve arguments, all unprivileged, with about 5 ms startup overhead. “Static policy compiled into the kernel, plus a narrow supervisor for the hard cases” is likely where lightweight agent sandboxes head.

  • Orchestration is standardising. The Kubernetes SIG’s agent-sandbox (a Sandbox CRD with warm pools, delegating isolation to gVisor or Kata via RuntimeClass) signals that “a sandbox” is becoming a first-class, schedulable object with stable identity, persistent storage and a sub-second claim from a pre-warmed pool, rather than an ad-hoc docker run.

  • Snapshotting is a product feature, not just a speed trick. Pause/resume, fork-a-running-state, and “close your laptop, the agent keeps going” (AWS AgentCore) are becoming table stakes. That pushes toward VM-level isolation, because snapshotting a whole guest is cleaner than checkpointing a process tree (CRIU is fragile).

  • Egress control moves up the stack. The trend is away from “block all sockets” toward identity-aware filtering proxies with per-domain allow-lists and audit. Agents must reach some network, and the useful policy is about where, not whether.


3. Contenders vs the status quo

The status quo for running arbitrary code has been OCI containers (Docker and runc): namespaces, cgroups, a default seccomp profile and dropped capabilities. They have good ergonomics and a huge ecosystem, but they share a kernel, so they are insufficient for genuinely untrusted, multi-tenant code. The contenders differ mainly in where the trust boundary sits.

Approach Boundary Isolation strength Start Cost / density Best fit for agents
chroot path resolution only ~none (escapable) instant free not a sandbox
bwrap / nsjail / Landlock launchers namespaces, shared kernel low to moderate ~ms very high trusted code; per-call confinement
OCI container (runc) namespaces + seccomp, shared kernel moderate ~100 ms very high semi-trusted; the status quo
gVisor user-space kernel intercepts syscalls high (small host surface) ~100 to 300 ms high untrusted code without a VM; some compatibility cost
Kata / Firecracker / Cloud Hypervisor separate guest kernel (KVM) highest (on Linux) ~100 to 150 ms, faster from snapshot moderate untrusted, multi-tenant: the serious default
WASM runtime capability-based VM, no syscalls high for what it supports ~ms very high narrow, pure-compute tools

Moving down the table trades density and compatibility for isolation strength. Containers sit in the middle and are fine for code you mostly trust. The direction of travel for untrusted agent code is toward gVisor and especially microVMs, with OS-level tools relegated to the innermost layer or to trusted single-user setups.

A few nuances the table flattens:

  • gVisor vs microVM isn’t strictly “weaker vs stronger”. gVisor shrinks the host syscall surface to a few dozen but adds its own (smaller) kernel’s surface and some workload incompatibility. A microVM gives a full, familiar kernel per guest, but a larger in-guest surface, and the KVM/virtio boundary is the thing to trust.
  • WASM is the strongest per-feature boundary (no ambient syscalls at all), but it can only run what has been compiled to it and wired to explicit host capabilities. It is great for a sandboxed function, not for “run this arbitrary repo’s test suite”.
  • Composition beats any single row. Real systems stack them: OS-level confinement inside a container inside a microVM, with a filtering proxy on egress. Each layer catches what the one inside it missed.

4. How leading systems sandbox untrusted AI agents

Two families, by where they run.

Local coding-agent CLIs (the OS-level tier)

  • OpenAI Codex CLI. On Linux it uses Landlock and seccomp: Landlock restricts the filesystem to user-approved directories, and seccomp blocks outbound network by default. A newer path vendoring bubblewrap sits behind a feature flag. On macOS it uses Seatbelt (sandbox-exec). Its docs warn that inside Docker the needed APIs may be unavailable and sandboxing can fail, which is the portability problem from section 1 made concrete.
  • Claude Code. Anthropic’s sandbox-runtime uses bubblewrap on Linux for filesystem isolation and an empty network namespace. The only way out is an HTTP proxy (HTTP and HTTPS) and a SOCKS5 proxy (other TCP) on the host, reached through unix sockets bind-mounted into the sandbox and bridged by socat, which gives domain-level egress control. A seccomp filter blocks other unix sockets on x86-64 and arm64; without it, unix sockets stay open with a warning. On macOS it uses sandbox-exec. By default it warns and runs unsandboxed if dependencies are missing, with a failIfUnavailable hard-fail mode for managed deployments. That is the “fail open vs fail closed” decision made visible.
  • Gemini CLI. The sandbox is opt-in (off by default) and pluggable: macOS Seatbelt, or Docker, Podman, gVisor or LXC containers. It mainly limits the filesystem to the project directory.

These share a fundamental limit that several guides state: bubblewrap and Seatbelt run on the same kernel as the host, so they offer little against kernel exploits. They are suitable mainly for trusted code on trusted machines, the right tool for a developer running an agent on their own laptop and not for a multi-tenant service executing strangers’ code.

Hosted execution platforms (VM or user-space-kernel tier)

  • E2B. Each sandbox is a Firecracker microVM with its own kernel. The core is Apache-2.0 and self-hostable, with roughly 150 ms cold start from snapshot, and snapshot-based pause/resume is central. It is widely treated as the reference open option.
  • Vercel Sandbox. Firecracker microVMs, with snapshots and time-limited sessions.
  • Modal. Sandboxes default to gVisor, with a VM runtime (own kernel) added for stronger isolation, and filesystem-snapshot persistence. A standout for GPU inside the sandbox.
  • AWS Bedrock AgentCore Code Interpreter. A per-session isolated Linux microVM with a persistent workspace and “keep running while your laptop is closed” semantics (see the credential-exposure critique in section 1).
  • Cloudflare Sandbox SDK. Built on Cloudflare Containers, one sandbox per VM. Its docs recommend per-user sandboxes because everything in one sandbox shares resources. Third parties dispute the exact mechanism, and it is pre-1.0.
  • Daytona. Fast-create persistent workspaces with snapshots and forks. Default isolation is reported as containers on the host kernel, with optional Kata or Sysbox for stronger isolation. Licensing and status were in flux in 2026, so verify before relying on it.
  • Kubernetes agent-sandbox. Not an isolator itself: a Sandbox CRD plus controller that schedules pods onto gVisor or Kata via RuntimeClass, with warm pools for sub-second starts. It is the orchestration story for self-hosting agent sandboxes at scale.

The pattern across all of them

  1. Tiered by trust. Local CLIs accept the shared-kernel tier because the code is semi-trusted and the machine is the user’s own. Hosted platforms running arbitrary users’ code almost universally reach for a second kernel (Firecracker) or a user-space kernel (gVisor).
  2. Egress is a filtering proxy, not a toggle. The serious systems control which domains, with audit, because agents need package registries and APIs.
  3. Snapshot/restore is a first-class feature, which itself pushes toward VM isolation.
  4. Least privilege per task, increasingly expressed declaratively (Landlock rulesets, Kubernetes CRDs, per-call policy) rather than one static container configuration.

5. Why Firecracker rather than QEMU, and what about GPUs?

Could plain QEMU do the job of Firecracker?

Yes. Both put the guest behind hardware virtualisation with its own kernel, so the kind of boundary is the same. The difference is what it costs to get it:

  • Tiny device model. QEMU emulates a very wide range of hardware (legacy devices, USB, many NICs and disk controllers, graphics). Firecracker exposes a handful of virtio devices, a serial port and a minimal keyboard controller. Less emulation code reachable from the guest means less attack surface, and many past QEMU escapes came from obscure emulated devices.
  • Boot time and footprint. About 100 to 150 ms and a small per-VM memory overhead, against a slower start for a stock QEMU machine type with firmware. Agent workloads create thousands of short-lived sandboxes, so this matters.
  • Density. Low overhead per VM lets a host pack far more sandboxes.
  • Snapshots. Snapshot/restore is a core feature, and it is what “resume a warm sandbox” products build on.
  • Built for multi-tenancy. Written by AWS for Lambda and Fargate in Rust, and run under a jailer (seccomp, namespaces, cgroups).

QEMU is the better choice when you need hardware Firecracker lacks (GPU passthrough, USB, other CPU architectures, Windows or macOS guests) or you want the most mature, widely supported tool. QEMU’s microvm machine type with no firmware and a stripped config gets most of Firecracker’s benefits, and Cloud Hypervisor and crosvm are other minimal VMMs. Firecracker is not the only way; it is the one managed platforms standardised on.

Firecracker and GPUs

Stock Firecracker cannot give a guest a GPU. Its minimal device model has no PCI bus, which rules out PCIe passthrough, and that is a consequence of the design, not an oversight. (Firecracker’s PCI story has been changing; check the current release notes.) For GPU workloads, sources point to QEMU or Cloud Hypervisor instead. The options in use:

Model Boundary Catch
VFIO passthrough into a VM (QEMU, Cloud Hypervisor, Kata) Hypervisor, with exclusive device ownership One GPU per tenant. Naive passthrough can re-open the boundary through GPU DMA, so it needs an IOMMU and care.
gVisor + nvproxy Filtered NVIDIA ioctls forwarded to the host driver Weaker than a VM: the boundary is the driver’s ioctl surface. Supports only selected driver versions and CUDA commands.
NVIDIA MIG Hardware-level partitions of one card Supported data-centre GPUs only (for example A100, H100, H200, B200). Each partition can go to a different VM.
Time-slicing / vGPU Little (time-slicing); licensed vendor driver (vGPU) Time-slicing suits only low-sensitivity workloads.
Plain container + NVIDIA Container Toolkit Shared host kernel and driver Fine for trusted code, poor for untrusted.
No GPU in the sandbox n/a Run agent glue code in a microVM and call inference over an API. Often enough.

Why it is hard: GPU drivers are large and complex, and passing a device through means the guest drives real hardware over DMA. That widens exactly the surface a microVM was chosen to shrink, and it makes cheap snapshot and migration much harder. The shared-kernel tier has the mirror-image property: a GPU is just /dev/nvidia* files you allow through Landlock, which is simple but leaves the driver reachable from the sandboxed process.

What vendors sell for GPU isolation

Either a whole VM or bare-metal instance with a GPU attached (the EC2 model: one customer per GPU, isolation is the hypervisor’s), or a managed GPU sandbox:

  • Modal. gVisor with the proxied NVIDIA driver (not passthrough); a wide GPU range from T4 up to H100, H200 and B200; one GPU reserved per sandbox; not self-hostable.
  • Daytona. Reportedly VFIO passthrough, one dedicated GPU per ephemeral sandbox VM. Sources conflict on availability, so check its docs.
  • Northflank. gVisor where nested virtualisation is unavailable; L4 through H200.
  • Fly.io. L40S machines (single secondary source).
  • E2B. CPU-only per the sources.

The leading edge

  • Confidential-computing GPUs. H100 and Blackwell can attest the device and run in a confidential mode. On H100 the on-package HBM is not encrypted, because NVIDIA treats the package as the trust boundary. What is encrypted is CPU-to-GPU traffic, through encrypted bounce buffers over PCIe,1 and that is where most of the overhead comes from. Sources disagree on how much more Blackwell encrypts (HBM, NVLink), so check NVIDIA’s own documentation. NVIDIA’s reference architecture runs Kata confidential VMs on AMD SEV-SNP or Intel TDX with VFIO passthrough. This protects tenants from the host operator, not just from each other. The cost is performance; recent work on Blackwell confidential computing is about recovering LLM-serving throughput lost to it.
  • A Type-1 hypervisor per workload (Edera, per a September 2026 comparison): VM-grade isolation while still sharing the card, instead of dedicating a whole GPU to each tenant.
  • Per-sandbox GPUs on cheap isolation. The commercial push is ephemeral sandboxes that each get a GPU, but only Modal’s whole-card approach is consistently documented.

For a GPU SaaS with mutually untrusted tenants: dedicated-GPU VMs with VFIO (Kata or Cloud Hypervisor), plus confidential computing if tenants distrust the operator; MIG where a card must be subdivided; gVisor with nvproxy as the cheaper middle option whose boundary is the ioctl surface.


6. Research and proposals for the next 5 to 10 years

Papers (arXiv, 2026 unless noted)

  • Tyche, composable isolation (arXiv:2507.12364). A hardware and monitor primitive that unifies enclaves, VMs and containers, so a tenant can carve out its own isolated domain without trusting the layer below. The most direct research answer to “how do you compose isolation layers”.
  • Threat model, taxonomy and measurement. AI Sandboxes (arXiv:2606.18532) and AI Code Sandboxes: A Comparative Security Study (arXiv:2606.08433) cover attack surface, leakage, CVE history and patch cadence. They give shared vocabulary and numbers instead of vendor claims.
  • SandboxEscapeBench (arXiv:2603.02277). Measures whether frontier LLMs can escape Docker/OCI sandboxes. It makes the attacker side measurable, which is likely to drive the field: a sandbox claim can now be tested against a model.
  • Learned capability governance (arXiv:2604.11839). Learns the smallest tool set each task type needs, motivated by a reported 15x overprovisioning ratio. The paper’s naming differs between versions, so read it with care.
  • Mandatory access control for LLM agents (arXiv:2601.11893). A MAC framework against privilege escalation between agent components.
  • Shrinking the shared kernel from inside. Dynamic sandboxing for unprivileged eBPF (arXiv:2308.01983) and compartmentalizing monolithic kernels (arXiv:2404.08716), relevant because the shared kernel is the weak point of every tier except VMs.

Kernel proposals and standards

  • Landlock extensions. Much of this has now landed: abstract-socket and signal scoping in ABI 6, pathname unix sockets in ABI 9 and UDP in ABI 10 (see the kernel docs, LWN and kernsec links below). Landlock’s default is ambient authority, so every workload still needs an explicit policy.
  • eBPF LSM enforcement and per-sandbox network policy, for example per-sandbox TAP devices with eBPF rules. Reported as becoming the standard pattern for agent network isolation, though the sources are vendor guides, not papers.
  • WASI and the Component Model. A component starts with no ambient authority and can only do what the host grants. Best for tools and functions you can compile, not yet a full dev environment. GPU access (wasi:webgpu) is a Phase 2 proposal.

Other research directions

  • CHERI hardware capabilities (Cambridge and ARM Morello) and seL4, the verified kernel: capability enforcement in hardware or by proof, instead of by a large trusted kernel.
  • Confidential VMs (AMD SEV-SNP, Intel TDX) and confidential GPUs, covered above.
  • In-process isolation with Intel MPK and ARM MTE.
  • Memory-safe VMMs and Rust in the kernel, which shrink the bug surface beneath every layer.

Where this seems to be heading (opinion)

  1. The shared kernel is the weak point. The long-term answers either avoid it (microVMs, confidential VMs) or shrink it (Landlock-style scoping, compartmentalization, memory safety).
  2. Policy will matter as much as mechanism. Static allow-lists overprovision, so least privilege per task, possibly learned, is where the agent-specific papers are going.
  3. Composable isolation looks like the most promising architecture. Tyche-style designs would let a tenant nest its own boundary instead of trusting the provider’s.

Gaps: I found no paper proposing microVM snapshots specifically for agent isolation, and none on seL4- or CHERI-based agent sandboxes.


7. Why isn’t OpenBSD the answer?

OpenBSD is one of the most security-focused commodity OSes, so it is a fair question. It is a strong answer for the OS-level tier, but it does not change the tiering in section 3.

Two framing points. The tiers are about where the trust boundary sits, not only about CPU-level isolation. And OpenBSD is also a shared-kernel design, so a kernel bug still breaks every sandbox on the machine.

What OpenBSD does well

  • pledge(2) and unveil(2) are arguably better designed than seccomp and Landlock. Coarse promises such as stdio rpath inet are hard to get wrong, where seccomp filters are easy to write badly. unveil limits which parts of the filesystem a process can see.
  • Privilege separation is a core convention in its daemons.
  • Exploit mitigations on by default (W^X, ASLR, stack protector, removal of old code), a small audited codebase, and a good security track record.

Why it is not enough for this problem

  1. The shared-kernel limit applies to it too. A smaller, cleaner kernel makes an exploit less likely, not impossible. For mutually untrusted tenants, hosted platforms want a second kernel, which OpenBSD alone does not give you.
  2. Only half of it works from outside. A wrapper can confine a program that never calls pledge: the second argument, execpromises, sets the promises the next execve starts with. But unveil does not survive execve (“the new program starts running without any unveils”), so there is no external way to limit which files an arbitrary binary, such as an agent’s pip install and shell, can see. On Linux, bubblewrap and Landlock launchers do both from outside.
  3. Missing building blocks. It has rlimits and login.conf, but nothing like cgroups v2 for memory, PIDs and CPU, and no unprivileged user-namespace-style containers. It has no seccomp user-notify equivalent for a supervisor that vets syscalls. Egress control is pf, which is good but needs root and is not per-process or per-domain.
  4. Ecosystem and hardware. Agents, toolchains, Python wheels, Node and Docker images assume Linux, and CUDA does not run on OpenBSD, so GPU workloads are out. Its hypervisor, vmm(4)/vmd, is much less mature than KVM, and the microVM tooling (Firecracker, gVisor, Kata) and snapshot/restore are all Linux and KVM.
  5. Operations. Cloud vendors do not offer it the way they offer Linux, so there are few managed options.

Where it fits

OpenBSD works well as the host or guest for a small, auditable control plane or an egress proxy. For the part that runs untrusted agent code, the tooling is on Linux. For one trusted-ish agent on your own machine, pledge and unveil are a good fit and arguably simpler than the Linux stack; they just do not scale to the multi-tenant case.


8. What the stack above still misses

Sections 1 to 7 are about confining the process. Checked against a real agent deployment, they leave gaps that no layer in the table closes, because the damage happens outside the box or through a channel the box is meant to allow.

Persistence after the sandbox exits. The agent may write only inside the project, but the project is full of things that later run unsandboxed: .git/hooks/*, .git/config (core.fsmonitor, core.sshCommand), .envrc for direnv, .vscode/tasks.json and settings.json, the Makefile, package.json scripts, and .bashrc if $HOME is writable. The confinement is perfect, and the payload runs the next time you type git commit or open the editor. Deny writes to these paths (Landlock rules or read-only bind mounts over them) and review the diff before running anything outside the sandbox.

Exfiltration through allowed channels. A domain allow-list that contains github.com also allows pushing your code to an attacker’s repo or a gist, and pypi.org allows uploading a package. Policy has to be per destination identity (this org, this repo, read-only methods), not per domain.

The model is an exfiltration channel the sandbox cannot see. Data the agent reads goes into the model’s context, and a prompt-injected agent can leak it in its output (for example through a rendered Markdown image URL) without a single syscall from inside the sandbox. Simon Willison’s lethal trifecta is the frame: private data, untrusted content and a way to communicate out. Remove one leg, because no sandbox removes it for you.

Credential brokering instead of credential stripping. Stripping secrets is the minimum. The stronger pattern is that secrets never enter the sandbox: the egress proxy injects a short-lived, narrowly scoped token on the way out, so a compromised agent can only use the credential for what the proxy allows, and cannot copy it.

Host-side session sockets. D-Bus, ssh-agent, gpg-agent, X11 and Wayland, PulseAudio, and the Docker or Podman socket. X11 alone allows keystroke injection into other windows. A fresh network namespace hides abstract unix sockets, because they are per network namespace, but any pathname socket you bind-mount in is a door. Landlock ABI 9 can gate those.

Host kernel knobs. Every tier except the VM shares the kernel, so turn off what agents do not need: kernel.unprivileged_bpf_disabled=1, kernel.io_uring_disabled=2, vm.unprivileged_userfaultfd=0, dev.tty.legacy_tiocsti=0, a stricter kernel.yama.ptrace_scope, and kernel.perf_event_paranoid=3 where the distribution supports it. These are cheaper than any seccomp filter, and they cover processes you forgot to wrap.

Resource exhaustion beyond memory and PIDs. Disk fill (a tmpfs with size=, or project quotas), inodes, I/O bandwidth (io.max), CPU (cpu.max) and wall-clock time. An agent stuck in a loop is the common case, not the exotic one.

Lifecycle and reuse. Sandboxes should be ephemeral and never reused across tenants or tasks, and a warm pool must hand out clean snapshots. Reusing a sandbox turns one task’s prompt injection into the next task’s compromise. Cloned snapshots also share RNG state and ASLR layout unless the guest reseeds (VM generation ID), a known microVM snapshot pitfall.

Audit and approval. You need a record of what the agent did: Landlock audit (ABI 7), seccomp SECCOMP_RET_LOG, the proxy’s request log, and human approval for irreversible steps such as push, deploy and delete. Without a log you cannot tell an escape from a bug.

Testing the sandbox itself. Run escape tests in CI (the x32, io_uring and TIOCSTI cases from section 1, plus something like SandboxEscapeBench from section 6) on every kernel and distribution you ship on, because the same sandbox can fail open elsewhere.

Side channels and physical access. Spectre-class leaks, SMT sharing and Rowhammer cross every software boundary, the VM one included. The mitigations are scheduling (no SMT sharing between tenants, core scheduling) and hardware. Physical attacks on the bus are out of scope for every tier here.

Could quantum help?

Not with isolation. Every boundary in this post is enforced by page tables, privilege levels, the kernel or the hypervisor, and none of them relies on a hard maths problem, so nothing labelled “quantum” makes them stronger or weaker. Quantum does touch agent sandboxing in a few narrower places:

Quantum technology Does it help? Why
Quantum computers (Shor’s algorithm) No, it is a threat It breaks the RSA and elliptic-curve cryptography that the trust around a sandbox depends on.
Post-quantum cryptography (ML-KEM, ML-DSA) Yes, indirectly Classical algorithms that keep that trust intact once quantum computers arrive.
Quantum random number generators No The kernel’s CSPRNG is not the weak point. Cloned snapshots are, and a QRNG does not fix them.
Quantum key distribution (QKD) No It needs dedicated optical links between two sites and says nothing about code on one host. The NSA and the UK NCSC recommend post-quantum cryptography instead.
Cloud quantum computers It is a new sandboxing problem Tenants sharing one quantum processor leak to each other through crosstalk.

What a quantum computer would break. It would break the cryptography that lets you trust the sandbox, not the sandbox itself:

  • Attestation. SEV-SNP and TDX reports, and NVIDIA’s GPU attestation, are signed with ECDSA today. Anyone who can forge them can present a fake confidential VM or GPU, which defeats the “protect tenants from the host operator” model in section 5.
  • The egress path. TLS from the agent to the proxy and from the proxy to the registry, plus SSH to git hosts.
  • Supply-chain signatures. Container images, snapshots, packages and git commits. Sigstore’s default, for example, is ECDSA P-256.

Symmetric cryptography such as AES-256 and SHA-256 stays safe. Grover’s algorithm only halves its effective strength.

The two halves migrate on different clocks.

  1. Key exchange is urgent now, because of harvest now, decrypt later: traffic recorded today can be decrypted once a large quantum computer exists. Source code and secrets that cross the egress proxy stay sensitive for years. The fix is mostly an upgrade: OpenSSH 10.0 uses the hybrid mlkem768x25519-sha256 by default, and OpenSSL 3.5 offers X25519MLKEM768 by default. A TLS-intercepting proxy has two TLS legs, so check both.
  2. Signatures can wait, but take longest to move. A signature cannot be forged after the fact, so it only matters once a quantum computer exists. But attestation keys are rooted in silicon, in CPU and GPU firmware and vendor PKI, so post-quantum attestation needs new hardware generations and cannot be patched in. NIST’s draft transition plan (IR 8547) deprecates 112-bit RSA and ECC after 2030 and disallows quantum-vulnerable algorithms after 2035. Hardware bought for a sandbox fleet today may still be in service by then, so ask vendors about their post-quantum attestation roadmap.

Randomness: the real problem is not where a QRNG would help. The RNG risk in this post is the snapshot pitfall from earlier in this section: clones of one microVM snapshot start with the same RNG state. Better entropy hardware does not fix that. Reseeding on restore does, via the VM generation ID.

The other direction: sandboxing quantum computers. If agents start submitting circuits to cloud quantum processors, the isolation problem comes back in new hardware. Crosstalk side channels in multi-tenant quantum computers (NDSS 2025) can identify which algorithm a co-located tenant is running, and the SWAP attack can both spy on a neighbouring circuit and corrupt its results. The proposed defences look familiar: do not place tenants next to each other, the way core scheduling keeps tenants off sibling SMT threads. The classical job-submission stack in front of the processor is still sandboxed with everything in this post.


A decision guide

  • Running an agent on your own laptop, your own code? The OS-level tier (bubblewrap as in Claude Code, or Landlock plus seccomp as in Codex) is proportionate. Add a filtering proxy if the agent has network.
  • Running your users’ code, multi-tenant? Do not stop at containers. Use a microVM (Firecracker, E2B, Kata) or at least gVisor, and treat the VM as the boundary. Put OS-level confinement inside it as defence in depth.
  • A narrow, pure-compute tool? Consider WASM: the strongest per-feature boundary and trivially dense.
  • Whatever you pick: control egress by domain with audit, strip ambient credentials (environment, metadata, mounted sockets, ~/.ssh), cap resources with cgroups (fork bombs are cheap), protect the files that run after the sandbox exits (git hooks, editor tasks, .envrc; see section 8), and fail closed. A sandbox that silently runs unconfined is the worst outcome.

Sources

Point-in-time (checked October 2026); vendor docs and 2026 analyses, so verify before relying on specifics.

Notes

1. Encrypted bounce buffers. A confidential VM’s memory is encrypted by the CPU with a key only the CPU holds, so the GPU cannot DMA into it directly: it would read ciphertext. Instead, the driver inside the VM encrypts the data itself, with a session key agreed with the GPU during attestation, and copies it into a separate, shared region of ordinary system memory: the bounce buffer. The GPU fetches it from there over PCIe and decrypts it on the chip, and results come back the same way in reverse. The host, the hypervisor and anyone probing the PCIe bus only ever see ciphertext. The price is an extra copy plus a CPU-side encrypt or decrypt on every transfer, which hurts most for workloads that move a lot of data relative to the compute they do. The longer-term fix is PCIe link encryption (IDE) with TEE-I/O and TDISP, which would let a trusted device DMA straight into the VM’s private memory and remove the bounce. ↩




Enjoy Reading This Article?

Here are some more articles you might like to read next: