A self-contained web page that boots the TinyOS ISO inside the
v86 x86-to-WebAssembly emulator — no server, no
build step, nothing leaves the visitor's machine. See
../doc/WASM_BROWSER_FEASIBILITY.md for
the feasibility study and empirical results this demo is based on.
Live demo: https://douglasmun.github.io/TinyOS_enhanced/. This folder is the
source of truth; GitHub Pages serves from a separate gh-pages branch (repo
Settings → Pages only allows / or /docs, not /web). To ship a change here,
copy the updated files to the root of gh-pages and push — see "Refreshing the
deployed site" below.
The assets must be served over HTTP (WASM won't load from file://). Everything
the page needs — including tinyos.iso — lives in this folder, so serve web/
directly:
# from the repo root
python3 -m http.server 8000
# open http://localhost:8000/web/Press Start, then click the console and type. First boot sets a root
password; after login try help, ls D:, and /hello.elf (verifies an
ECDSA signature, then prints Hello from ELF! from ring 3).
Crypto (PBKDF2 100k, bit-serial ECDSA) is slow under the emulator's JIT, so password setup and the first program launch take a little while — this is a speed cost, not a fault.
| File | Source | In git? |
|---|---|---|
index.html, README.md |
the demo page (this repo) | yes |
tinyos.iso |
the OS image, loaded at runtime by v86 | yes — force-added past the *.iso ignore so Pages can serve it |
.nojekyll |
disables GitHub Pages' Jekyll processing | yes |
vendor/libv86.js |
v86 emulator (BSD-2-Clause, © the v86 contributors) | yes |
vendor/v86.wasm |
v86 WebAssembly core | yes |
vendor/seabios.bin, vendor/vgabios.bin |
SeaBIOS / VGABIOS ROMs shipped with v86 (LGPLv3) | yes (force-added past *.bin ignore) |
web/tinyos.iso is a committed copy of the built image. After a kernel change,
rebuild and re-copy it (then commit):
make -j8 kernel.elf
cp kernel.elf iso/boot/kernel.elf
i686-elf-grub-mkrescue -o dist/tinyos.iso iso # needs xorriso
cp dist/tinyos.iso web/tinyos.iso
git add -f web/tinyos.isoThe committed ISO is built from main at the v3.0 tree (the four 2026-10
audits, the #126 context_switch fix and the dead-TLS-crypto removal, plus the
TINYOS_VERSION bump), and matches the signed v3.0 release asset. It is a
pinned image, not a rolling build of main: it only moves when someone runs the
steps above, so expect it to fall behind again as work lands.
Login drops straight into the ring-3 shell (PR #51), which is what the demo
shows. Run a signed program by its path (/hello.elf); type kshell to hand
over to the kernel shell for the privileged and introspection commands (pae,
mem, wxaudit, auditlog, networking), and exit to log out. The nine
privileged commands are gated on euid 0, so a non-root user reaching the
kernel shell still cannot run them.
A round of CVE-class audits (ELF loader, syscall boundary, VFS path resolution, concurrency) and a hardening sweep. Most of it is internal and not visible from this offline demo, but for the record:
- ELF loader (PR #167): static loader,
parse == signedsize bound, and a page-granular segment-overlap check — three defence-in-depth hardenings. - VFS protected-path gate (PR #169):
/bin /sbin /etc /bootand/kernelnow match exact-or-slash, not a bare prefix, so the directory node itself is protected and a same-prefix sibling is not. - Scheduler/mutex (PRs #162, #166): the
context_switchESP off-by-4 that triple-faulted some builds (#126) is fixed; mutex waiters are generation- validated and double-enqueue is guarded. - Dead TLS1.3 crypto removed from the build (PR #170): the unreachable HKDF/AES-GCM/ECDHE demo surface (with a latent stack overflow in a never- compiled caller) is no longer compiled or linked.
- Copy-user fault line demoted to a kdbg trace (PR #168), so a faulting user buffer no longer prints a per-fault console line.
SHA-256 31e858092e022cb53f4faf6fb60b99e1fe169092600240ac5299394279821487 as of
2026-10-10. Note i686-elf-grub-mkrescue is non-deterministic, so a fresh
rebuild will hash differently even with identical inputs — this hash identifies
the committed artifact, it is not reproducible from source.
Pages serves the gh-pages branch (root), not web/. After changing a file
here (e.g. index.html) and merging it to main, mirror it onto gh-pages:
git worktree add /tmp/ghp origin/gh-pages
cp web/index.html /tmp/ghp/index.html # or the ISO, vendor files, etc.
git -C /tmp/ghp commit -am "gh-pages: sync index.html"
git -C /tmp/ghp push origin HEAD:gh-pages
git worktree remove /tmp/ghpThe branch holds only what the site serves: index.html, tinyos.iso,
vendor/, and .nojekyll (no README.md).
-
No hard disk attached → drive C: (FAT32) is unavailable (
[IDE] not initializedis expected). D: (in-memory RAMFS) works. To enable C:, attach a FAT32 image as an IDEhdainindex.html. -
No networking —
index.htmlconstructsV86with nonet_device, so no NIC is attached at all and the PCI scan finds nothing. Expect exactly one line:[PCI] No NIC present; networking disabled [OK]This is a supported configuration, not an error: boot proceeds offline (DHCP → APIPA fallback). Adding
net_devicewould attach an NE2000 or virtio-net, neither of which TinyOS drives — it drives the Intel e1000 (8086:100e) only — so the message would then name the device instead:[PCI] No supported NIC (want 8086:100e); found 1 other NIC(s), first 10ec:8029 -- networking disabled [OK] -
NX unavailable in v86 → W^X enforcement degrades to PARTIAL (by design; PAE paging itself works and is active).
Any static host works (GitHub Pages, Netlify, etc.). No server code. Copy the
whole web/ folder — it is self-contained. v86 does not require cross-origin
isolation (COOP/COEP) for this single-threaded configuration.