Phase 409 — build onix-busybox.stone
| Item | Value |
|---|---|
| Command | make phase 409 |
| Underlying make target/script | vm/phase4/build-busybox-stone.sh |
| Recipe template | vm/phase4/stone-recipes/onix-busybox/stone.yaml.in |
| Mutates disk/image? | No |
| Boots QEMU? | No |
| Main proof | ONIX can build BusyBox from source into a local .stone, verify it with moss, and add it to the local Phase 4 moss repo. |
Why this phase exists
Phase 407 found a real architecture problem:
the current booting image still contains Nix-sourced system payloads
That was okay for getting a first boot, but it is not the final ONIX model.
The ONIX rule is:
machine-plane software = moss/.stone
user toolbox software = Nix
BusyBox is machine-plane software.
It provides commands the operating system needs before a user has installed any personal toolbox:
/usr/bin/sh
/usr/bin/ls
/usr/bin/mount
/usr/bin/ifconfig
/usr/bin/nc
/usr/bin/ps
So BusyBox must become a .stone package.
Phase 409 builds that first replacement package.
What BusyBox is
BusyBox is one executable that behaves like many small Unix commands.
For example:
/usr/bin/busybox
can act as:
sh
ls
cat
mount
ip
ifconfig
nc
ps
The usual trick is symlinks:
/usr/bin/sh -> busybox
/usr/bin/ls -> busybox
/usr/bin/cat -> busybox
When the same binary is launched as /usr/bin/sh, it runs the shell applet.
When it is launched as /usr/bin/ls, it runs the list-directory applet.
That makes it very useful for early operating-system work because one small binary can provide enough commands to inspect and repair a minimal system.
Why this package is static for now
Most normal Linux programs are dynamically linked.
That means the program is not complete by itself. At runtime it needs:
the dynamic loader
shared libraries
the C library
For musl systems, that usually includes something like:
/lib/ld-musl-x86_64.so.1
ONIX will eventually package the C library and loader properly too.
But Phase 409 is earlier than that.
So this BusyBox build is static:
/usr/bin/busybox contains the code it needs to run
That lets us test the BusyBox .stone before solving every runtime library
ownership question.
This is not meant to hide the future work. It only keeps the learning step small:
first prove .stone BusyBox
later package the rest of the system dependencies
Important distinction: Nix source, musl build, boulder stone
This phase still uses Nix for one thing:
find the pinned BusyBox source tarball
That is acceptable here because source acquisition is not the final installed system payload.
There is one extra subtlety.
Boulder currently creates an AerynOS-style build container. That container has a glibc toolchain. ONIX is intentionally staying musl-based.
So Phase 409 uses a careful bootstrap bridge:
BusyBox source tarball
|
v
build BusyBox in the Alpine/musl forge VM
|
v
create a small prepared payload tarball
|
v
boulder packages that payload into a .stone
|
v
onix-busybox-...stone
|
v
moss local repo
What is not allowed is:
copy Nix's already-built BusyBox into the final image and call it done
Phase 409 does not do that.
What the script does
Run:
make phase 409
The host-side script:
vm/phase4/build-busybox-stone.sh
does this:
-
Reads the pinned
nixpkgs_2revision fromflake.lock. -
Asks Nix for the matching BusyBox source tarball path.
-
Computes the source tarball SHA-256.
-
Copies the recipe template and source tarball into the forge VM.
-
Builds BusyBox in the Alpine/musl forge VM with
CONFIG_STATIC=y. -
Verifies the built BusyBox binary is static.
-
Creates a prepared payload tarball containing
/usr/bin/busybox, applet links, and package notes. -
Generates a concrete
stone.yamlfrom:vm/phase4/stone-recipes/onix-busybox/stone.yaml.in -
Runs
boulder buildinside the forge VM to package the prepared payload. -
Runs
moss inspect --checkon the produced.stone. -
Extracts the
.stoneand checks the payload. -
Installs the package into a disposable moss target.
-
Copies the
.stoneback to the host. -
Adds it to:
artifacts/onix-local-repo/
What the recipe does
The generated recipe packages the prepared musl-static payload.
The important recipe parts are:
install:
copy usr/ into the install root
normalize modes
clear inherited setgid bits from package-created directories
The source build still happens in this phase, just before the boulder packaging step. That preserves the musl-static payload while we are still learning how to give boulder a native musl build environment.
The installed package contains:
/usr/bin/busybox
/usr/bin/sh
/usr/bin/ash
/usr/bin/awk
/usr/bin/cat
/usr/bin/getty
/usr/bin/hostname
/usr/bin/ifconfig
/usr/bin/ip
/usr/bin/nc
/usr/bin/netstat
/usr/bin/nslookup
/usr/bin/ping
/usr/bin/route
/usr/bin/uname
/usr/share/onix/packages/onix-busybox.applets
/usr/share/onix/packages/onix-busybox.md
Why /usr/bin and not /bin inside the stone?
Boulder/Moss currently keep package payloads /usr-centric. The previous
attempt to package /bin/busybox proved this: boulder explicitly ignored
non-/usr files during artifact analysis.
So Phase 409 packages BusyBox under /usr/bin.
Phase 410 will be responsible for image-level compatibility:
/bin -> usr/bin
or, on an image with a real /bin directory:
/bin/busybox -> ../usr/bin/busybox
/bin/sh -> busybox
The applet list is intentionally broader than the final system may need.
At this point we are still learning which bootstrap proofs need which commands. Later ONIX can shrink this to an explicit config.
Why it builds inside the forge VM
The forge VM already has:
boulder
moss
musl userspace
build tools
That makes it the right place to build ONIX bootstrap stones.
The host orchestrates the build, the forge compiles the musl-static BusyBox
binary, and boulder cuts the .stone:
host script = conductor
Alpine forge = musl source build
boulder = .stone packager
moss = .stone verifier/installer
Expected output
You should see the source policy first:
==> Phase 409 source-built BusyBox stone
source : /nix/store/...-busybox-1.37.0.tar.bz2
version : 1.37.0
sha256 : ...
nix role : source acquisition only
source build: Alpine/musl forge VM
stone cut : boulder packages the musl-static payload into a .stone
Then the forge builds the source, prints the generated recipe, cuts the .stone,
and verifies the result.
The important success lines are:
==> success
stone : /home/mason/stone-lab/onix-busybox/out/onix-busybox-...
repo : /home/mason/stone-lab/onix-busybox/repo/stone.index
Then the host copies the stone back and refreshes the local repo:
==> success
onix-busybox stone: artifacts/onix-stones/onix-busybox-...
local repo index : artifacts/onix-local-repo/stone.index
What this phase proves
Phase 409 proves:
- we can build a real system package from source,
- the output is a
.stone, - moss accepts and verifies it,
- the BusyBox binary is static or static-PIE and built in the musl forge,
- the required applet symlinks exist,
- the package installs into a disposable moss target,
- the host has a local repo containing
onix-busybox.
That is the first local replacement package for Phase 4.
Small build choices we learned here
Two details matter:
-
filemay describe the binary as:static-pie linkedThat is still self-contained for our purpose. It is a static position independent executable.
-
The BusyBox
tcapplet is disabled in this bootstrap build.Alpine’s current kernel headers no longer expose old CBQ traffic-control structs used by the BusyBox
tcapplet. ONIX does not needtcfor the Phase 4 shell/network/SSH proofs, so Phase 409 leaves it out.
What this phase does not do
Phase 409 does not yet install BusyBox into the ONIX disk image.
The current boot image is not changed.
That is intentional.
The safe order is:
409 — build and verify the stone
410 — install/use it in the image
411 — boot and prove shell/network/SSH still work
If Phase 409 fails, the booting image remains untouched.
How to inspect the result
After success, inspect the host artifacts:
ls -lh artifacts/onix-stones/
ls -lh artifacts/onix-local-repo/
You can inspect the package with host Moss:
artifacts/host-tools/bin/moss inspect artifacts/onix-stones/onix-busybox-*.stone
You can check the local repo index exists:
test -f artifacts/onix-local-repo/stone.index
The next phase should consume this package from the local repo instead of copying a BusyBox payload directly from Nix.