Skip to content
Oznog

6.2 · operations and agents · as-is

Imperative drift on NixOS evaporates silently

date
20260815 to 20260816
what happened
sandbox1 became the fleet's first NixOS host, installed and operated by an agent to test whether an agent could run NixOS without falling back on imperative habits. It could, and it produced one deliberate negative finding. An out-of-band change is not rejected. It succeeds, then disappears at the next rebuild.
what it cost
Nothing here, because it was a controlled test. But the failure mode is worse than a refusal. A manual fix that worked looks like success until the next convergence erases it without an error.
what changed
A bare-metal wipe and reinstall on 20260816, verified by a byte-identical closure hash computed before the disk was touched, set the pattern every later NixOS host inherited. State lives in committed configuration, never in a hand-run command.
the check now
Any doubt about whether a fix will survive is answered by comparing the closure hash before and after a rebuild, not by re-running the command and seeing that it still works.

A closure is the complete set of store paths a NixOS system depends on, so its hash is a fingerprint of the whole machine’s declared state. That is what made this test possible. Before any other host depended on the pattern holding, Node0 put its first NixOS machine through the destructive version of the question. Wipe the disk, rebuild from the repository alone, and compare the resulting closure hash against one taken before the wipe. The hashes matched. The host that came back was the host that went away, built from nothing but committed configuration.

The finding worth carrying elsewhere is the negative one. The risk on a declarative system is not that an agent, or a person in a hurry, will be blocked from making an ad hoc fix. It is that the fix will appear to work, be believed, and then vanish at the next scheduled convergence with nothing in any log calling the disappearance out as a regression. A refused change is loud. A forgotten change is silent, and the silence arrives hours or days after the person who made it has moved on.

That is why every agent on this fleet is told the same thing. Anything that matters has to go into the repository, because nothing that exists only as a hand-run command will still be there after the next rebuild. And if you are unsure whether a change will survive, do not test it by running the command again and watching it succeed. Compare the closure hash across a rebuild instead, which is the only answer the system itself cannot fake.

Source: node0 lessons v0.1, lesson 6.2. Sanitized: checklist v0.1, 20260921; runbook file names; voice pass 20260921. Part of oznog.com/node0.