Architecture¶
How the dotfiles repository is organized and why.
Structure¶
dotfiles/
├── platforms/ # Platform configurations (what gets deployed)
│ ├── common/ # Shared configs (all platforms)
│ ├── macos/ # macOS-specific overrides
│ ├── wsl/ # WSL Ubuntu overrides
│ ├── arch/ # Arch Linux overrides
│ └── ubuntu/ # Ubuntu server overrides
├── apps/ # Personal CLI applications (shell scripts)
│ ├── common/ # Cross-platform: menu, notes, backmeup, safekeep, patterns, ...
│ ├── macos/ # macOS-specific tools
│ └── arch/ # Arch Linux-specific tools (rofi menus, etc.)
├── management/ # Repository management
│ ├── machines/ # Machine manifests (what to install per computer)
│ ├── shell/ # Modular shell aliases and functions (build source)
│ │ ├── aliases/ # Alias group files (core.sh, platform-*.sh)
│ │ └── functions/ # Function group files (core.sh, git.sh, fzf.sh, ...)
│ ├── symlinks/ # Symlinks manager (Python)
│ ├── common/ # Shared installers and libraries
│ ├── {platform}/ # Platform-specific install scripts
│ └── packages.yml # Package definitions
├── Taskfile.yml # Task automation
└── docs/ # MkDocs documentation
External tools (installed from GitHub, not in this repo):
sess,toolbox: Go apps viago install github.com/datapointchris/...theme,font: Bash tools cloned to~/.local/share/
Symlink System¶
Two-layer approach: common base + platform overlay.
How it works:
- Links
platforms/common/configs to$HOME - Overlays platform-specific files (auto-detected: macos, wsl, arch, or ubuntu)
- Links apps from
apps/{platform}/to~/.local/bin/ - Excludes generated shell files (aliases.sh, functions.sh) -- these are built by
build-shell.sh
Common commands:
task symlinks:link # Deploy all symlinks
task symlinks:check # Verify symlinks are correct
task symlinks:show # Show all symlinks
task symlinks:relink # Complete refresh (remove and recreate)
Example results:
platforms/common/.config/zsh/.zshrc→~/.config/zsh/.zshrcplatforms/macos/.gitconfig→~/.gitconfig(overrides common)apps/common/menu→~/.local/bin/menu
Package Management¶
System Packages: Homebrew (macOS), apt (Ubuntu/WSL), pacman (Arch)
Language Versions: uv (Python), nvm (Node.js)
Why separate: Version managers provide cross-platform consistency and project-specific versions without system conflicts.
Machine Manifests¶
Installation is driven by machine manifests in management/machines/. Each manifest defines exactly what gets installed:
# management/machines/arch-personal-workstation.yml
machine: arch-personal-workstation
platform: arch
function_groups: [core, git, python, aws, docker, network, reference, node, fzf]
alias_groups: [core]
system_packages: true
go: true
go_tools: [task, cheat, terraform-docs, ...]
github_releases: [fzf, neovim, lazygit, yazi, ...]
custom_installers: [bats, awscli, claude-code, ...]
rust: true
nvm: true
# ... etc
Run installation with: bash install.sh --machine arch-personal-workstation
Shell Build System¶
Shell aliases and functions are modular source files in management/shell/ that get concatenated into single output files based on the machine manifest:
- Source:
management/shell/aliases/*.shandmanagement/shell/functions/*.sh - Builder:
management/shell/build-shell.sh - Output:
~/.local/shell/aliases.shand~/.local/shell/functions.sh
The manifest function_groups and alias_groups fields control which modules are included. Platform-specific groups (e.g., platform-arch) are added automatically based on the platform field.
Platform Detection¶
Shell (platforms/common/.config/zsh/.zshrc):
if [[ "$OSTYPE" == "darwin"* ]]; then
# macOS
elif [[ -f /proc/version ]] && grep -q Microsoft /proc/version; then
# WSL
elif [[ -f /etc/arch-release ]]; then
# Arch
elif [[ -f /etc/lsb-release ]] && grep -q Ubuntu /etc/lsb-release; then
# Ubuntu
fi
Install script: Platform is read from the machine manifest via manifest_field "platform" rather than auto-detected.
Configuration Layers¶
Configurations use inheritance: shared base with platform overrides.
Example: Git Config
macOS (platforms/macos/.gitconfig):
WSL (platforms/wsl/.gitconfig):
[core]
editor = nvim
[credential]
helper = /mnt/c/Program\\ Files/Git/mingw64/bin/git-credential-wincred.exe
Example: Neovim
Common (platforms/common/.config/nvim/): Base LSP, core plugins, keybindings
Platform-specific (optional): AI plugins (CodeCompanion for macOS), platform LSP configs
Design Decisions¶
Symlinks over Stow: Custom tool provides better two-layer linking, clearer error messages, platform awareness.
Taskfile over Makefile: Cross-platform consistency, better syntax for complex commands, modular includes, self-documenting.
Version Managers for Languages: Same Node/Python versions across platforms, project-specific versions, no system conflicts.
Unified Theme System: The theme CLI generates consistent configs for ghostty, tmux, btop, and Neovim from a single theme.yml source file per theme.
Advantages¶
Minimal Duplication: Only platform differences exist in platform directories.
Clear Separation: platforms/common/ for shared, platform dirs for quirks only, apps/ for tools, management/ for repo tooling.
Easy Maintenance: Update shared config once, all platforms benefit.
Testable: Each platform can be tested independently with Docker containers.
Trade-offs¶
Symlink Complexity: Two-layer system adds complexity, but symlinks tool handles it with clear errors.
Platform Knowledge: Need to know whether to edit platforms/common/ or platform dir. Experience makes this clear.
See Platform Differences for platform-specific quirks.
Deep Dives¶
-
System vs language version managers
-
Tool precedence and environment setup
-
How tools work together