Skip to content

Architecture and the Three Layer Model

Gerrrt edited this page Jun 14, 2026 · 1 revision

Architecture & the Three-Layer Model

dotfiles-core is the keystone of a nine-repo dotfiles system. The whole design exists to answer one question cleanly: where does a given piece of config belong? Everything follows from the three-layer model.

The three layers

Layer Lives in Examples
Core this repo, vendored into each OS repo via git subtree zsh modules, tmux base, nvim, git/delta
OS-native dotfiles-{MacBook,Windows,Debian,Fedora,Arch,openSUSE,Alpine,Gentoo} package manager, clipboard shim, paths
Role / offensive dotfiles-Kali engagement scaffolding, C2, Impacket, wordlists

The two-question test

Before any file lands in Core, run it through these two questions:

  1. Does it change when the operating system changes? (package manager, paths, clipboard backend) → it belongs in the OS repo.
  2. Does it change when you as an operator change? (C2, wordlists, engagement layouts) → it belongs in dotfiles-Kali.

If the answer to both is "no" — it is the same everywhere — then it is Core and lives here.

Why this exists: the drift problem

Previously each repo carried its own copy of Core, and drift between copies was caught after the fact with a core-diff.sh reconciliation script. That approach works at four repos. At nine it doesn't — you spend all your time doing N-way reconciliation.

This repo flips the model: Core is authored once, here, then pulled into each OS repo as a vendored core/ subtree. There is no more N-way reconciliation because there is only one authoritative copy.

How an OS repo consumes Core

Each machine repo (for example dotfiles-Fedora) vendors this repo under core/:

# one-time, inside the OS repo:
git subtree add --prefix=core https://github.com/<you>/dotfiles-core main --squash

That physically copies Core into core/ and commits it. The repo now clones and works with no submodule flags — important, since these are public showcase repos people will browse.

To update every OS repo after a Core change, run the loop helper from this repo:

./scripts/sync-core.sh          # subtree-pulls main into all 9 OS repos
./scripts/sync-core.sh --dry-run

The OS repo's bootstrap.sh then symlinks core/zsh/*.zsh, core/tmux/, core/nvim/, core/git/, core/starship/, core/mise/, and core/bin/ into place alongside its own OS-native files.

Why subtree (not submodule, not chezmoi)

This is the most-asked design question, so it's worth spelling out.

vs. submodule

A submodule stores a pointer (a commit SHA), so a fresh clone is empty until you run git submodule update --init. A subtree vendors the actual files, so every repo is self-contained and clone-and-go. For public portfolio repos people will browse, self-contained wins.

vs. chezmoi

chezmoi (one repo + per-OS templates) is the most DRY answer, and it is the right move if you ever want to collapse nine repos into one. It trades the nine-repo breadth-portfolio for minimalism. This system deliberately keeps the portfolio. Switching to chezmoi later would be a content migration, not a rewrite, because the Core files here are already plain and OS-agnostic.

The shipped-vs-tooling split

A subtle but important distinction: not everything in this repo is vendored out to the OS repos.

  • bin/shipped. The cross-OS clip / clip-paste clipboard shims are vendored into every OS repo (they're listed in core.manifest).
  • scripts/dev tooling. audit-core.sh, test-core.sh, bench-core.sh, sync-core.sh, etc. run the quality gate here and are never vendored out. They live in the audit's allowlist rather than the manifest.

The line is: if it runs on a target machine, it's bin/ and in the manifest; if it maintains this repo, it's scripts/ and in the allowlist.

The manifest is the contract

core.manifest is the canonical inventory of what Core ships. The audit (scripts/audit-core.sh) enforces it in both directions:

  • every path listed in the manifest must exist on disk, and
  • every tracked file must be either listed in the manifest or in the audit's repo-meta allowlist (docs, CI config, dev tooling).

Adding a new Core file means adding its path to core.manifest in the same change. See CONTRIBUTING.md for the full checklist.

The nine-repo map

  • Core — this repo. Authored once, vendored everywhere.
  • OS reposdotfiles-{MacBook, Windows, Debian, Fedora, Arch, openSUSE, Alpine, Gentoo}. Fedora is the Linux template; Arch, openSUSE, Alpine, and Gentoo are stamped from it (see PORTING-MATRIX.md). MacBook (Homebrew) and Windows are their own lineages.
  • Role repodotfiles-Kali. Debian-family, plus an offensive role layer that adds an offensive stage to the zsh loader.

See also

Clone this wiki locally