userland.rundocs70Try the demo
SDK

SDK overview

@userland-run/nano-sdk is the supported way to embed NanoVM: a typed, ESM-only package with no runtime dependencies that boots the VM and exposes files, processes, a shell engine, server previews, a JavaScript sandbox, a worker transport, a signed app catalog and a terminal UI.

bash
npm install @userland-run/nano-sdk

These docs describe SDK 0.2.10, the version the live samples run on, and the runtime it vendors (nano v0.2.1, Node.js v25.4.0). Recent additions: typed worker catalog wrappers (0.2.7), host-engine server previews and terminal dispose() (0.2.8), a nodert CommonJS fix (0.2.9) and the agent tools subpath (0.2.10). The SDK is licensed MPL-2.0 OR LicenseRef-UEL; the vendored runtime is AGPL-3.0-only OR LicenseRef-UEL.

Entry points

Import
What it is
@userland-run/nano-sdk
the core: createNano, Nano, Vfs, Shell, NodeRuntime, serve, worker, catalog, createHeadless, provision
@userland-run/nano-sdk/terminal
the terminal UI (<nano-terminal>, createTerminal); a separate bundle so headless apps never download it
@userland-run/nano-sdk/worker
the Web Worker entry for createNanoWorker
@userland-run/nano-sdk/agent
VM tools for LLM agents and the WebMCP adapter (Agent tools)
@userland-run/nano-sdk/service-worker
nano-sw.js: cross-origin isolation and preview routing (Requirements)

Boot a VM

javascript
import { createNano } from "@userland-run/nano-sdk";

const nano = await createNano({
  image: { wasm: "/nano/console.wasm" }, // emulator + BusyBox
  ramMB: 512,
  engines: { node: "host" },             // node() on the browser's JS engine
  scripting: { wasm: "/nano/boa.wasm" }, // optional: the JS sandbox
});

Every option is documented with a runnable sample on Configuration. The samples on these pages use the instance this page boots, available as nano.

SDK · JavaScript

A tour in six lines

Files, a BusyBox command, a shell pipeline and Node.js, all against one virtual filesystem.

javascript
nano.fs.writeFile("/work/nums.txt", "3\n1\n2\n");
print((await nano.run("cat /work/nums.txt")).stdout.split("\n").join(" ").trim());
print((await nano.shExec("sort -n /work/nums.txt | tr '\\n' ' '")).stdout.trim());
const r = await nano.node(["-e", "const n = require('fs').readFileSync('/work/nums.txt', 'utf8').trim().split('\\n').map(Number); console.log('sum', n.reduce((a, b) => a + b))"]);
print(r.stdout.trim());
print(nano.fs.list("/work").map((e) => e.name).join(", "));
loading…
Expected output
3 1 2
1 2 3
sum 6
.keep, nums.txt

What the SDK gives you

Area
API
Guide
Run commands
run, exec, shExec, node, stdin, cancel
Files
nano.fs (Vfs): sync reads/writes, async mutations, archives
Terminal engine
Shell, tokenize
Node.js
host engine, RISC-V engine, NodeRuntime snapshots
Previews
startServer, ServeBridge, parseHttpResponse
Sandbox
script, scripting (Boa)
Off the UI thread
createNanoWorker
Apps
installApp, installBundle, Catalog, wasm apps
UI
<nano-terminal>, createTerminal
Terminal features without UI
createHeadless
Recipe-driven app runtime
provision
Agent tools
createVmTools, exposeWebMcp (/agent)

Behavior to know

  • VM operations are serialized. One VM runs one thing at a time; run, shExec, VM node, filesystem mutations and snapshots queue in FIFO order. See Running commands.
  • A server holds the VM. While a startServer() launch runs, other VM calls reject immediately until you stop it. See Serve mode.
  • Output is combined. stdout in every result holds stdout and stderr interleaved as the guest wrote them.
  • Main thread by default. createNano() runs the VM on the calling thread; long runs block the UI. Use the worker transport for interactive apps.
Tip · Known gaps in the current release are collected on Limitations, each with the observed behavior.

Finished apps built on the SDK are in the examples gallery; product-level tours are under features.