Runs Linux binaries in the browser, on a kernel and CPU written from scratch in Zig. It can boot all the way into the Node.js REPL.
- Around 110 syscalls of the riscv64 ABI. The file/fd families, mmap and mprotect, clone + futex (real threads that really block), pipes, epoll/ppoll, signals, wait/exit_group, eventfd, System V shared memory, and a chunk of the socket API.
- An ELF loader for static riscv64 binaries.
- An in-memory VFS, seeded from a filesystem image that gets embedded at build time. /proc and /dev exist, ptys/ttys work.
- A socket layer backed by lwIP over a tun/tap device.
- A scheduler that wraps either the Web Worker pool or a native thread pool, same interface for both.
There is no Linux here at all: the ELF loader, the syscalls, processes/threads, the filesystem, and the CPU itself are all implemented in this repo and compiled to WASM. Each guest thread gets a Web Worker and they all share one WebAssembly.Memory, so guest memory is actually shared between "cores" and fork can be copy-on-write instead of a memcpy.
The CPU emulator (RV64 IMAFDC, Sv39/Sv48 paging) lives in emu/ and is split
out as its own package: riscvemu.
The kernel hooks its ecall handler and plays the role of Linux from there.
There's also a native build, which is what I actually use for debugging.
The original goal was to get production-grade Node.js apps running in the browser, and this was the first approach: emulate enough of Linux that the real Node binary just runs. It works, but an interpreted CPU has a performance ceiling, so I eventually moved on to strapkit — a Node.js runtime compiled to WebAssembly, so the JavaScript runs in the browser directly instead of through an emulated CPU.
Build, run, and test instructions moved to docs/running.md.