One kernel routes each program to the right engine — a RISC-V VM, Node on the host JS engine, WASI commands, or sandboxed JS — running real BusyBox, Node.js v25 and the npm + TypeScript toolchain. No server. No install. Just a tab.
A shared kernel — an in-memory filesystem, a process router, a bus, capabilities and services — with four peer runners on top: a browser-based RISC-V VM that runs real ELF binaries unmodified, Node.js on the host JS engine, a wasm32-wasip1 tier, and a sandboxed JS engine. Each program runs on the right one. No transpilation, no shims — the actual BusyBox and Node.js binaries, plus wasm apps and scripts, over one operating-system layer.
A runner imports only from the kernel — never another runner. Cross-tier work goes through the router, the bus and the shared VFS, so a wasm tool, a Node server and a sandboxed script all see the same filesystem.
riscv · slownode · fastwasm · fastboa · medium~/proj $ echo "Hello from RISC-V" | busybox cat Hello from RISC-V ~/proj $ busybox ls /bin busybox sh ls cat echo grep sort seq wc ~/proj $ seq 5 | busybox sort -r 5 4 3 2 1
The riscv runner — the fidelity oracle of the four tiers — is a monolithic exec() function whose dense dispatch compiles to a WASM br_table. #![no_std] Rust, fat-LTO'd into one function, talking to the host over a handful of imports.
The Node-in-the-browser page runs live demos on each tier: the unmodified Node.js v25 binary and toolchain, an image CLI on the wasm runner, a capability-scoped Boa script, and a cross-tier handoff between the host engine and the RISC-V VM — all client-side, open source.
Run it in the browser →Two on-ramps, one model. Prototype in the live terminal above — npm install a package, write a server, watch it render. Then lift it into code with @userland-run/nano-sdk: a typed, ESM, zero-dependency package. Drop in <nano-terminal> for the whole UI, or call createHeadless for the same features without it — the full terminal, or just its capabilities, in your own page in minutes.
// Display the whole terminal — files, editor, live preview, ⌘K palette — as one // web component. Shadow DOM keeps its styles fully isolated from your page. import { defineNanoTerminal } from "@userland-run/nano-sdk/terminal"; defineNanoTerminal(); // <nano-terminal wasm-url="/nano/nano.wasm" style="height:560px"></nano-terminal> // …or take the same features headlessly — no UI, just the capabilities: import { createHeadless } from "@userland-run/nano-sdk"; const s = await createHeadless({ wasmUrl: "/nano/nano.wasm" }); s.fs.writeFile("/work/server.js", code); // files await s.installApp("ripgrep"); // catalog await s.run("node /work/index.js"); // run const srv = await s.serve({ node: ["/work/server.js"] }, { port: 3000 }); print(srv.url()); // routed through the service worker
# clone & build the slim ~2.3MB wasm module git clone https://github.com/userland-run/nano cd nano make build # run the suite — 55 passed, 0 failed make test # serve the build locally on :8080 make serve