Repository messaging and storage

ctrl+alt+d · repository channels, users, messages and the product origin object pool

L2 object pool

Developer guide

Install the toolchain

The shared setup for everything else in this guide. Both building packages and building the kernel compile through the same brintOS toolchain — do this once, then follow either guide.

The coroutine pass is mandatory, not optional. Both your packages and the kernel are compiled through our LLVM pass, HwjsCoroutinize — it's what makes a wasm program suspendable, resumable, and forkable. The build arms and then verifies the pass ran on every compile; a build that drops the plugin fails loudly rather than silently producing untransformed code. There is no Emscripten, Binaryen/Asyncify, or JSPI in the picture. See The brintOS toolchain.

What you'll build

  • Pinned clang + wasm-ld (from llvm-project) — clang has no stable plugin ABI across releases, so the pass must load into the exact LLVM it was built against.
  • The HwjsCoroutinize pass + wasmcc/wasmld drivers (from hwjs-cc).
  • Your libc — musl or glibc (from musl or glibc), cross-built into a sysroot — only needed for packages; the kernel is freestanding and doesn't link libc. Pick one (see step 3) — you don't need both.

Which pieces each workflow needs:

WorkflowLLVM + HwjsCoroutinizelibc sysroot (musl or glibc)
Build packagesrequiredrequired
Build the kernelrequirednot needed

1. Get the toolchain sources

Use the pinned toolchain and libc sources from your linux-in-the-browser checkout. Follow that project's README to initialize its submodules and build the toolchain for your rig. The paths below describe the individual toolchain components; use your checkout's build recipes for the current layout and pins.

2. Build clang, wasm-ld, and the pass

This is the part both workflows need. First build the pinned LLVM — it must be built with the LLVM dynamic-library and plugin flags so the coroutine pass can load into it, so use the repo's build script rather than a plain cmake invocation. Then build the HwjsCoroutinize pass and the wasmcc/wasmld drivers against it. Each repo ships a one-command build that carries the authoritative flags:

# Build the pinned clang + wasm-ld + opt + libLLVM (clang 22, commit 60513b8d)
#   with the dylib + plugin flags the pass loader needs. See llvm-project/BRINTOS.md.
./llvm-project/build-brintos.sh              # -> llvm-project/build/bin

# Build the HwjsCoroutinize pass plugin AND wire up the wasmcc/wasmld drivers.
#   toolchain/build.sh auto-discovers the LLVM you just built.
cd hwjs-cc
toolchain/build.sh
export PATH="$PWD/bin:$PATH"
wasmcc --version

This produces the wasmcc/wasmld drivers (now on your PATH, from hwjs-cc/bin) and the pass plugin at hwjs-cc/pass/coroutinize/build/HwjsCoroutinize.so. Packages compile through wasmcc, which emits LLVM bitcode per translation unit; wasmld then runs HwjsCoroutinize once over the whole merged program. (The kernel build, by contrast, loads the same .so per translation unit via -fpass-plugin — see Build the Linux kernel.) For the full driver environment interface — sysroots, the musl bitcode pool, the pinned plugin — see docs/BUILDING.md in hwjs-cc.

3. Build your libc — musl or glibc (packages only)

Skip this entirely if you only want to build the kernel (it's freestanding and links no libc). For packages, cross-build one libc for wasm32 into a sysroot, using the toolchain you just built. Pick the one that matches the userland you're targeting:

Choose musl or glibc — you won't need both. A package is compiled and linked against a single libc, and a machine's userland is built on one of them, so most developers pick one and stay there. Reach for musl when you want small, simple, statically-linked binaries; reach for glibc when you need GNU/Linux source compatibility — real dynamic loading (dlopen), NSS, and fuller locale/printf coverage. The two sysroots are independent, and each has its own driver: musl links through wasmcc, glibc through wasmcc-glibc.

Option A — musl

Cross-build our musl into a sysroot (see musl for the exact configure flags):

cd ../musl
./configure --target=wasm32 CC=wasmcc --prefix="$HOME/brintos-sysroot"
make && make install

# Point the drivers at the musl artifacts and select the coroutinizing arm.
# The env tables in docs/BUILDING.md (brintos/hwjs-cc) are authoritative:
export WASMLD_HWJS=1                       # required: the coroutinizing arm
export HWJS_TOOLS_DIR=/path/to/artifacts   # base for startfiles + the musl
                                           # bitcode pool + musl headers
# (individual overrides exist too: WASMLD_HWJS_LIBC_BC, WACKY_LIBC_INCLUDE, ...)

Option B — glibc

Cross-build our wasm32 glibc port. It's heavier than musl: glibc is built as a coroutinized bitcode object pool plus native startfiles and installed headers, so each package link pulls exactly the translation units it needs. Packages then compile through the glibc driver, wasmcc-glibc, in place of wasmcc. Follow glibc for the authoritative recipe, pins, and build script — the shape is:

cd ../glibc
# Configure for wasm32, then build the coro bitcode pool + startfiles + headers
# into a sysroot. glibc's build is multi-stage (~1900 TUs), so brintos/glibc
# ships the build script that drives configure -> pool build -> install.
./configure --host=wasm32-hwjs-linux-gnu --build=x86_64-pc-linux-gnu \
  CC=wasmcc-glibc --prefix="$HOME/brintos-glibc-sysroot"
make && make install

# Point the driver at the glibc build tree (sysroot/, glibc-bc/, startfiles/)
# — the env tables in docs/BUILDING.md (brintos/hwjs-cc) are authoritative:
export WASMLD_HWJS=1
export GLIBC_BUILD=/path/to/glibc/build
wasmcc-glibc --version
Order matters: your libc is cross-compiled with the clang + pass you just built, so build LLVM and the wrappers (step 2) before it. Everything here is a normal native build — nothing runs in the browser at this stage.

Next