Help
Limitations
What doesn't work, or works differently, in the current release. Each item is either recorded in the nano repository or was observed while writing these docs against SDK 0.2.10 (nano v0.2.1 runtime) in Chromium; the samples show the behavior live.
Items marked 0.2.10 were observed with the SDK this site runs; fixes are in progress upstream. The changelog tracks what lands.
Node on the host engine
The host-engine runner ("nodert") is checked against the real Node.js v25.4.0 running on the RISC-V runner. These differences are recorded in runners/node/DIVERGENCES.md:
Id
Area
On the host engine
Real Node
DIV-001
process.platform / archlinux / x64 (on purpose, for package compatibility)linux / riscv64 on the VMDIV-BUF-M0, DIV-CONSOLE-M0, DIV-FS-M0, DIV-URL-M0
Buffer, console, fs, url
lean implementations (no console colors; fs covers sync, promises and callbacks)
upstream modules
DIV-INSPECT-PROMISE
util.inspect of promises/proxiesdegraded output
full introspection
DIV-NET-M0
netloopback only (kernel pipes)
TCP
DIV-HTTP-M0
httpHTTP/1.1 with Content-Length bodies; no chunked request parsing
llhttp
DIV-CRYPTO-M0
cryptosha256, sha1, HMAC, random
full OpenSSL/BoringSSL
DIV-SQLITE-DUCKDB
node:sqlitebacked by DuckDB-wasm
embedded SQLite
DIV-ESM-CYCLE, DIV-ESM-DATAURL
ES modules
cycles concatenated into one module; blob:/data: module URLs
separate module records
DIV-WT-JSON
worker_threadsJSON messages over kernel pipes
structured clone, SAB transfer
DIV-RSPACK
rspack
unsupported
native bundler
DIV-VM-POW
**correct (
2**10 === 1024)the emulated VM's FP
pow is off by an ulpObserved with SDK 0.2.10 in the browser (the SDK doesn't register every kernel service the node tier can use):
require("zlib")functions fail withENOSYS: no service 'zlib';node:sqlitefails withno service 'duckdb'.child_processfails withENOSYS: no vm spawn delegate registered.vm.runInNewContextthrows aTypeError.http2throwsERR_NODE_HOST_UNSUPPORTED;engines.node: "auto"does not retry such a program on the VM.- An ES module entry file (
node /work/app.mjs) exits 0 without output. - A relative entry path (
node(["main.js"], { cwd })) exits 1 without output; pass an absolute path. - Global
fetch()doesn't complete before the process exits, andhttps.get()connects to a local port; use/dev/__net__(see Networking).
Check a few of them
javascript
const probe = async (label, src) => {
const r = await nano.node(["-e", src]);
print(label.padEnd(14), "exit", r.exitCode, "|", r.stdout.split("\n")[0].slice(0, 60));
};
await probe("arch", "console.log(process.platform, process.arch)");
await probe("zlib", "require('zlib').gzipSync('x')");
await probe("child_process", "require('child_process').execSync('true')");
await probe("http2", "try { require('http2').connect('https://x') } catch (e) { console.log(e.code) }");Expected output
arch exit 0 | linux x64 zlib exit 1 | ENOSYS: no service 'zlib' child_process exit 1 | ENOSYS: no vm spawn delegate registered http2 exit 0 | ERR_NODE_HOST_UNSUPPORTED
The RISC-V runner
- No
symlinkat/linkat:ln -sinside the guest doesn't create a link. 98 syscalls are handled; others returnENOSYS(Syscalls). - Signals:
rt_sigactionandrt_sigprocmaskare stubs, so handlers never run;killis a no-op. - Memory isn't returned:
munmapis a stub andbrk/mmapare bump allocators. tar czfproduces an empty archive andtar cf -to a pipe fails; write a.tarfile, thengzipit.- Only 67 BusyBox applets are linked into
/bininconsole.wasm; call the rest asbusybox <applet>(for examplebusybox httpd,busybox nproc). - BusyBox
httpdanswers a single connection (0.2.10); use Node.js servers for previews. - Speed: interpreted, roughly 50× to 1,600× slower than native depending on the workload (Performance).
The wasm runner (WASI preview 1)
- No termios / raw TTY (WASM-TERM): line-oriented tools only.
- No signals (WASM-SIGNAL): any kill terminates the app.
- No sockets (WASM-SOCK):
sock_*returnsENOTSUP. cwdis the only preopened directory: paths outside it (..) fail with "Capabilities insufficient".- A registered app runs through
execWasmApponly;nano.run("photon …")and shell lines report "not found" (0.2.10). wasm-componentartifacts are reserved and not runnable yet.
SDK
console.wasmembeds a single ELF (BusyBox) that the runtime also uses as its Node.js ELF:NodeRuntime.warmup()fails and VMnode()withoutcwd/envruns BusyBox ("node: applet not found"). Use the slimnano.wasmwith the catalog's node (0.2.10).- Interactive stdin blocks the page on the main thread (observed with SDK 0.2.6); use the worker.
writeStdin()before a run is discarded; redirect from a file instead.- The
Shellsendsnode …lines tonano.node(), so a RISC-Vnodeinstalled from the catalog is reached only throughshExecor withengines.node: "vm". Shellbuiltins: only one plainexport KEY=valueper line is tracked.provision()restores the warmup-time filesystem on every run: pass files asextraFiles. It reloads the page once if the service worker doesn't control it yet.ProvisionedApp.sh()runs one applet without shell parsing.- Catalog: the current index (generation 12) has no
wasm-apporwasm-serviceentries.
Scripting (Boa)
- With
webapis: ["timers"],setTimeoutcallbacks never run and awaiting a timer rejects with "script did not settle" (0.2.10). timeoutMsis checked between async host calls; it can't interrupt synchronous code, so always setlimits.evalModule()resolves tonulland imports are disabled.- Values cross the boundary as JSON.
Terminal
- SDK 0.2.6 and 0.2.7 shipped a terminal without
dispose()(removing the element left its VM running); 0.2.8 and later dispose on removal. showPreview()of a server started from the terminal's interactive shell couldn't be verified on a page that had mounted another terminal before (the preview answered from the disposed one); there is no live sample for it.
Networking
- Outbound traffic only through
/dev/__net__, i.e. the browser'sfetch(): CORS applies, CORS-blocked targets return502 Bad Gatewayunless you run the CORS proxy. - No raw TCP/UDP, no ICMP, no DNS;
wgetand Node's own sockets on the RISC-V runner can't resolve hosts.
Browsers and memory
- Cross-origin isolation is mandatory (Requirements).
- One VM's memory is at most 2 GiB (wasm32); Node.js on the RISC-V runner needs about 1.8 GB.
- The site's samples are tested on Chromium; Firefox and Safari aren't covered by the test suite yet.
Reporting
Found something not listed here? Open an issue in the relevant repository (nano, sdk, terminal or catalog) on GitHub.