NumericalOS

The state format

numos.state is the single artifact numinit reads at boot. It is line-oriented, not JSON, and that choice is load-bearing.

Why not JSON

The design originally specified JSON. Implementation killed it: the boot floor is POSIX shell, and shell cannot parse JSON without a parser. Shipping one contradicts the whole premise of a smallest bootstrap — so the runtime format became line-oriented and read-native, and JSON survives only as a web view for tooling that wants it.

The result is a format a shell can consume with grep and cut and nothing else.

The six record types

V <version>
C <sha256-of-body>
A <canonical> <aliases_csv> <zig_triple|-> <busybox_variant> <artifact_sha256|->
P <ordinal> <name> <on_failure> <required_units_csv|->
U <name> <kind> <restart> <backoff_ms> <backoff_max_ms> <arch_mask|-> <health_probe|-> <requires|-> <after|-> <exec...>
X <name> <interval_s> <threshold> <local_action> <on_fire|-> <probe...>
Blocks appear in the order V C A P U X; records are sorted within each block.

Three rules make it safe to read from shell:

Because a space would shift every subsequent field, export-time validation rejects any non-final field containing one. A unit whose name has a space is not a formatting inconvenience — it is a state file that parses as something entirely different, and it is refused before it can be written.

The content hash

The C record carries the SHA-256 of every other line. Both verifiers — the Python one at build time and the shell one at boot — recompute it and refuse a mismatch.

Getting those two to agree turned out to be the subtle part. They must have identical opinions about what the body is: whether blank lines count, whether a second C record is legal, and whether trailing content after the hash is ignored or fatal. Two verifiers that disagree produce a file one accepts and the other rejects, which is worse than either being wrong alone. They agree now, and tests assert it from both directions.

Determinism

The same graph state must produce a byte-identical file. Two things enforce it: generated_at is excluded from the hashed body, and every block is sorted so registry ordering cannot leak into the output. A test asserts that reversing the input operation list yields identical bytes.

This is what makes drift detectable. A changed hash means the graph changed; it never means the exporter felt different today.

Never hand-edit it

Editing numos.state breaks its content hash, and numinit will correctly refuse to boot from it. To change what a machine does, change the graph and re-export. That is not a limitation working around — it is the entire point of the design.

python3 -m numos.export_state --out dist