Back to Blog

WASM Sandboxing Explained: How MPP Isolates Tool Execution

MPP Protocol·March 5, 2026·11 min read
MPPWASMWebAssemblySandboxingSecurityTechnical

WebAssembly has an image problem in the enterprise. Mention it to a backend team and they think of browser games, client-side compute, maybe edge functions. The idea that WASM is a serious server-side isolation technology — one that can replace containers for certain workloads — hasn't fully landed yet.

It should. For the specific problem of isolating AI tool execution, WebAssembly provides stronger guarantees than Docker, starts faster than a process fork, and uses less memory than a microVM. MPP's security model is built on these properties.

This post explains what WASM sandboxing actually does at the technical level, why it's a better fit than containers for per-invocation tool execution, and how MPP configures the sandbox to enforce the capability model.


What the Sandbox Provides

When an MPP tool executes, it runs inside a WebAssembly module instantiated by Wasmtime — a production-grade, memory-safe WASM runtime originally developed by the Bytecode Alliance. The sandbox provides four properties that matter for security:

1. Memory Isolation

Every WASM module operates in its own linear memory — a contiguous block of bytes that the module can read and write, but that is completely separate from the host process's heap. The tool cannot read the host's memory. It cannot access pointers outside its own linear memory. It cannot follow a reference to a data structure in the runtime.

This is not process-level isolation (where the OS enforces memory boundaries through page tables). It is specification-level isolation — the WASM instruction set physically cannot express an operation that accesses memory outside the module's linear memory. There is no mmap, no ptrace, no /proc/self/mem. The boundary is not enforced by a policy; it is enforced by the instruction set itself.

For AI tool execution, this means a compromised tool cannot:

  • Read API keys or credentials stored in the host process's memory
  • Access the model context or conversation history
  • Inspect the state of other tools that executed before it
  • Exfiltrate data through shared memory side-channels

2. No Ambient Capabilities

A freshly instantiated WASM module has access to nothing. No filesystem. No network. No environment variables. No system calls. No clock. Nothing.

Everything the module can do must be explicitly provided by the host through WASI (WebAssembly System Interface) — a standardised set of system interfaces that the host can selectively enable. If the host doesn't mount a directory, the module has no filesystem. If the host doesn't provide network functions, the module cannot make HTTP requests. If the host doesn't inject environment variables, the module sees an empty environment.

This is the opposite of how containers work. A Docker container starts with access to the full Linux system call interface and then restricts it with seccomp profiles, capabilities, and namespace configuration. A WASM module starts with nothing and then receives only what was explicitly granted.

The direction of the default matters enormously. With containers, a misconfiguration grants too much access. With WASM, a misconfiguration grants too little — the tool fails safely rather than failing dangerously.

3. Deterministic Execution Control

Wasmtime provides fuel metering — a mechanism that assigns a fuel budget to each module instance. Every WASM instruction consumes fuel. When the fuel runs out, execution terminates immediately with an ExecutionTimeout error.

This is more reliable than wall-clock timeouts for several reasons:

  • It is deterministic: the same code with the same input always consumes the same amount of fuel, regardless of system load, CPU speed, or scheduling delays.
  • It prevents busy-loop DoS: a tool that enters an infinite loop burns through its fuel budget and terminates, rather than consuming a CPU core indefinitely.
  • It is granular: fuel consumption is proportional to actual computation, not elapsed time. A tool waiting for a network response doesn't burn fuel during the wait.

MPP calibrates the fuel budget from the timeout_ms value declared in the tool's manifest:

| Manifest Setting | Sandbox Behaviour | |-----------------|-------------------| | timeout_ms: 30000 | ~30 seconds of computation at standard fuel rate | | memory_limit_mb: 64 | 64 MB linear memory ceiling |

4. Per-Invocation Isolation

