# dotconfs Configuration for three machines out of one repository: an Arch desktop, an Arch server and a Mac. The role is decided when the shell starts, not when files are deployed. The bare-repository technique is from [an Atlassian post](https://developer.atlassian.com/blog/2016/02/best-way-to-store-dotfiles-git-bare-repo/): a git directory in `$HOME/.cfg` with `$HOME` as its work tree, driven by an alias. No symlinks, no extra tooling. ## Roles | Role | Machine | Prompt | |---|---|---| | `desktop` | beastix — Arch, KDE | powerlevel10k, blue | | `server` | shron — Arch, headless | powerlevel10k, ASCII, **red** | | `mobile` | the Mac — macOS, Apple Silicon | ys, green once p10k is there | Red on the server is a guard, not decoration. shron carries mail, web and cloud, and the prompt should answer "which machine is this" before the next command is typed. The role lives in `~/.config/dotconfs/role`, one word, and is **not** tracked — a shared copy would defeat the purpose. Write it before the first login. Without it the shell falls back to `desktop`. ## Layout ``` .zshrc stub: role, then oh-my-zsh, then the fragments .zprofile login shells only; Homebrew's shellenv .config/zsh/rc.d/ 05-pre-omz.zsh theme and plugins - must precede oh-my-zsh 10-aliases.zsh shared 20-functions.zsh shared 30-path.zsh shared 50-desktop.zsh exactly one of these is sourced 50-server.zsh 50-mobile.zsh 90-local.zsh machine-local, never committed .config/zsh/p10k-server.zsh the server's prompt, ASCII and font-agnostic packages/ one plain list per platform and role .scripts/packages.sh check | install .scripts/curate.sh derive a package list from usage ``` Every machine checks out every file. A KDE configuration on the server is a few inert kilobytes and costs less than the machinery needed to avoid it. ### Load order It matters, and getting it wrong fails quietly: 1. powerlevel10k instant prompt — must be first, it may print 2. role 3. `05-pre-omz.zsh` — **theme and plugins**, because oh-my-zsh consumes both while it is being sourced. Set afterwards, they are ignored: an early attempt reported `ZSH_THEME=powerlevel10k` from a shell running without it. 4. oh-my-zsh 5. `10-` to `40-` — shared 6. `50-.zsh` — **the prompt colour**, because `~/.p10k.zsh` assigns `POWERLEVEL9K_CONTEXT_BACKGROUND` itself and is sourced in `30-path.zsh` 7. `90-local.zsh` — last, so it can override anything Theme before oh-my-zsh, colour after `.p10k.zsh`. Two constraints pulling in opposite directions. ## Portability Anything that depends on a command is guarded by whether that command exists, rather than duplicated per role: ```sh (( $+commands[lsd] )) && alias ls='lsd' ``` Without the guard, `alias ls='lsd'` turns `ls` into a broken command on a machine without lsd. The Mac is such a machine. Plugins are assembled per role and then filtered by what is installed. Naming a plugin that is absent produces an oh-my-zsh warning at every single login. ## Packages Lists are plain names, one per line — data, not code, so they port to `home-manager` and `environment.systemPackages` almost unchanged. ``` .scripts/packages.sh what is missing, what is extra .scripts/packages.sh install install what is missing ``` **It never removes anything.** Extras are reported and left alone: on a server, individual packages carry mail and web services. `curate.sh` derives a candidate list from four usage signals, because each one alone is blind to something — shell history sees no GUI application, KDE's activity database sees no chat client, autostart sees only what starts by itself, and flatpaks never appear in `pacman -Qqe`. On beastix that turned 512 explicit packages into 62. ## Secrets Credentials live beside the code that reads them and are never committed. The waybar mail module imports from `mailsecrets.py`; `.gitignore` covers that, private keys, `.netrc`, `.pgpass`, and `.config/kdeconnect/`, which holds the device certificate. Machine-local overrides and anything sensitive belong in `.config/zsh/rc.d/90-local.zsh`, which is sourced last and never tracked. `config add -f` still bypasses all of it. The ignore list guards against carelessness, not intent. ## Setting up a new machine ```sh curl -Ls https://ls.shron.de/dotconf | /bin/bash ``` The installer clones into `$HOME/.cfg`, defines the alias and moves anything in the way to `.config-backup`. Write the role file before the first login: ```sh mkdir -p ~/.config/dotconfs && echo server > ~/.config/dotconfs/role ``` Then, by hand: ```sh config config --local status.showUntrackedFiles no config checkout master ``` > Earlier revisions of this file used `curl -Lks`. The `-k` disables > certificate verification against a host that has a valid certificate, > removing precisely the protection that matters when piping a remote script > into a shell. It is gone. ## Day to day ```sh config status config add .zshrc config commit -m "..." config push ``` `status.showUntrackedFiles=no` keeps `$HOME` from drowning the output. Servers pull over HTTPS rather than SSH: a machine that only consumes configuration has no business holding push credentials. ## History Everything before 2026-08-07 is on `archive/2021-i3-sway`. That tree described an i3/sway desktop that no longer exists — its display menu drove `xrandr` outputs the machine does not have — while the live files had moved on by years. `master` was rebuilt from the running configuration rather than merged with it.