[Toybox] WASM + toybox (again!)
Samuel Marks
samuelmarks at gmail.com
Mon Apr 13 04:49:09 PDT 2026
http://samuelmarks.github.io/toybox/
https://github.com/SamuelMarks/toybox/tree/wasm
I'd like to share an experimental WebAssembly (WASM) port of Toybox
that I've been working on. This port allows a large subset of Toybox's
core Unix utilities to run natively inside a web browser or via
Node.js, entirely client-side.
The project directory now includes the necessary build scripts
(`build_wasm.sh`), header stubs, a vanilla TypeScript/xterm.js
frontend, and a Node wrapper to execute simple pipelines.
### How it was achieved technically
WASM environments (especially in the browser) inherently lack
traditional OS-level primitives like `fork`, raw sockets, and loop
devices. Compiling a POSIX systems toolset requires carefully
decoupling the executable from these missing features.
Here is the approach taken in the build process:
1. **Aggressive Configuration Pruning**: The build starts with `make
defconfig`, followed by using `sed` on the `.config` file to strip out
features that are useless or broken in WASM. This includes system
daemons (`syslogd`), hardware/mount tools (`mount`, `blkid`,
`losetup`), process control (`ps`, `top`, `kill`), and shadow
passwords.
2. **Non-intrusive CFLAGS Stubs**: To handle unsupported POSIX
functions (`fork`, `vfork`, `execvp`, `sigaction`), we inject
`-include wasm_include/wasm_stubs.h` via `CFLAGS`. This allows us to
redefine and stub out these calls at compile time without polluting or
permanently altering the core Toybox C code.
3. **Temporary Code Patching**: For specific system calls that
Emscripten fundamentally fails to resolve (e.g., `xgetrandom`), the
script temporarily comments out those lines in `lib/lib.c` and
`lib/xwrap.c` using `sed` right before running `make CC="emcc"`, and
restores the files immediately afterward.
4. **Emscripten Compilation**: We build the project using Emscripten
(`emcc`) with flags like `-s MODULARIZE=1`, `-s FORCE_FILESYSTEM=1`,
and `-s EXPORTED_RUNTIME_METHODS=ccall,cwrap,callMain,FS`. This
produces a cleanly encapsulated JavaScript factory module
(`toybox.js`) alongside the binary (`toybox.wasm`), mounting
Emscripten’s in-memory virtual file system.
To interact with the compiled binaries, there is a `frontend/`
directory housing a frontend terminal emulator using `xterm.js`.
Commands are simply invoked by passing arguments to Emscripten's
runtime via `callMain(['command', 'args'])`.
### Caveats and Limitations
This port is functional but comes with strict limitations dictated by
the sandbox architecture:
* **No Subprocesses**: Because standard `fork()` and `exec()` don't
exist in browser WASM, standard job control, `unshare`, and pipeline
tools that rely heavily on process cloning had to be disabled or
stubbed.
* **Networking**: Standard raw BSD sockets do not work out of the box.
Tools like `wget`, `curl`, `netcat`, or `ping` will fail if they
attempt to establish standard TCP/UDP connections. Enabling network
tools in the future would likely require bridging them to the
browser's native `fetch()` API via JavaScript callbacks rather than
relying on the C networking stack.
* **Brittle Build Script**: The current `build_wasm.sh` approach
relies heavily on brute-force `sed` modifications to both the
`.config` and source files. It works as a proof of concept, but it's
hacky. If this were to be maintained long-term, it would likely
require a dedicated `make wasmconfig` target or clean `#ifdef
__EMSCRIPTEN__` blocks for things like `xgetrandom`.
* **Virtual File System Limits**: Emscripten’s `MEMFS` is strictly an
in-memory virtual filesystem. Anything relying on hardware
interaction, loopback devices, block devices, or `mount`/`chroot` is
unsupported.
I'd love to get your thoughts and feedback on this—specifically
regarding how to better handle the stubbed functions and
configurations without intruding on the core codebase.
PS: My next steps are to bring along `curl` and `jq` for my headless
compiler efforts; for both my OpenAPI ⟷ /bin/sh and my new multicloud
devops system; but that's for another post!
Best,
Samuel Marks
https://github.com/SamuelMarks
https://linkedin.com/in/samuelmarks
More information about the Toybox
mailing list