[Toybox] WASM + toybox (again!)

Rob Landley rob at landley.net
Mon Apr 20 11:51:21 PDT 2026


On 4/13/26 06:49, Samuel Marks wrote:
> 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.

It's not gonna have vfork() either, is it? :)

(I've kept "bolting this into a bootloader like uboot" in mind as a use 
case, a reasonable subset of commands should work.)

> 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.

...are non-shadow passwords supported?

I'm assuming https://github.com/landley/toybox/pull/601 is related to 
this, adding explicit O_RDONLY when there isn't an O_RDWR in a bunch of 
callers, because this (almost uniquely) has O_RDONLY _not_ be zero 
despite Unix system 6 having it as 0 and everybody else copying that for 
50 years.

If it does really need it, seems like a better idea of have 
xcreate_stdio() do it in lib/xwrap.c? There's probably a simpler way than:

   int fd = open(path, (flags^O_CLOEXEC)&~WARN_ONLY,
     O_RDONLY ? mode&O_RDWR ? mode : mode|O_RDONLY : mode);

But I haven't checked what optimizes out. (mode&O_RDWR ? mode : 
mode|O_RDONLY) is simpler but even though mode|0 should become just 
"mode" I'm not 100% sure ((simple test with no side effects) ? same_x : 
same_x) optimizes out. (It _should_, I just dunno if it DOES...)

> 2. **Non-intrusive CFLAGS Stubs**: To handle unsupported POSIX
> functions (`fork`, `vfork`, `execvp`, `sigaction`), we inject
> `-include wasm_include/wasm_stubs.h` via `CFLAGS`.

Or lib/portability.h could have an ifdef.

> 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.

In theory what lib/portability.c is for.

> 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.

I'll take your word for it, not my area...

> 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.

I dunno what resources that environment _does_ have available, but "no 
scheduler is present" is fairly standard for running in a bootloader.

> * **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.

Ok.

> * **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`.

Stuff in lib/portability.? can have those kind of #ifdefs. I thought 
xgetrandom already _was_ in there...

scripts/wasm_miniconfig makes sense, along with a wasm_defconfig target 
in Makefile. (Although I had a vague todo item about maybe generating 
that from scripts/*_miniconfig the way the "include .singlemake" works 
now...)

> * **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.

Sounds like /proc and /sys aren't there either.

> 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.

I'm... not fully engaged these days.

If the OS age discrimination stuff goes into effect I'm almost certainly 
deleting toybox off the net entirely to avoid legal liability. (Gavin 
"spyware" Newsom says I get fined $7500/instance if any californians 
download files from https://landley.net/bin/mkroot after January 1, and 
Google donated to the Epstein Ballroom instead of fighting it before 
locking down the app store, so...)

It's a 50 state strategy (minnesota's failed to progress on a 67-67 tie 
but has had a bunch of "authors" added since, and the federal one is at 
https://www.gamingonlinux.com/2026/04/new-us-congress-bill-proposal-requires-all-operating-system-providers-to-verify-ages/), 
so I'm applying to https://jobs.minnesotanonprofits.org/ to pivot out of 
the industry entirely before it hits the fan.

I've been referring to 
https://osselcna2026.sched.com/event/2JQxw/building-the-simplest-possible-linux-system-rob-landley-hobbyist 
as my "farewell address". I've been TRYING to scrape up the spoons to 
finish toysh, but it's just hard to care when you actively _want_ Taiwan 
to blow the fabs?

I wouldn't mind so much if it was maggots doing it because we need to 
Damnatio Memoriae everything they touch as soon as It Happens, but 
Spyware Newsome is copying Ken Paxton's 
https://www.npr.org/2022/07/11/1107741175/texas-abortion-bounty-law and 
https://www.npr.org/2025/06/27/nx-s1-5422424/scotus-texas-porn-law and 
yet is somehow considered NOT a member of maga? And 
https://bsky.app/profile/localcelebrity.bsky.social/post/3mjn4ogjawc27 
is J.B. Pritzker and Elizabeth Warren is sponsoring Kosa and I was 
already kind of tired of vibe coded rust winding up in linux-kernel 
and... I just want to go do something useful? (At least minnesota's 
version failed to advance on a 67-67 party line vote with all democrats 
voting against and all republicans voting for, that I can UNDERSTAND...)

So... good luck?

Rob

P.S. Yes I know 
https://blog.system76.com/post/system76-on-age-verification says go 
along with the frog boiling, because a government that blocked all 
router imports won't REALLY require Palantir's "clear" to provide a 
signature to your hardware's Trusted Platform Module so all online 
activity is de-anonymized so anti-ICE protests can never organize 
through Signal again. I mean obviously Chuck Schumer would never allow 
laws that REQUIRE government permission to reinstall your own laptop 
(and incidentally outlaw all historical OS preservation efforts, outlaw 
library computers and homeless people whose ID got stolen getting burner 
phones from wal-mart, the shared router in your apartment building 
somehow needs an "age")... and I'm just tired. I was tired when it was 
just vibe-coded rust in the kernel inserting a glue layer between two 
contexts in ring 0 with https://www.youtube.com/live/OdMQwDtumJ8 but 
this? Google doesn't need me, they have Gemini. And I wish them a very 
merry https://en.wikipedia.org/wiki/Donald%27s_Happy_Birthday with 
Gemini in place of the cigars.

P.P.S. It comes and goes. Sometimes I think I should do a big sprint to 
get the final version of Linux From Scratch (12.4 I think) to build 
under mkroot. I've still got over half a year! But it's hard to sustain 
the drive to finish https://en.wikipedia.org/wiki/G%C3%A4vle_goat when 
I'm the one who's going to then burn it. What's the point? It's no fun 
anymore.


More information about the Toybox mailing list