Bash-compatible shell runtime in Rust, compiled to WebAssembly. Runs in browsers, inside Pyodide, and as a horizontally-scaled sandbox pool on Kubernetes — all from one codebase.
A sandbox for LLM agents that need a shell, Python, and a filesystem without giving the model host access. Bash with 88 utilities (grep, sed, awk, jq, tar, curl, …), Python 3.13 with pip/micropip for pure-Python packages, a virtual filesystem — all running in-process as WebAssembly, with no OS processes and no network unless explicitly allowed.
Three deployment modes from one core:
| Target | When | Entry point |
|---|---|---|
Standalone (wasm32-unknown-unknown) |
browser Web Worker, offline | crates/wasmsh-browser |
Pyodide (wasm32-unknown-emscripten) |
Node or browser, Python sharing the VFS | packages/npm/wasmsh-pyodide |
| Scalable (Kubernetes) | multi-tenant agent platforms | deploy/helm/wasmsh |
LLM-generated shell commands are adversarial input. wasmsh is built so a bad rm -rf / or a curl to an exfil host cannot escape the sandbox:
- WASM boundary. No syscalls, no
std::fs, no hostexecin any shipped profile. The wasm module only sees what the embedder hands it. - Capability-based VFS. Every session gets an isolated in-memory filesystem; nothing on the host is visible unless the embedder mounts it.
- Network allowlist.
curl/wgetroute through a host-mediated broker that enforces a per-session hostname allowlist. Empty list = no network. - Step budgets. Every command runs with a bounded step count; runaway loops and fork-bombs terminate deterministically.
- Per-session V8 isolation (scalable path). Each session is its own worker with a capped heap; one session cannot starve its neighbours.
- Clean-room provenance. No GPL code in the core — behavior-compatible with bash, but a fresh implementation, so no licence contamination for downstream embedders.
Full surface in docs/reference/sandbox-and-capabilities.md; threat model and design choices in docs/explanation/design-decisions.md.
No containers, no VMs, no OS processes per session. Starting a sandbox is a wasm snapshot restore, not a docker run:
- ~300 ms cold spawn, ~6 ms snapshot restore once the template worker is warm
- ~1.5 ms per warm bash command, ~3 ms per warm
python3 -cround-trip through the dispatcher - ~80 MB RSS per active session in steady state (stock Pyodide + bash)
That makes per-node density the ceiling instead of CPU. Rough sizing on a 64 GB / 40-core node: ~500–800 warm sessions for typical agent workloads, ~100 session creates/s burst throughput. See docs/guides/performance-testing.md#sizing-a-scalable-deployment for the benchmark and full capacity table — and re-run just bench-dispatcher-compose on your own hardware before committing to a size.
Two interchangeable backend classes with the identical BaseSandbox surface — upgrading from laptop to cluster is a one-line import change:
| Ecosystem | Package | In-process | Scalable |
|---|---|---|---|
| npm | @mayflowergmbh/langchain-wasmsh |
WasmshSandbox.createNode() |
WasmshRemoteSandbox.create({ dispatcherUrl }) |
| Python | langchain-wasmsh |
WasmshSandbox() |
WasmshRemoteSandbox(dispatcher_url) |
import { createDeepAgent } from "deepagents";
import { WasmshSandbox } from "@mayflowergmbh/langchain-wasmsh";
const sandbox = await WasmshSandbox.createNode();
const agent = createDeepAgent({ backend: sandbox });from deepagents import create_deep_agent
from langchain_wasmsh import WasmshSandbox
agent = create_deep_agent(backend=WasmshSandbox())The Python adapter also ships a persistent Python REPL middleware
(WasmshInterpreterMiddleware) with optional programmatic tool calling
(PTC) — the model can await tools.<name>(...) inside one py_eval
invocation to fan out, branch, and chain LangChain tools without
extra LLM turns:
from langchain_wasmsh import WasmshInterpreterMiddleware
agent = create_deep_agent(
model="claude-sonnet-4-6",
tools=[my_search_tool],
middleware=[WasmshInterpreterMiddleware(ptc=["my_search_tool"])],
)A WasmshFilesystemBackend exposes the same sandbox VFS as a DeepAgents
Memory
backend; pair with SkillsMiddleware for import skills.<name> support
inside the REPL.
Full integration guide (remote variant, deployment topology, operational knobs): docs/integrations/langchain-wasmsh.md.
Runnable examples covering every deployment shape:
| Variant | Directory |
|---|---|
| In-browser agent (Pyodide + LLM all client-side) | examples/deepagent-browser/ |
| In-process Node agent | examples/deepagent-typescript/ |
| In-process Python agent | examples/deepagent-python/ |
| Scalable Docker Compose (remote sandbox) | examples/deepagent-typescript/ + examples/deepagent-python/ |
| Scalable Kubernetes (Helm) | examples/deepagent-kubernetes/ |
| Direct Rust embedding (no sandbox layer) | examples/rust/ |
| Raw wasm-pack (JS/TS, no LLM) | examples/web/, examples/typescript/, examples/python/ |
| Registry | Package | Install |
|---|---|---|
| crates.io | wasmsh-runtime |
cargo add wasmsh-runtime |
| npm | @mayflowergmbh/wasmsh-pyodide |
npm i @mayflowergmbh/wasmsh-pyodide |
| PyPI | wasmsh-pyodide-runtime |
pip install wasmsh-pyodide-runtime |
| Containers | ghcr.io/mayflower/wasmsh-{dispatcher,runner} |
docker pull |
Pre-built tarballs and image digests: GitHub Releases. Build from source: just ci (Rust), just build-standalone, just build-pyodide.
| Start here | Tutorials: Rust, JavaScript, Python |
| Deep Agents | Integration guide (in-process + remote, both languages) |
| Deploy | Docker Compose (single-host), Helm chart (Kubernetes), snapshot-runner architecture, runner runbook |
| Tune | Performance testing & sizing, dispatcher API, runner metrics |
| How-to | Embedding, Pyodide integration, Adding a command, Troubleshooting |
| Reference | Shell syntax, builtins, utilities, protocol, sandbox & capabilities, supported features |
| Explanation | Architecture, design decisions, ADRs |
The Pyodide integration would not be possible without the outstanding work of the Pyodide team. They brought CPython to WebAssembly and built an ecosystem that makes running Python in the browser practical and reliable. wasmsh links directly into their Emscripten module, sharing the interpreter and filesystem — a testament to how well-designed their architecture is. Thank you to everyone who contributes to Pyodide.