# micropython-wasm

> Runs untrusted Python inside a WASI MicroPython module via Wasmtime, with hard memory, fuel and wall-clock limits and no filesystem or network by default

- Category: Agents & Automation / Code Sandboxes & Isolation
- Language: Python
- Licence: Apache-2.0
- Repository: https://github.com/simonw/micropython-wasm
- Canonical page: https://ai-tldr.dev/tools/micropython-wasm/
- Updated: 2026-06-06

## In short

micropython-wasm packages MicroPython as a WASI WebAssembly module and executes it from Python through the official `wasmtime` package. It sits in the Code Sandboxes & Isolation category of the Agents & Automation tools and is open-source, written in Python and licensed Apache-2.0. To get started: install.

## Overview

micropython-wasm packages MicroPython as a WASI WebAssembly module and executes it from Python through the official `wasmtime` package. It exists to run small snippets of code you do not trust — the classic case being a tool call an LLM agent generated — inside a boundary the host controls, without spinning up a container or a VM. Simon Willison wrote it up in *Running Python code in a sandbox with MicroPython and WASM* on 6 June 2026.

The isolation properties are the point. Each call builds a fresh Wasmtime engine, store, WASI configuration and module instance, so globals and imports do not leak between runs. There is no host filesystem access unless you explicitly preopen a read-only directory, which the guest then sees as `/input`, and there is no network capability at all. Resource use is bounded three ways: a maximum WebAssembly linear memory in bytes, a Wasmtime fuel budget that traps the guest when it runs out, and a wall-clock timeout enforced with epoch interruption.

Two execution shapes are provided. `run()` is one-shot: pass code, get back a `RunResult` with stdout, stderr and the remaining fuel. `MicroPythonSession` keeps a real resident MicroPython VM in a background thread, so variables, imports, functions, classes and live objects genuinely persist between calls — a bootstrap loop inside MicroPython calls back to the host for each next snippet and executes it with `exec(..., globals())`. Host functions can be registered so guest code can call back into Python through a low-level JSON bridge. The package is Apache-2.0, published to PyPI, and its README labels it experimental.

## What it does

- Fresh Wasmtime engine, store and module instance per call — no state leaks between one-shot runs
- No host filesystem unless a read-only directory is explicitly preopened as /input, and no network capability at all
- Three independent limits: WebAssembly memory in bytes, a Wasmtime fuel budget, and a wall-clock timeout
- `MicroPythonSession` keeps a genuinely resident VM in a background thread so state persists between snippets
- Register host Python functions the guest can call, with a bounded serialized response size
- Ships as a library, a CLI (`micropython-wasm`) and a REPL; runnable without installing via `uvx`

## Getting started

The package bundles its own WASI MicroPython artifact, so pip install is all the setup there is.

1. Install

   From PyPI. For local development the repository uses uv.

```bash
pip install micropython-wasm

# development
git clone https://github.com/simonw/micropython-wasm
cd micropython-wasm
uv run pytest
```

2. Run code from the command line

   The no-argument form starts a REPL with persistent state between prompts. --memory sets the WebAssembly memory limit in bytes and --fuel the Wasmtime fuel budget.

```bash
micropython-wasm -c "print(1 + 1)"
micropython-wasm script.py
micropython-wasm

uvx micropython-wasm --help

micropython-wasm --memory 33554432 --fuel 20000000 -c "print('hello')"
```

3. Run one snippet from Python

   run() returns a RunResult carrying stdout, stderr and the remaining fuel count.

```python
from micropython_wasm import run

result = run("print('hello')")

print(result.stdout)          # "hello\n"
print(result.stderr)          # ""
print(result.fuel_remaining)  # integer Wasmtime fuel count
```

4. Set explicit limits

   Every resource bound is an argument. Pass wall_timeout_seconds=None to disable epoch interruption; readonly_dir exposes a host directory to the guest as read-only /input.

```python
from micropython_wasm import run

result = run(
    "print(sum(range(10)))",
    memory_bytes=16 * 1024 * 1024,
    fuel=20_000_000,
    wall_timeout_seconds=1.0,
    host_result_bytes=256 * 1024,
)
```

5. Keep state across calls

   MicroPythonSession starts lazily on the first run() and keeps a live VM in a background thread. Fuel is refreshed per request; if a snippet exhausts fuel or otherwise traps, the VM stops and the session should be discarded.

```python
from micropython_wasm import MicroPythonSession

with MicroPythonSession() as session:
    print(session.run("x = 10\nprint(x)").stdout)   # 10
    print(session.run("x += 5\nprint(x)").stdout)   # 15
    print(session.run("print(x * 2)").stdout)       # 30
```

6. Expose host functions to the guest

   Register Python callables the sandboxed code can call, so a tool-use loop can hand controlled capabilities back in.

```python
from micropython_wasm import MicroPythonSession

session = MicroPythonSession(
    memory_bytes=16 * 1024 * 1024,
    fuel=20_000_000,
    readonly_dir="fixtures",
    host_functions={"add": lambda a, b: a + b},
    host_result_bytes=256 * 1024,
)
```

## When to use it

- Execute LLM-generated Python tool calls without giving them filesystem or network access
- Add a code-execution step to an agent loop where a container per call would be too slow or too heavy
- Bound a runaway computation with a fuel budget and a wall-clock timeout instead of hoping it terminates
- Keep an interactive analysis session alive for a user while still confining it to a WebAssembly sandbox

## Version history

- 2026-06-06 0.1a2: Simon Willison released the alpha, packaging MicroPython as a WASI WebAssembly artifact run through Wasmtime with configurable memory, fuel and wall-clock limits, plus a persistent session API for LLM agent tool use.
