microsandbox
Lightweight VM sandboxes for Node.js — run AI agents and untrusted code with hardware-level isolation.
The microsandbox npm package provides native bindings to the microsandbox runtime. It spins up real microVMs (not containers) in under 100ms, runs standard OCI (Docker) images, and gives you full control over execution, filesystem, networking, and secrets — all from a simple async API.
Features
- Hardware isolation — Each sandbox is a real VM with its own Linux kernel
- Sub-100ms boot — No daemon, no server setup, embedded directly in your app
- OCI image support — Pull and run images from Docker Hub, GHCR, ECR, or any OCI registry
- Command execution — Run commands with collected or streaming output, interactive shells
- Guest filesystem access — Read, write, list, copy files inside a running sandbox
- Named volumes — Persistent storage across sandbox restarts, with quotas
- Network policies — Public-only (default), allow-all, or fully airgapped
- DNS filtering — Block specific domains or domain suffixes
- TLS interception — Transparent HTTPS inspection and secret substitution
- Secrets — Credentials that never enter the VM; placeholder substitution at the network layer
- Port publishing — Expose guest TCP/UDP services on host ports
- Rootfs patches — Modify the filesystem before the VM boots
- Detached mode — Sandboxes can outlive the Node.js process
- Metrics — CPU, memory, disk I/O, and network I/O per sandbox
Requirements
- Node.js >= 18
- Linux with KVM enabled, or macOS with Apple Silicon (M-series)
Supported Platforms
| macOS | ARM64 (Apple Silicon) | @superradcompany/microsandbox-darwin-arm64 |
| Linux | x86_64 | @superradcompany/microsandbox-linux-x64-gnu |
| Linux | ARM64 | @superradcompany/microsandbox-linux-arm64-gnu |
Platform-specific binaries are installed automatically via optional dependencies.
Installation
npm install microsandbox
The postinstall script automatically downloads the msb binary and libkrunfw library to ~/.microsandbox/.
Quick Start
import { Sandbox } from "microsandbox";
const sandbox = await Sandbox.create({
name: "my-sandbox",
image: "alpine",
cpus: 1,
memoryMib: 512,
});
const output = await sandbox.shell("echo 'Hello from microsandbox!'");
console.log(output.stdout());
await sandbox.stopAndWait();
Examples
Command Execution
import { Sandbox } from "microsandbox";
const sandbox = await Sandbox.create({
name: "exec-demo",
image: "python",
replace: true,
});
const result = await sandbox.exec("python3", ["-c", "print(1 + 1)"]);
console.log(result.stdout());
console.log(result.code);
const output = await sandbox.shell("echo hello && pwd");
console.log(output.stdout());
const configured = await sandbox.execWithConfig({
cmd: "python3",
args: ["script.py"],
cwd: "/app",
env: { PYTHONPATH: "/app/lib" },
timeoutMs: 30000,
});
const handle = await sandbox.execStream("tail", ["-f", "/var/log/app.log"]);
let event;
while ((event = await handle.recv()) !== null) {
if (event.eventType === "stdout") process.stdout.write(event.data);
}
await sandbox.stopAndWait();
Filesystem Operations
const fs = sandbox.fs();
await fs.write("/tmp/config.json", Buffer.from('{"debug": true}'));
const content = await fs.readString("/tmp/config.json");
const entries = await fs.list("/etc");
for (const entry of entries) {
console.log(`${entry.path} (${entry.kind})`);
}
await fs.copyFromHost("./local-file.txt", "/tmp/file.txt");
await fs.copyToHost("/tmp/output.txt", "./output.txt");
if (await fs.exists("/tmp/config.json")) {
const meta = await fs.stat("/tmp/config.json");
console.log(`size: ${meta.size}, kind: ${meta.kind}`);
}
Named Volumes
import { Sandbox, Volume, Mount } from "microsandbox";
const data = await Volume.create({ name: "my-data", quotaMib: 100 });
const writer = await Sandbox.create({
name: "writer",
image: "alpine",
volumes: { "/data": Mount.named(data.name) },
replace: true,
});
await writer.shell("echo 'hello' > /data/message.txt");
await writer.stopAndWait();
const reader = await Sandbox.create({
name: "reader",
image: "alpine",
volumes: { "/data": Mount.named(data.name, { readonly: true }) },
replace: true,
});
const output = await reader.shell("cat /data/message.txt");
console.log(output.stdout());
await reader.stopAndWait();
await Sandbox.remove("writer");
await Sandbox.remove("reader");
await Volume.remove("my-data");
Network Policies
import { Sandbox, NetworkPolicy } from "microsandbox";
const publicOnly = await Sandbox.create({
name: "public",
image: "alpine",
});
const isolated = await Sandbox.create({
name: "isolated",
image: "alpine",
network: NetworkPolicy.none(),
});
const open = await Sandbox.create({
name: "open",
image: "alpine",
network: NetworkPolicy.allowAll(),
});
const filtered = await Sandbox.create({
name: "filtered",
image: "alpine",
network: {
blockDomains: ["blocked.example.com"],
blockDomainSuffixes: [".evil.com"],
},
});
Port Publishing
const sandbox = await Sandbox.create({
name: "web",
image: "python",
ports: { "8080": 80 },
});
Secrets
Secrets use placeholder substitution — the real value never enters the VM. It is only swapped in at the network layer for HTTPS requests to allowed hosts.
import { Sandbox, Secret } from "microsandbox";
const sandbox = await Sandbox.create({
name: "agent",
image: "python",
secrets: [
Secret.env("OPENAI_API_KEY", {
value: process.env.OPENAI_API_KEY!,
allowHosts: ["api.openai.com"],
}),
],
});
Rootfs Patches
Modify the filesystem before the VM boots:
import { Patch, Sandbox } from "microsandbox";
const sandbox = await Sandbox.create({
name: "patched",
image: "alpine",
patches: [
Patch.text("/etc/greeting.txt", "Hello!\n"),
Patch.mkdir("/app", { mode: 0o755 }),
Patch.text("/app/config.json", '{"debug": true}', { mode: 0o644 }),
Patch.copyDir("./scripts", "/app/scripts"),
Patch.append("/etc/hosts", "127.0.0.1 myapp.local\n"),
],
});
Detached Mode
Sandboxes in detached mode survive the Node.js process:
const sandbox = await Sandbox.createDetached({
name: "background",
image: "python",
});
await sandbox.detach();
const handle = await Sandbox.get("background");
const reconnected = await handle.connect();
const output = await reconnected.shell("echo reconnected");
TLS Interception
const sandbox = await Sandbox.create({
name: "tls-inspect",
image: "python",
network: {
tls: {
bypass: ["*.googleapis.com"],
verifyUpstream: true,
interceptedPorts: [443],
},
},
});
Metrics
import { Sandbox, allSandboxMetrics } from "microsandbox";
const sandbox = await Sandbox.create({
name: "metrics-demo",
image: "python",
});
const m = await sandbox.metrics();
console.log(`CPU: ${m.cpuPercent.toFixed(1)}%`);
console.log(`Memory: ${(m.memoryBytes / 1024 / 1024).toFixed(1)} MiB`);
console.log(`Uptime: ${(m.uptimeMs / 1000).toFixed(1)}s`);
const all = await allSandboxMetrics();
for (const [name, metrics] of Object.entries(all)) {
console.log(`${name}: ${metrics.cpuPercent.toFixed(1)}%`);
}
Runtime Setup
import { isInstalled, install } from "microsandbox";
if (!isInstalled()) {
await install();
}
API Reference
Classes
Sandbox | Live handle to a running sandbox — lifecycle, execution, filesystem |
SandboxHandle | Lightweight database handle — use connect() or start() to get a live Sandbox |
ExecOutput | Captured stdout/stderr with exit status |
ExecHandle | Streaming execution handle — call recv() for events |
ExecSink | Writable stdin channel for streaming exec |
SandboxFs | Guest filesystem operations (read, write, list, copy, stat) |
Volume | Persistent named volume |
VolumeHandle | Lightweight volume handle from the database |
Factories
Mount | Volume mount configuration — Mount.bind(), Mount.named(), Mount.tmpfs() |
NetworkPolicy | Network presets — NetworkPolicy.none(), NetworkPolicy.publicOnly(), NetworkPolicy.allowAll() |
Secret | Secret entry — Secret.env(name, options) |
Interfaces
SandboxConfig | Sandbox creation configuration |
ExecConfig | Full command execution options (cmd, args, cwd, env, timeout, tty) |
NetworkConfig | Network policy with rules, DNS blocking, TLS interception |
MountConfig | Volume mount (bind, named, or tmpfs) |
PatchConfig | Pre-boot filesystem modification |
VolumeConfig | Volume creation options (name, quota, labels) |
SecretEntry / SecretEnvOptions | Secret binding to env var with host allowlist |
ExecEvent | Stream event: "started", "stdout", "stderr", "exited" |
ExitStatus | Exit code and success flag |
FsEntry / FsMetadata | Filesystem entry info and metadata |
SandboxInfo | Sandbox listing info (name, status, timestamps) |
SandboxMetrics | Resource metrics (CPU, memory, disk I/O, network I/O, uptime) |
TlsConfig | TLS interception options (bypass domains, upstream verification) |
Functions
isInstalled() | Check if msb and libkrunfw are available |
install() | Download and install runtime dependencies |
allSandboxMetrics() | Get metrics for all running sandboxes |
Enums
LogLevel | "trace", "debug", "info", "warn", "error" |
PullPolicy | "always", "if-missing", "never" |
License
Apache-2.0