Safety and undo
What niwa does before it overwrites anything, what the journal records, how far back undo reaches, and why there is no world rollback.
niwa writes to your home directory, your packages, and your system settings. Everything on this page exists so that is never frightening.
Nothing is ever the only copy
Any write that would overwrite existing bytes archives them first. The
archive sits beside the journal, in ~/.local/state/niwa/archive/: one
directory per resource identity, and each file is named after the digest
of the bytes it holds, so undo finds a copy without reading it.
The rule covers every path to a displaced byte: a niwa.file whose
target diverged, a defaults value being replaced, an orphan you agreed
to remove, and undo itself. Overwriting becomes moving.
If the resource's spec used a secret, the archive is sealed with this machine's key. Otherwise undo would write your plaintext secret into the state directory, which is the leak these rules exist to prevent.
The overwrite ladder
One rule governs every file niwa writes. If the live bytes are the bytes niwa last wrote, apply updates them freely. If they are bytes niwa never wrote, apply leaves them alone and reports the resource as protected.
The choice is then yours. niwa pull brings the live bytes home to the
config. niwa apply --force <target> lets the write proceed. What force
displaces is archived first, so the ladder ends where the archive rule
starts.
The journal
Each apply writes an entry to ~/.local/state/niwa/journal.json. The
file holds three things: what the last apply acknowledged for every
identity, one entry per apply, and the proposals you answered "never".
An entry is a list of steps. A step is an identity and the effect it
had: a file written, a link made, a defaults value set, a package
installed, a service set, a Homebrew service started, a binary
installed, or an irreversible command. The journal records the digest of
what it wrote, never the content. The content is in the archive.
Neither the journal nor the archive is ever committed. Both belong to one machine, and nothing in the config repo reads them.
Undo reaches one apply
niwa undo walks the most recent entry, newest effect first. Files
restore from the archive. A defaults value goes back to what it was.
Packages that the run installed are uninstalled. Undo prints what it is
about to reverse and asks first, the same plan-then-confirm shape apply
has, and --yes skips the question.
The walk ends at the entry's boundary. One undo reaches one apply.
Each reversed step leaves the journal before the next one begins. If a
step fails, or you stop the run, the remainder sits exactly where the
next niwa undo finds it, and undo counts what came back and what did
not.
niwa.run and niwa.once are recorded as irreversible. Undo names
those steps rather than skipping them quietly, and never counts them. An
irreversible marker also survives the walk, so a later apply does not
run a once body a second time.
Verifying the run
niwa apply --verify re-reads everything once the run is over and
demands silence. Anything that still reports a change is not idempotent,
and niwa names the resource and the source line that declared it. The
run then exits 1.
That is the literal definition of the property the rest of the tool rests on, and it costs one extra read-only pass. It is opt in, because doubling the time on a converged machine is when you need it least.
Ninety days
Archived bytes are pruned ninety days after they were written. One thing overrides the horizon: bytes that a journal entry still references stay whatever their age, because undo needs them. Pruning is best effort, so a file that will not delete today deletes on a later run.
niwa doctor reads the archive and reports how many copies opened.
doctor --deep decrypts the sealed ones as well. An archive nobody can
read is a broken undo, and that is worth learning before you need it.
Why there is no world rollback
niwa keeps no generations. There is no state from six weeks ago to return to, and nothing rebuilds the machine as it stood on a date.
Two things stand in its place. Undo the last apply, or revert the config
in git and apply again. niwa history browses the applies before the
most recent one, so the record is still there to read.
The reason is cost. A world rollback means niwa owning every version of every package and file it ever wrote, and a store to keep them in. That is a different project, and the last apply is what people ask for.
Next
