Runtime

WebAssembly vs microVM: is Wasm safe enough for LLM code?

WebAssembly isolates one compiled module inside a runtime; a microVM isolates a whole Linux machine behind hardware virtualization.

Runtime gives model-written code a whole Firecracker microVM, so it runs as written: Python, Node.js, shell, pip install, subprocesses. A new sandbox ran its first Python command 351 ms after the create request at the median, over the public internet, on 24 September 2026 (speed).

How WebAssembly isolates code

WebAssembly is a bytecode format run by a runtime such as Wasmtime. The specification's security page says "each WebAssembly module executes within a sandboxed environment separated from the host runtime using fault isolation techniques." A module has its own linear memory, and accesses to it are bounds-checked. Calls go only to declared functions, and indirect calls are type-checked. A module can reach the outside world only through the functions its host chose to import.

WASI extends that to system resources. In its words, a module "starts with no ambient authority and can only do what the host explicitly grants." A host that grants no directory gives the module no files; one that grants no sockets gives it no network.

Wasmtime adds defence in depth on top: a 2 GB guard region before each linear memory, memory zeroed after an instance ends where it can be, Spectre mitigations on bounds checks, and around-the-clock fuzzing through Google's OSS-Fuzz.

How a microVM isolates code

A microVM runs a guest Linux kernel under a hypervisor. Firecracker, which Runtime uses, asks the host's KVM for a virtual machine and gives it what its site counts as "only 5 emulated devices". The code inside can do anything Linux allows, root included, because the boundary is the virtual machine itself. Firecracker's design document starts from the worst case: "all vCPU threads are considered to be running malicious code as soon as they have been started."

Side by side

WebAssembly (Wasmtime with WASI) MicroVM (Firecracker)
Unit of isolation One module in a runtime, often inside your own process A virtual machine with its own Linux kernel
Boundary Software fault isolation, validated by the runtime Hardware virtualization through KVM
What the code can call Only functions the host imports; WASI grants per capability Any Linux system call, answered by the guest kernel
What reaches the host The runtime's code and every host function you expose 5 emulated devices, in the project's count
Languages Anything compiled to Wasm; Python needs a Wasm build Any Linux program, unchanged
Packages Must be built for Wasm; native extensions need cross-compile apt, pip, npm as on any Linux machine
Processes and threads Pyodide, CPython's Wasm port, cannot start either Normal Linux processes and threads
Start time No single figure published by Wasmtime Up to 125 ms to guest init, in Firecracker's specification
Gaps the spec names Code reuse, TOCTOU races, timing side channels The guest kernel is the code's own; the host's is not shared

Is WebAssembly safe enough for LLM code?

For a small, pure function compiled to Wasm and given no imports, the sandbox is strong and cheap. The question is what the code needs, because each import is a hole you cut on purpose.

Model-written code rarely stays pure. It opens files, installs packages, calls APIs and starts processes. Each of those becomes a host function that your Wasm host implements and has to get exactly right. The specification says "other classes of bugs are not obviated by the semantics of WebAssembly": code reuse attacks against indirect calls, time-of-check to time-of-use races, and side channels such as timing attacks.

The host layer is where real bugs land. On 20 August 2026 Wasmtime published a High-severity advisory, GHSA-vqjp-4c8c-hfgg, "Filesystem sandbox escape when paths or symlinks contain trailing slashes", in its WASI implementation. That was a flaw in the capability layer, not in Wasm itself, which is the point: the more of the operating system you expose, the more of an operating system you are writing.

A microVM moves that work into a real Linux kernel that the code owns. If the code breaks the guest kernel, it has broken only its own disposable machine.

Can WebAssembly run Python or pip install?

Only in a Wasm build. Pyodide ports CPython to WebAssembly; its package installer takes pure-Python wheels and packages cross-compiled for Pyodide, and threading, multiprocessing and subprocess raise RuntimeError. Pyodide vs a server sandbox covers the limits in full. A microVM runs the Python on the machine, so any wheel on PyPI installs.

When each fits

  • Pick WebAssembly for plugins and user-defined functions inside your own service, edge handlers, and code you compile yourself with a narrow, known interface, where the host decides every capability.
  • Pick a microVM for code an agent or model wrote in an ordinary language, which expects a shell, a package manager, a filesystem, subprocesses and the network, and which you cannot review before it runs.

Why Runtime uses a microVM

Runtime gives each sandbox its own Firecracker microVM with Ubuntu 24.04, Python 3.12, Node.js 24, Bun, git and gcc. The controls a Wasm host would implement live on the Runtime host, outside the guest, so root inside cannot change them (security):

  • Network: off, or narrowed to named hosts; private addresses are always refused (turn off sandbox internet).
  • Time: commands time out, and the sandbox stops when its lease ends.
  • Money: maxCostMicros refuses a create whose first lease would cost more.
TypeScriptimport { Sandbox } from "withruntime";const code =  "import subprocess; print(subprocess.run(['uname', '-r'], capture_output=True, text=True).stdout)";await using sbx = await Sandbox.create({  network: { internet: false },  timeoutSeconds: 300,  onLeaseEnd: "stop",});await sbx.files.write("/workspace/answer.py", code);const run = await sbx.exec(["python3", "answer.py"], { timeoutMs: 30_000 });console.log(run.exitCode, run.stdout);
Pythonfrom withruntime import Sandboxcode = "import subprocess; print(subprocess.run(['uname', '-r'], capture_output=True, text=True).stdout)"with Sandbox.create(    network={"internet": False},    timeout_seconds=300,    on_lease_end="stop",) as sbx:    sbx.files.write("/workspace/answer.py", code)    run = sbx.exec(["python3", "answer.py"], timeout_ms=30_000)    print(run.exit_code, run.stdout)

The model's subprocess call works, because the sandbox is a real Linux machine; the network stays off whatever the code tries.

Try it on the free trial, 50 sandbox hours with no card:

Terminalnpx withruntime sandbox run --trial -- python3 -c 'import subprocess; print(subprocess.run(["id"]))'

More: microVM vs container, run untrusted LLM code, what is a microVM?, unikernel vs microVM.

Sources

Checked 25 September 2026.

Facts on this page were checked on 25 September 2026.