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