The three-layer model
Every piece of configuration in the system lives in exactly one of three layers. The layer you put something in is decided by one question: what does it change with?
The layers
| Layer | Lives in | Owns |
|---|---|---|
| Core | dotfiles-core, vendored into every OS repo’s core/ | zsh modules, tmux, git, starship, and the editor it vendors in |
| OS-native | dotfiles-{MacBook,Fedora,Arch,Debian,openSUSE,Alpine,Gentoo,NixOS} | package manager, clipboard, paths |
| Role | dotfiles-Offense, dotfiles-Defense | offensive / defensive tooling on the OS layer |
| Native host | dotfiles-Windows | the Windows host: pwsh, Terminal, the WSL bridge |
| Editor | dotfiles-nvim, vendored into Core’s nvim/ and into dotfiles-Windows | the Neovim config |
dotfiles-Windows is the model’s one named exception rather than a fourth layer: it vendors no
Core at all, replicating it natively in PowerShell, so it sits beside the three rather than inside
the OS-native row.
dotfiles-nvim is the other exception, and its arrow points the other way. The editor is still
identical on every machine, so it ships as part of Core. But it is authored in dotfiles-nvim,
whose gate can do what Core’s cannot: start a real Neovim, install the pinned plugins and run
:checkhealth. Core vendors a tagged release into nvim/, pinned by nvim.lock, and a hand-edit
there fails Core’s audit. Editor changes go upstream, and the pin moves with a Core release.
dotfiles-Windows vendors the same release through its own nvim.lock.
The rule for where a change belongs
A change belongs in Core only if it is identical on every machine, not OS-specific, and not role-specific. Concretely:
- Changes with the OS → the OS repo. Anything that differs by package manager, clipboard backend, or filesystem path is OS-native, not Core.
- Changes with the operator’s role → the role repo. Offensive engagement tooling belongs in
dotfiles-Offense; defensive detection tooling indotfiles-Defense. - Everything else that’s truly universal → Core.
Core is authored once and vendored into each OS repo, so a defect in Core fans out to all of them at once. That leverage is the whole point — and the reason Core changes go through a single audit gate before they ship. See Vendoring Core for how the fan-out works.
Why split it this way
The alternative — one repo with per-OS conditionals — collapses under its own weight as the fleet grows: the universal parts and the host-specific parts tangle together until no one can say what is shared and what is not. The layer split keeps that boundary explicit and auditable: each OS repo is a clean, self-contained, public artifact, and Core stays plain and OS-agnostic.
It is deliberately more structure than a single-machine setup needs. If you have one or two boxes
with no real OS spread, a bare $HOME git repo or GNU stow is far less ceremony — the layer model
earns its keep only across a genuine multi-OS fleet.