MPP instantiates a fresh WASM module for every tool invocation. The module loads, receives its input via stdin, executes, writes its output to stdout, and is torn down. There is no persistent state inside the module between invocations. There is no warm connection to a database. There is no background thread still running.

This means:

  • A tool cannot accumulate state that drifts from its expected behaviour over time
  • A failed invocation cannot corrupt the state for subsequent invocations
  • There is no long-running process to monitor, restart, or patch
  • Memory is reclaimed completely — no leaks, no fragmentation

For frequently-invoked tools where startup latency matters, MPP supports warm starts: the host keeps a pre-compiled WASM module in memory and instantiates it without re-parsing. This reduces re-invocation time to sub-10ms while preserving per-invocation isolation — the module state is fresh, but the compilation step is cached.


How MPP Configures the Sandbox

The sandbox is not a generic WASM runtime. MPP configures it specifically to enforce the capability model declared in the tool's manifest and approved by the permission engine.

WASI Configuration

The host configures WASI based on the CapabilityToken — the runtime proof that a tool's requested capabilities were approved:

| Capability | WASI Configuration | |-----------|-------------------| | FilesystemRead("/data") | Pre-open /data as read-only | | FilesystemWrite("/output") | Pre-open /output as read-write | | EnvVar("API_KEY") | Inject API_KEY into the WASI environment | | KvStore | Set MPP_KV_PATH to the package's KV store path |

Paths not listed in the capability token are not pre-opened. The tool cannot access them — not because a policy check rejects the request, but because the directory literally does not exist in the module's filesystem namespace.

Network Filtering

Network access in WASM is not provided by WASI — there are no sockets in WASI Preview 1. Instead, MPP provides custom host functions that the tool can call:

mpp_net::is_allowed(domain) → 1 or 0
mpp_net::fetch(url, buffer) → response length or error code
mpp_net::post(url, body, buffer) → response length or error code

The host functions check every request against the tool's declared network domains. A tool that declared access to api.github.com can call mpp_net::fetch("https://api.github.com/repos/...") and get a response. A call to api.stripe.com returns error code -1 (domain denied).

This is enforced at the host function level — the tool's WASM code calls a host function, and the host decides whether to fulfil the request. There is no way for the tool to bypass this by constructing raw TCP packets or using an alternative network stack, because the WASM sandbox does not provide access to raw sockets.

Resource Limits

let config = SandboxConfig {
    memory_limit_bytes: 64 * 1024 * 1024,  // 64 MB
    timeout: Duration::from_millis(30_000),  // 30 seconds
    stdin_data: request_bytes,               // JSON-RPC input
    env_vars: approved_env_vars,             // Only declared vars
    dir_mappings: approved_dirs,             // Only declared paths
    capability_token: Some(token),           // Approved capabilities
    allowed_network_domains: vec!["api.github.com".into()],
};

let result = sandbox.execute(&wasm_binary, config)?;

If the tool tries to allocate more than 64 MB of linear memory, the allocation fails. If it tries to compute past its fuel budget, execution terminates. If stdout exceeds 1 MB, the buffer caps. Every resource has a declared ceiling, and every ceiling is enforced.


Why Not Containers?

The natural question from enterprise teams is: why not just run tools in Docker containers? Containers provide isolation too. They are well-understood. The tooling is mature.

The answer is that containers solve a different problem. Containers isolate long-running services. WASM sandboxes isolate individual function invocations. The difference in granularity drives differences in every other dimension.

Startup Time

A Docker container takes hundreds of milliseconds to seconds to start, depending on the image size and the runtime. A WASM module instantiates in single-digit milliseconds — and with pre-compilation, sub-millisecond.

For AI tool execution, where an agent might invoke dozens of tools in a single conversation, container startup latency is a deal-breaker. The user is waiting for a response. Adding 500ms per tool invocation turns a fluid conversation into a painful experience.

Image Size

A minimal Docker image (Alpine-based) is 5–50 MB. A typical application image is 100–500 MB. An MPP package containing a WASM binary, manifest, and resources is typically 100–500 KB — three orders of magnitude smaller.

This matters for distribution (faster downloads, less bandwidth), for storage (thousands of tools on a developer's machine), and for edge deployment (constrained storage environments).

Capability Granularity

Docker's security model operates at the container level. A container either has access to a network or it doesn't. It either has a volume mounted or it doesn't. Fine-grained control (this container can reach api.github.com but not api.stripe.com) requires external network policies, iptables rules, or service mesh configuration.

MPP's capability model operates at the per-domain, per-path, per-variable level. The granularity is built into the package format and enforced by the runtime without external tooling.

Isolation Strength

This is the one area where the comparison is nuanced. Docker containers use Linux namespaces and cgroups — OS-level isolation primitives that are well-tested but have a history of escape vulnerabilities (CVE-2019-5736, CVE-2020-15257, CVE-2024-21626). WASM isolation is specification-level — the instruction set itself prevents out-of-bounds access.

Both approaches are strong enough for production use. But WASM's isolation comes from a simpler, more formally verifiable foundation: a typed instruction set with bounds-checked memory access, rather than a complex kernel subsystem with a large API surface.

Docker Is for Services — WASM Is for Functions

The right mental model is not "WASM instead of Docker" — it is "WASM for the workloads Docker was never designed for." Running a PostgreSQL server in WASM would be absurd. Running a 50ms AI tool function in a Docker container is overkill.

MPP uses WASM because AI tools are functions: they take input, produce output, and should disappear. The execution model matches the workload.


The Execution Flow

Putting it all together, here is what happens when an AI agent invokes an MPP tool:

1. Gatekeeper verifies the .mpp package
   → Archive safety, signature, manifest validation

2. Permission engine evaluates capabilities
   → Issues CapabilityToken listing approved resources

3. Host configures the WASM sandbox:
   ├── Set memory limit (e.g., 64 MB)
   ├── Set fuel budget (e.g., 30s equivalent)
   ├── Mount approved directories (read-only or read-write)
   ├── Inject approved environment variables
   ├── Configure network filter with allowed domains
   └── Write JSON-RPC request to stdin buffer

4. Wasmtime instantiates the WASM module
   → Fresh instance, empty state, no connections

5. Module executes _start()
   → Reads request from stdin
   → Processes request using approved capabilities
   → Writes JSON-RPC response to stdout

6. Host reads stdout
   → Module is torn down
   → Memory is fully reclaimed

7. Privacy filter processes the response
   → PII is redacted before the agent sees it

8. Filtered response returns to the agent

The entire pipeline — from package load to filtered response — completes in tens of milliseconds for typical tools. The security overhead is negligible. The isolation guarantees are not.


Practical Implications for Enterprise Teams

If your organisation is evaluating AI tool security, WASM sandboxing changes the risk calculus:

  • No container orchestration required. You don't need Kubernetes, Docker Compose, or any container runtime to isolate AI tools. The isolation is built into the MPP runtime.
  • No network policies to maintain. Domain-level network filtering is declared in the tool's manifest and enforced by the sandbox. No iptables, no Calico, no service mesh.
  • No long-running processes to monitor. Each tool invocation is ephemeral. There is no server to patch, restart, or keep alive.
  • No shared state to worry about. Per-invocation isolation means one tool's behaviour cannot affect another's. There are no race conditions, no connection pool exhaustion, no memory leaks.
  • Predictable resource consumption. Memory limits and fuel budgets provide hard ceilings on resource usage per invocation. Capacity planning is arithmetic, not guesswork.

The complexity that enterprises typically manage at the infrastructure layer — orchestration, networking, process management, resource limits — is handled by the MPP runtime at the application layer. The operational burden of running thousands of AI tools drops from "operate a container fleet" to "run a host process."


For the full sandbox API, see the Runtime API Reference. For how WASM sandboxing fits into the broader security model, read Inside the Gatekeeper.