NumericalOS

The boot chain

From kernel handoff to steady state, and what happens at each point where something can go wrong.

Two entry vectors

They differ deliberately in how destructive they are.

bare metal / VMexisting Linux host
entryinitramfs whose /init is bootstrap.shcurl numericalos.com/boot | sh
PID 1numinit becomes ituntouched — runs under existing init
to take PID 1automaticrequires explicit --take-pid1

Silently replacing PID 1 on someone's running server is destructive, so the convenient vector does not do it. That asymmetry is intentional.

Stage 1 — bootstrap.sh

1. mount /proc /sys /dev
2. ARCH=$(uname -m) -> canonical, via the generated ArchTarget table
3. resolve init artifact:
     prefer  numinit-$ARCH        (static)
     else    busybox-static-$ARCH + numinit.sh
4. sha256 verify  -- FAIL-CLOSED
5. load numos.state -> verify content hash
6. exec numinit as PID 1

Step 2 uses a table generated from the graph, so an unrecognized uname -m halts rather than guessing. Step 4 is the security boundary: a hash mismatch halts with both the expected and the actual value, because that message is what a human debugs from.

Steps 3, 4, and 6 are specified but not yet implemented. bootstrap.sh currently resolves the architecture and verifies a state file it is handed; it does not fetch an artifact and does not exec numinit. Artifact publication is later work. Stating this here rather than letting the diagram imply otherwise.

Stage 2 — numinit as PID 1

It walks BootPhase records by ordinal. Within each phase it resolves the unit DAG from requires and after edges, then runs units in dependency order.

The resolver is iterative — Kahn's algorithm — and that is not a style preference. POSIX shell has no local, so every variable in a function is global; a recursive resolver's recursive call clobbers its caller's loop variable and returns the wrong unit. The iterative form has no such hazard.

Unit kinds

kindbehavior
oneshotruns synchronously; failure propagates to the phase
targetsynchronous grouping point
longrunbackgrounded; the phase continues once it starts

A backgrounded longrun unit's failure is not visible to its phase's failure policy — nothing observes its exit code. That matches how real supervisors behave, and it is worth knowing before you write a state that depends on the opposite.

Failure policy

on_failureresult
haltboot stops, nonzero exit, stderr names the phase; later phases do not run
degradeboot continues; the node advertises degraded=1 so the coordinator stops scheduling onto it
continueboot continues, logged, not degraded

degrade is the interesting one. A partially broken node stays in the topology and says so, rather than dropping out entirely or pretending to be healthy.

A note on halting from shell

Fail-closed in POSIX shell has a trap that this project fell into and had to dig out of. exit inside a command substitution terminates only the subshell. A helper that calls numos_die from inside $(...) prints its alarming message to stderr, and the caller then continues with exit status 0.

Two conditions — a dependency cycle and a dangling unit reference — "booted successfully" this way while printing numos: HALT:. Every such substitution is now captured to a variable and status-checked, and the tests for those paths assert on exit status, not on the presence of the message, because the broken version emitted the message too.

Steady state

After the phase walk, numinit is meant to supervise longrun units with exponential backoff and run HealthPredicate probes on an interval, escalating fires to a swarm once the network is up.

Supervision is implemented and tested but not yet invoked. The backoff and restart-policy primitives exist with their own test coverage; numos_main does not call them. Health predicates round-trip through the state format but nothing executes them — interval supervision needs a real event loop, which the shell path does not do well. That is the clearest argument for the static-binary fast path.