Graph-driven userspace init
Linux userspace whose configuration is a content-hashed graph export, fail-closed under hash mismatch, with a POSIX shell floor and optional static fast path.
Revision 1eb4fb9 ·
docs ·
evidence
Abstract
Conventional init systems reconstruct a dependency graph from unit files at
boot. NumericalOS inverts the order: a knowledge graph is the system of record;
numos.state is a deterministic, content-addressed projection. There
are no unit files on the target. Hash mismatch is a hard halt. Spec 1 implements
and tests the shell floor. Kernel handoff has been observed under QEMU on
x86_64: self-init completes (degraded=0); fleet on bare QEMU is
PASS-degraded. Metal and other arches remain open.
1. The problem with configuration-as-files
A modern init system is, structurally, a dependency resolver over a set of declarations. The declarations live in files; the dependency graph is implicit in them. Three consequences follow.
The graph is never first-class. It is reconstructed at boot from text, and no artifact ever represents it directly. Questions like "what depends on this" are answered by re-parsing, not by traversal.
Drift is invisible. Two machines with the same role diverge one edit at a time, and nothing detects it because there is no canonical form to compare against.
Authorship does not scale with derivation. When a fleet's capabilities are themselves a registry — a set of operations a node can perform — hand-writing one unit file per operation is transcription, and transcription rots.
2. The inversion
NumericalOS treats a graph as the system of record and the machine as a projection of it. Four graphs, following the M/G/S/MGS convention of the Intrikata topology from which this project descends:
| graph | role | node types |
|---|---|---|
| META | policy — what may run | admission rules, capability grants, trust roots |
| GENESIS | bring-up — ordered stages | BootPhase |
| MGS | steady state | OSUnit |
| SHADOW | failure and residual | HealthPredicate |
A fifth graph, the Agent Swarm Extension Complex, closes the loop: a SHADOW predicate that fires opens a swarm invocation, and remediation lands back as a contribution node. Self-healing is not a special subsystem; it is the same graph machinery pointed at the machine's own failures.
2.1 The claim is literal
"Configuration comes from the graph" is easy to assert and easy to quietly
violate — typically by hand-maintaining the one table the bootstrap needs
before it can read anything. NumericalOS closes that specific hole: the
architecture-detection table in the bootstrap is generated from
ArchTarget nodes, and a test asserts the committed file still
matches its generator. Adding an architecture is a data insert. If someone edits
the generated file by hand, the suite fails.
2.2 Units are derived, not authored
Partially implemented as of this revision. The unit set is
derived and exported. Floor numctl can write a capability document
and record join outcomes (disabled/joined/failed).
Op units are still not phase-required daemons; run-op is a dispatch
edge to NUMOS_OP_RUNTIME, not a local op interpreter. A node can
record identity and join state; it does not yet run topology ops itself.
A NumericalOS machine is a compute node in a topology: it boots, advertises
its silicon and its supported operations, and receives scheduled work. Its unit
set is therefore derived from the operations registry — 196
registered operations become 196 units, each depending on the join
unit, alongside a small seeded set for mount, network, clock, identity, and
join.
The honest consequence: a node's unit set changes when the registry changes. This is intended, and it is why the exported state is content-hashed. Drift becomes detectable rather than silent.
3. The artifact
One file, numos.state, is the machine's entire runtime input. It
is line-oriented rather than JSON, for a reason that only appears at
implementation time: the boot floor is POSIX shell, and shell cannot parse JSON
without a parser. Shipping a JSON parser into a project whose premise is the
smallest bootstrap is self-defeating. So the runtime format is one a
shell can consume with grep and cut, and JSON survives
only as a view for tooling.
Two properties make it trustworthy.
Determinism. The same graph state produces byte-identical output. The generation timestamp is excluded from the hashed body and every block is sorted, so registry ordering cannot leak into the artifact. A changed hash means the graph changed — never that the exporter felt different.
Content addressing. A C record carries the
SHA-256 of every other line. Both the build-time verifier and the boot-time
verifier recompute and compare. Editing the file by hand breaks the hash and the
machine correctly refuses to boot from it.
Optional resource envelopes travel as separate R records rather
than extra fields on U, so shell field indices for unit records stay
stable. Declaring a limit the floor cannot honour (notably
cpu_pct) is a named halt, not a silent ignore — the same
truthfulness discipline as hash verification.
Getting two verifiers in two languages to agree on what "the body" means — blank lines, duplicate records, trailing content — is subtler than it sounds. Two verifiers that disagree produce a file one accepts and the other rejects, which is worse than either being wrong alone.
4. Fail-closed as a construction, not a policy
An unverified init is worse than a halted machine. Every abnormal path in
NumericalOS halts with a named reason on the console and a nonzero exit: hash
mismatch, unknown architecture, dependency cycle, dangling unit reference,
malformed record, phase failure under a halt policy.
Two design decisions give that teeth.
Validation happens twice. Cycles and dangling references are
rejected at export time, so a machine never discovers them as PID 1. They are
also rejected at boot, because numinit may be handed a
state file that never passed through the exporter. The last line of defence does
not get to assume the first one ran.
Halting from shell is harder than it looks. In POSIX shell,
exit inside a command substitution terminates only the subshell. A
helper that halts from inside $(...) prints its message and lets the
caller continue with status 0. Two conditions — a dependency cycle and a
dangling reference — "booted successfully" this way during development,
while printing a halt message. The fix was mechanical once seen; finding it
required a test that asserted on exit status rather than on the presence
of the message, because the broken version printed the message too.
That episode is the argument for the whole verification posture. A fail-closed system that has not been tested for whether its failures actually close is decorative.
5. Smallest bootstrap, all architectures
These pull against each other. "Smallest" argues for a compiled static binary; "all chip infrastructures" argues for something that runs anywhere. Picking one means lying about the other.
The resolution is a floor and a fast path. The floor is POSIX shell over a
static busybox — zero compilation, runs on every architecture busybox
targets. The fast path is a static numinit binary, smaller and with
proper signal discipline, for architectures where one is published. Coverage is
the union of the two, and the bootstrap prefers the binary and falls back to the
shell.
Nine architectures are registered: x86_64, aarch64, riscv64, arm,
powerpc64le, s390x, mips64el, x86, loongarch64. The registry records, per
architecture, its uname -m aliases, its cross-compilation triple
where one exists, and its busybox variant. Where the triple is null, the shell
floor carries that architecture — which is the mechanism by which the "all
chips" claim stays honest rather than aspirational.
6. What this buys
Configuration becomes queryable. The dependency structure is a graph before it is a file, so "what depends on this" is a traversal.
Drift becomes detectable. Deterministic export plus content addressing means two machines either have the same hash or they do not.
Capability and configuration converge. When units are derived from the operations registry, a node that gains a capability gains its unit without anyone editing a file.
Degradation is expressible. A phase failure under a
degrade policy leaves the node running and advertising itself as
degraded, so a coordinator stops scheduling onto it. The alternative —
binary healthy-or-gone — discards information the fleet could use.
7. Limits (Spec 1)
Boot: observed under QEMU 10.2.2 on x86_64.
Self-init graph profile: boot complete degraded=0
+ steady state on both kernel+initrd and hybrid
ISO / GRUB serial (-cdrom) paths.
Fleet profile on bare QEMU (kernel+initrd):
degraded=1 (net/clock/join). Not observed on metal, not observed on
other arches. Shell/Python logic is covered by the unittest suite.
Zero-compile-first development shaped the floor; live boot still requires
numericalos-verify-boot for any new artifact.
- Steady state, health ticks, restart quarantine: implemented, tested, and exercised under QEMU (join health degrades on bare QEMU as expected).
numctl: identity, capability file, join outcomes (disabled/joined/failed); no control socket;run-opis a dispatch edge (not an interpreter).- Resource envelopes:
ram_mb/timeout_senforced;cpu_pctrefused (cgroups required). - Signals:
TERM/INTnamed-halt; not fullSIGCHLDdiscipline. - Static
numinit: resolution tested; binaries not published. - Op units: exported, not phase-required; no scheduled work path yet.
- Artifact production without WSL: Cloudflare Sandbox + R2
(
numericalos-build-iso-cf). - Graph-resolved
self-initdistro +numos-floortool floor (not busybox product layout).
Full residual list: evidence inventory. Documentation uses the same fail-closed rule as the runtime: no silent status upgrade.
8. Positioning
NumericalOS is not a distribution and does not compete with one. It ships no kernel and no package manager. It is a userspace and a configuration model that rides a kernel you already have, aimed at fleets where nodes are interchangeable compute rather than pets, and where the fleet's capability registry already exists as structured data.
The nearest relatives are declarative system builders and image-based immutable distributions. The distinction is where the source of truth lives: those systems treat a declarative file as canonical and build a machine from it, while NumericalOS treats a live graph as canonical and treats the file as a content-addressed projection with a hash the machine checks. The difference matters when the graph is also serving other consumers — a scheduler, an auditor, a swarm — and the machine is one reader among several.
9. Reproducing this
git clone https://numericalos.com/numericalos.git cd numericalos python3 -m unittest discover -s tests
No third-party dependencies. The repository is served as static files over dumb-HTTP — no forge, no account, no server-side code — so you can verify what you received against its history before running any of it. When the artifact you are about to build becomes PID 1, that property stops being a nicety.
To build a target on your own machine, and to close the boot gate this project cannot close for itself, see the agentic build skills.