[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