From 94b6ba3c334edfb754c9cbaaf56ea7f441bee04c Mon Sep 17 00:00:00 2001 From: "thomas.kopp" Date: Thu, 6 Aug 2026 23:43:59 +0200 Subject: [PATCH] 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 --- .../dotconfs/docs/2026-08-06-roles-design.md | 162 ++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 .config/dotconfs/docs/2026-08-06-roles-design.md diff --git a/.config/dotconfs/docs/2026-08-06-roles-design.md b/.config/dotconfs/docs/2026-08-06-roles-design.md new file mode 100644 index 0000000..91c9754 --- /dev/null +++ b/.config/dotconfs/docs/2026-08-06-roles-design.md @@ -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.