Skip to content

WASI & syscalls

Compiled binaries from clang, and any wasm32-wasi module you bring, run on the Nimbus wasi_snapshot_preview1 host. All 46 preview1 functions work.

Preview1 itself has no sock_open, bind or listen. It can only act on descriptors the host already handed over. Nimbus hands them over through synthetic paths. path_open('/dev/nimbus/listen/<port>') binds a listening descriptor. A guest compiled against wasi-libc takes a connection from it with sock_accept; a guest layered over its own filesystem (ruby.wasm) reads the descriptor instead. Both end at the same socket fd, so a WASM server takes /port traffic the same way a Node one does.

Blocking I/O uses JSPI. Socket calls, poll_oneoff and the file reads that can reach the session filesystem are wrapped in WebAssembly.Suspending. Every entry into such a guest runs under WebAssembly.promising. V8 requires the suspender at call time even for an import that returns a plain errno, so there is no partial adoption and no fallback. A guest whose entries are synchronous by contract asks for unwrapped imports instead. The terminal render backend is one, driven per frame from a sync caller. Such a guest cannot be given anything that blocks. Outbound TCP uses path_open('/dev/tcp/<host>/<port>') to open a real cloudflare:sockets connection. poll_oneoff can mix fd, clock, and socket subscriptions.

A suspension lasts only as long as the request that started it. A workerd request context resumes only the wasm stacks it suspended itself, so a program parked in accept stays parked when the next request arrives. Servers on the virtual-socket kernel run the other way round. The kernel queues the connection and asks the runtime to service it on one dedicated cooperative pump, the only place a JSPI suspension is legal. Pyodide and ruby.wasm serve loopback ports this way.

JSPI suspends a call, but it cannot snapshot a stack. Programs that need fork or setjmp, bash among them, are additionally instrumented with Asyncify. Asyncify can unwind a running program’s call stack to memory and rewind it in another instance. The process fabric covers what that unlocked.

O_NOFOLLOW and O_APPEND work, symlinks survive path_rename, and read(2) on a socket fd goes to the socket. wasi-libc maps read to fd_read for every fd, so the host has to route it correctly.

Programs see NIMBUS_ABI=wasm32-wasi-nimbus. The in-session clang emits it, and the runtime catalog uses it to classify artifacts. WASM magic or a saved exec bit sends ./prog through the WASI runner with a pid and a process tab.

workerd blocks request-time WASM compilation from raw bytes. Modules must arrive pre-compiled in the module map. Pyodide extension packages must therefore be declared startup modules, while arbitrary dynamic loading fails with a clear diagnostic.

WASI files are a cache over the session filesystem. The spawn seed is a manifest of sizes. Content is fetched on first read, and writes go back as they happen, so a process that never exits still persists. bash and python are the two runtimes not yet on it. Each still carries a private filesystem that copies the subtree in and reports a diff on exit.

stdin is EOF, and there are no inbound sockets. The remaining gaps are tracked with designs.

wasi-threads asks the host to instantiate a second copy of the same module against the same shared linear memory and start wasi_thread_start in it. Worker Loader isolates each own their WebAssembly.Memory, SharedArrayBuffer stays within one isolate, and Atomics.wait on isolate-local memory has nothing to wait on. A thread is defined by the memory it shares, so an isolate backs a process.

thread_spawn is therefore left out of the import table. A program that links pthreads stops at wasm-ld with undefined symbol: thread_spawn rather than running with wrong memory semantics. Parallelism lives one level up, at the process boundary: a facet per process. That facet gets its own memory budget when it runs on a peer DO. The design note lists what the Workers runtime would have to expose for in-language threading to work.