docs: design for multi-role dotfiles
The repository stopped matching reality in April 2021. It describes an i3/sway desktop that no longer exists - the display menu drives xrandr outputs the machine does not have - while .zshrc alone drifted by 360 lines. Checking out master would overwrite five years of work rather than update anything. Three machines, no shared base: beastix (Arch/KDE), shron (Arch, headless), a Mac. Common .zshrc lines between all three: four. Keeps the existing bare-repo-and-alias method. The role is resolved when the shell starts, not when files are deployed, so no deployment tooling is needed at all; every machine carries every file and sources one of them. Package lists are derived from usage data - shell history, the KDE activity database, autostart, flatpak - because each signal alone has a blind spot and pacman -Qqe lists 512 packages including years of experiments. That yields 62. Plain files and plain lists throughout, so a later move to home-manager stays cheap. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
cd6dd42178
commit
94b6ba3c33
@@ -0,0 +1,162 @@
|
||||
# Multi-role dotfiles — design
|
||||
|
||||
2026-08-06
|
||||
|
||||
## Problem
|
||||
|
||||
The repository stopped tracking reality in April 2021. It describes an i3/sway
|
||||
desktop that no longer exists: beastix runs KDE on X11, and the `xrandr` calls
|
||||
in the old display menu address outputs (`DVI-D-0`, `HDMI-0`, `DP-0`..`DP-5`)
|
||||
that the machine does not have. Meanwhile the live files kept evolving —
|
||||
`.zshrc` alone differs by 360 lines.
|
||||
|
||||
A `config checkout` of the current master would therefore not update a machine,
|
||||
it would overwrite five years of work.
|
||||
|
||||
Three machines exist and none of them shares a base with the others:
|
||||
|
||||
| | beastix | shron | Mac |
|
||||
|---|---|---|---|
|
||||
| Platform | Arch, KDE/X11 | Arch, headless | macOS 26.5.2, arm64 |
|
||||
| Prompt | oh-my-zsh + powerlevel10k | oh-my-zsh | oh-my-zsh |
|
||||
| `.zshrc` | 240 lines | 159 | 218 |
|
||||
| Packages | 512 explicit | 106 | 34 leaves + 36 casks |
|
||||
|
||||
Shared `.zshrc` lines: beastix↔Mac 51, beastix↔shron 6, all three 4.
|
||||
|
||||
The Mac config was clearly copied from the desktop at some point; shron is the
|
||||
outlier.
|
||||
|
||||
## Decisions
|
||||
|
||||
**Start from the live state, not from master.** The 2021 content is preserved
|
||||
on a branch. Nothing of value is lost: every keybinding unique to the old i3
|
||||
config is either present under a different key (`$mod+f` → `$mod+z`), available
|
||||
in another form (the system menu is now an i3-nagbar), or dead (the display
|
||||
menu targets outputs that do not exist).
|
||||
|
||||
**The base is not the intersection.** Four common lines would be a worthless
|
||||
base. The base is the good part of the desktop configuration; the server role
|
||||
leaves out what it does not need. Defining it the other way round lets the
|
||||
least-equipped machine set the standard for all of them.
|
||||
|
||||
**Keep the bare repo and the alias.** The existing method already works:
|
||||
|
||||
```sh
|
||||
alias config='/usr/bin/git --git-dir=$HOME/.cfg/ --work-tree=$HOME'
|
||||
```
|
||||
|
||||
No stow, no wrapper tool. The earlier objection to this approach — that
|
||||
`git status` drowns in untracked files and that roles need branches — is wrong
|
||||
on both counts: `status.showUntrackedFiles no` handles the first, and the
|
||||
second only applies if the role is resolved at deploy time.
|
||||
|
||||
**Resolve the role at runtime, not at deploy time.** Every machine checks out
|
||||
every file; which one gets sourced is decided when the shell starts. Dotfiles
|
||||
are kilobytes, so carrying an unused i3 config on the server costs nothing and
|
||||
removes the need for any deployment tooling.
|
||||
|
||||
**Stay portable to Nix.** NixOS is the likely long-term direction. Plain files
|
||||
and plain package lists map onto `home-manager` and `environment.systemPackages`
|
||||
almost unchanged; chezmoi templates would not. This costs nothing today.
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
.zshrc stub: source rc.d, then the role file
|
||||
.config/zsh/rc.d/
|
||||
10-aliases.zsh base, everywhere
|
||||
20-functions.zsh
|
||||
30-path.zsh
|
||||
50-desktop.zsh exactly one of these is sourced
|
||||
50-server.zsh
|
||||
50-mobile.zsh
|
||||
90-local.zsh machine-local, gitignored, sourced last
|
||||
.config/dotconfs/role one word, gitignored
|
||||
packages/
|
||||
arch-base.txt arch-desktop.txt arch-server.txt
|
||||
brew-mobile.txt brew-cask-mobile.txt
|
||||
flatpak-desktop.txt
|
||||
.scripts/
|
||||
packages.sh check | install
|
||||
curate.sh derive candidates from usage data
|
||||
```
|
||||
|
||||
The stub:
|
||||
|
||||
```sh
|
||||
ROLE=$(cat ~/.config/dotconfs/role 2>/dev/null || echo desktop)
|
||||
for f in ~/.config/zsh/rc.d/[0-4]*.zsh; do source "$f"; done
|
||||
source ~/.config/zsh/rc.d/50-$ROLE.zsh
|
||||
[ -r ~/.config/zsh/rc.d/90-local.zsh ] && source ~/.config/zsh/rc.d/90-local.zsh
|
||||
```
|
||||
|
||||
`90-local.zsh` is sourced last on purpose: it can override anything without
|
||||
touching the repository.
|
||||
|
||||
## Prompt
|
||||
|
||||
powerlevel10k on all three, same segments, colour by role:
|
||||
|
||||
- desktop — blue
|
||||
- mobile — green
|
||||
- server — **red**
|
||||
|
||||
This is a safety measure, not decoration. shron carries mail, web and cloud. A
|
||||
red prompt is the cheapest effective guard against running a command in the
|
||||
wrong terminal.
|
||||
|
||||
## Packages
|
||||
|
||||
Package selection is derived from usage data rather than from
|
||||
`pacman -Qqe`, which lists 512 packages on beastix and includes years of
|
||||
one-off experiments. Four signals, because each alone has a blind spot:
|
||||
|
||||
1. **Shell history** (10332 entries) — covers CLI tools, blind to anything
|
||||
started from the desktop menu.
|
||||
2. **KDE activity database** (`kactivitymanagerd/resources/database`) — covers
|
||||
applications that open documents, blind to chat clients.
|
||||
3. **Autostart and running processes** — covers the daily drivers
|
||||
(Signal, Discord, Thunderbird, KeePassXC, Nextcloud).
|
||||
4. **`flatpak list`** — these never appear in `pacman -Qqe` at all.
|
||||
|
||||
Combined, this reduces 512 to 62 candidates. `curate.sh` stays in the
|
||||
repository so the list can be regenerated rather than maintained by hand.
|
||||
|
||||
`packages.sh check` reports drift in both directions. `packages.sh install`
|
||||
adds what is missing. **Nothing is ever removed automatically** — individual
|
||||
packages on shron carry mail and web services, and a tool that tidies up on its
|
||||
own is a tool that eventually takes the mail server down.
|
||||
|
||||
## Secrets
|
||||
|
||||
The existing `mailsecrets` pattern becomes the rule: credentials live in a
|
||||
separate file that is imported and never committed. `.gitignore` covers
|
||||
`mailsecrets.py`, `.netrc`, `.pgpass`, `*.key`, `*.pem`, `id_*` (public keys
|
||||
excepted), the role file and `90-local.zsh`.
|
||||
|
||||
`config add -f` still bypasses this. The ignore list guards against
|
||||
carelessness, not against intent.
|
||||
|
||||
## Rollout order
|
||||
|
||||
`config checkout` overwrites existing files, so the sequence matters.
|
||||
|
||||
1. **beastix** — richest configuration, becomes the source. Split `.zshrc` into
|
||||
stub and `rc.d` fragments, write the role file, commit, push.
|
||||
2. **shron** — clone, checkout with conflicts moved to `.config-backup` as the
|
||||
existing installer already does. Then review its 57 unique lines: what
|
||||
belongs in `50-server.zsh`, what is obsolete.
|
||||
3. **Mac** — same, role `mobile`. The 51 lines it shares with beastix are
|
||||
already covered by the base.
|
||||
|
||||
The role file is written **before** the first shell start on each machine.
|
||||
Otherwise the default applies and shron comes up with a desktop prompt.
|
||||
|
||||
`.config-backup` stays until the result is confirmed. Nothing is deleted.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- Migration to NixOS. The design keeps the path open; it does not take it.
|
||||
- macOS `defaults` (Finder, keyboard) — deferred until the shell layer works.
|
||||
- Removing packages anywhere.
|
||||
Reference in New Issue
Block a user