The system is built on one decision repeated consistently: draw a hard boundary
between what is identical on every machine and what changes with the OS or the
operator. Get the boundary right and a single Core can serve every machine with
zero special-casing.
One Core, vendored into 8 machine repos via
git subtree — each clone is self-contained (no submodule init).
dotfiles-Kali stacks a Role layer on its OS layer; dotfiles-Windows
carries no core/ subtree and mirrors only the Neovim and starship configs.
Edge colour tracks live vendoring drift — green = carrying Core
v2.6.0, orange = behind (run
a sync). Each repo's label shows the release it currently vendors.
How an OS repo consumes Core
Each machine repo (except Windows, which replicates Core natively in PowerShell)
vendors dotfiles-core under core/ as a
git subtree. That physically copies Core in and commits it, so the repo clones and
works with no submodule flags — important, since these are public showcase repos
people will browse.
# one-time, inside an OS repo:$ git subtree add --prefix=core https://github.com/dotgibson/dotfiles-core main --squash# later, to pull Core updates down:$ git subtree pull --prefix=core https://github.com/dotgibson/dotfiles-core main --squash
Maintainers fan a Core change out to every OS repo in one shot with
scripts/sync-core.sh, which prints the exact short SHA each repo
receives so a sync is traceable.
How it compares — and when it's the wrong tool
Every dotfiles approach is a trade. This one optimizes for clone-and-go
self-containment across many machines and a public, per-machine
portfolio — paying a little vendored duplication for it. Here's how that
lands against the usual alternatives, each of which wins in its own case.
vs git submodule
A submodule stores a pointer, so a fresh clone is empty until git submodule update --init — the classic "I cloned it and nothing works" footgun. Subtree vendors the actual files, so every machine repo is self-contained and clone-and-go. The cost subtree pays back: a vendored copy in each repo (kept honest by the sync script + a manifest audit).
vs chezmoi / yadm
One repo + per-OS templates is the most DRY answer, and the right move the day you want to collapse the whole fleet into one. This system keeps the multi-repo portfolio instead — each machine is a clean, public, self-contained artifact. Because Core is already plain and OS-agnostic, moving to chezmoi later is a content migration, not a rewrite.
vs GNU stow
stow is a perfect, zero-magic symlink farmer over a single repo — and genuinely simpler if you have one machine. What it has no opinion about is layering or per-OS divergence, so a multi-OS setup drifts into host branches and .stow-local-ignore gymnastics. This system bakes the Core / OS / Role split in and ships its own idempotent symlinker (with backups + a dry-run).
vs a bare $HOME git repo
The git --bare + alias trick is the leanest possible — no symlinks, files live in $HOME. Hard to beat solo. But it has no layer model, no per-OS story, noisy status against your whole home dir, and it is a real footgun on a shared or multi-user box. This trades that leanness for an explicit, auditable structure.
When to reach for something else
A system that can't name its own constraints isn't worth trusting. Don't use this if:
You have one or two machines with no real OS spread — this is over-engineered for that; a bare $HOME repo or stow is far less ceremony.
You want ONE repo, not a fleet — reach for chezmoi or yadm. (The migration is content, not a rewrite, because Core is already plain.)
You can’t stomach any vendored duplication — the multi-repo + vendored-Core model deliberately trades a little duplication for clone-and-go self-containment and a public per-machine portfolio.
The canonical load order
Load order is load-bearing. tools initializes atuin (registering its
widget), options runs compinit (fzf-tab + carapace need
it), and fzf defines its widgets beforeplugins
loads zsh-vi-mode, whose init fires the keybinding hook in bindings.
Every OS repo’s .zshrc sources the chain in this order:
01toolsdetection + single init point (zoxide/starship/atuin/mise) — loads first
Kali stacks three layers — Core, its apt OS layer, and an offensive role stage
(dotfiles-Defense mirrors this on the blue side). Where a plain OS
repo’s loader ends … os local, Kali inserts one more stage —
… os offensive local — for engagement scaffolding (scope-first
workspaces, an audit-trail logshell, NetExec/BloodHound CE helpers).
Engagement data never lives in the repo; it stays under
~/engagements, and every tool is for authorized engagements with
written rules of engagement only.
The macOS desktop layer
OS-native layers own more than packages and paths. On macOS,
dotfiles-MacBook commits a full tiling-desktop setup — window manager,
menu bar, and keyboard — themed to Core’s tokyonight palette so the GUI reads as an
extension of the terminal, not a separate world.
◳ AeroSpace
i3-style tiling window manager — pure TOML, no SIP disable required, no daemon. alt drives focus/move/resize and workspaces, apps auto-place on a home workspace, and JankyBorders rings the focused tile.
▭ SketchyBar
A programmable menu bar themed to tokyonight-storm, matching starship + tmux. Live AeroSpace workspaces on the left; CPU, memory, disk, network, and a click-to-toggle keep-awake on the right — every glyph from the one Nerd Font, no extra deps.
⌨ Karabiner
Caps→Ctrl/Esc, and a Tab-hyper layer that mirrors the WM verbs — hyper+hjkl to focus, hyper+1–5 for workspaces — so the whole tiling workflow stays on the home row.
Deep dives
The design decisions above are documented in full inside the repos. These are the
long-form references worth reading next: