File locations
Every file niwa reads or writes, where it lives, and whether it is committed. Four roots hold all of it.
Four roots hold everything: the config repo, the state directory, shared data,
and a few places elsewhere on the machine. One module resolves all of them, so
nothing else in the tool reads HOME or the XDG variables.
The config repo
Everything here is committed. The repo is an ordinary git repository, and
niwa init runs git init in it.
~/.config/niwa
The secrets are committed because they are ciphertext. The key that opens them
is not in the repo: it lives in the state directory, and niwa seal-key backup
escrows a passphrase-protected copy at secrets/seal-key.age.
state/ is the only directory niwa writes into the repo by itself. Stamps
dirty the tree after every apply by design, so the dirtiness check that guards
apply --yes excludes them.
The state directory
None of this is committed, and none of it crosses to another machine.
~/.local/state/niwa
The journal stays local because the archives beside it hold your bytes.
Archives are pruned past ninety days. niwa uninstall leaves this directory
alone unless you add --purge.
Every verb that writes takes apply.lock: apply, undo, pull, add,
update and uninstall. A plain check takes it when it can, so it cannot
save a snapshot over a running apply. plan never takes it, and neither does
the watcher's check --notify, so a survey can never block an apply.
Shared data
niwa init writes the types and niwa uninstall removes them. A shared module
is stored under the hash the lockfile pins, so two machines resolving the same
niwa.use line read the same bytes.
Elsewhere on the machine
| Path | What is there |
|---|---|
~/.local/bin/niwa | the binary, where the installer puts it |
$ZDOTDIR/.zshrc, or ~/.zshrc | one PATH line, carrying the comment that lets uninstall remove exactly it |
~/Library/LaunchAgents/rs.niwa.watcher.plist | the watcher's launchd job |
~/Library/LaunchAgents/<label>.plist | one plist per niwa.service, named after its label |
~/Library/LaunchAgents/homebrew.mxcl.<name>.plist | read to check a niwa.brew.service |
~/Library/Preferences/ | user preference domains, and one of the watcher's two watch paths |
/Library/Preferences/ | the only place an absolute defaults domain may live |
/Library/Managed Preferences/ | read to find the keys an organization already governs |
Where the roots come from
| Root | Default | Moved by |
|---|---|---|
| home | — | HOME, which must be set and absolute or niwa stops |
| the config repo | ~/.config/niwa | XDG_CONFIG_HOME |
| the state directory | ~/.local/state/niwa | XDG_STATE_HOME |
| shared data | ~/.local/share | XDG_DATA_HOME |
| the Homebrew prefix | /opt/homebrew on Apple silicon, /usr/local otherwise | HOMEBREW_PREFIX |
An XDG variable is honored when it is set to an absolute path, and ignored
otherwise. Each one replaces only the leading half: XDG_STATE_HOME=/tmp/x
puts the journal at /tmp/x/niwa/journal.json.
Next
- File formats — it gives the exact shape of the lockfile, the journal and the stamp
- Environment variables — five of them move the roots this page names
- init — it is the verb that writes most of what is listed here
