Supersedes the old flat .config/ layout (last published 2026-06-28) with the private repo's structure: one shared base plus per-host overlays. - packages: common/ gui/ lw/ fl/ wm/ plus install.sh and bin/ tooling (dotsync, reconcile-hyde.sh) - new README covering the layout, deploy order and the HyDE dependency - current HyDE waybar rig (layouts/, cava), pi agent extensions, claude/ config, tmux, presenterm, aichat roles - fish: kp (keepassxc-cli + fzf picker, db path from $KP_DB) and bind_M_n_history (alt+1..9 recalls the nth history entry) - drops cruft that should never have been tracked: the duplicate top-level .pi/ copy, btop.log, zellij config.kdl.bak, fish_variables - .pi/agent/auth.json is gitignored; auth.json.example ships instead Host-specific work sessions and the personal backlog stay in the private tree. Endpoint locators in the llamacpp/whisper guides are placeholders ($SERVER, <own-domain>) — the guides themselves stay, since they are the useful part.
dots
Unified dotfiles for all machines: one shared base + per-host overlays, deployed with GNU Stow.
Layout
Every top-level directory is a Stow package (they all share one stow dir, which --override
requires). bin/ holds repo tooling and is not a package (never stowed):
common/ # shared everywhere (mirrors $HOME): fish, nvim, bat, btop, tmux, pi, claude, presenterm, ...
gui/ # GUI configs shared by graphical hosts: hypr, waybar, kitty, alacritty, mpv, zellij
wm/ # desktop: 4K monitors, NVIDIA, cloud LLM (duskadiy), work aliases
lw/ # laptop: no NVIDIA, roaming VPNs, network/LLM tools
fl/ # KDE Plasma box: a few Hyprland overrides + llamacpp, evremap
bin/ # tooling (not a stow package): install.sh companions
install.sh
A headless (terminal-only) machine has no overlay package — it runs common only. Mark any box
headless with touch ~/.config/dots-headless (a machine-local, hostname-independent marker); it then
skips gui too, so the same terminal-only setup replicates across any number of machines. In other
words common is the headless baseline; gui and the host packages layer graphical machines on top.
How it deploys
Deploy order is common → gui → <host>; later layers win (the host package is stowed last with
--override, so a host file replaces the common/gui file at the same path). Stow runs with
--no-folding, so each file is symlinked individually (directories stay real) — this is what lets
a host override a single file in a shared dir without shadowing the rest.
Editing a config edits the repo copy directly (it's a symlink) — no deploy step. You only re-run
install.sh when files are added or removed (to create/prune symlinks). See Syncing below.
The repo must be cloned to ~/.dots (the relative ../.dots symlink convention). A host's
package name must match hostname -s (or pass the host explicitly, e.g. ./install.sh lw).
Install (new machine)
sudo pacman -S stow
git clone <remote> ~/.dots
cd ~/.dots
./install.sh # auto-detects host via `hostname -s`
./install.sh -h lists all options: -n dry-run, -a adopt (first run), -D unstow (revert),
-h help. GUI hosts also need HyDE installed first — see HyDE dependency below.
Once per machine afterwards: bat cache --build (registers the repo's custom bat themes —
without it every bat run warns Unknown theme 'onedark'), and :Lazy restore inside nvim
(restore obeys the shared lazy-lock.json pins; sync/update would rewrite them).
nvim's treesitter (main branch) builds parsers via the tree-sitter CLI — pacman -S tree-sitter-cli first, or :TSInstallAll fails with ENOENT ... 'tree-sitter'.
First run on a machine that still has real config files
Stow aborts the whole package on the first conflict (one real file blocks everything in
common), and refuses to clobber real files without --adopt — you'll see
cannot stow … since neither a link nor a directory. Adopt them, review, then keep the repo's
versions:
cd ~/.dots
git status # make sure the repo is clean first (see the restore caveat)
./install.sh -a <host> # --adopt: pull existing files into the repo, symlink them back
git diff --stat # what this machine had vs the repo (review the drift!)
git restore . # discard adopted content, keep the repo versions
--adopt overwrites the repo copies with the machine's files, so git restore . is what keeps the
repo versions. Cautions: adopted changes can land under common//gui/ even when host-specific, and
git restore . drops all uncommitted edits — so commit/stash anything you want first. To keep a
genuine machine change (e.g. custom pyradio stations.csv), hand-copy it into the right package
instead of restoring that path.
Syncing (day-to-day)
Because configs are symlinks into the repo, you almost never run install.sh to sync:
- Pull/propagate edits:
cd ~/.dots && git pull— changes are live in place instantly. No re-stow, so no symlink churn and no app reloads (kitty etc. keep runtime state). bin/dotsyncdoes this safely:git pull, then re-link viainstall.shonly if the pull added/removed/renamed files (pure edits skip the re-stow).dotsync -npreviews incoming changes.install.shis a (re)deploy tool, not a sync tool — itsstow -Rdeletes+recreates every symlink, which is what makes apps reload. Only needed for the initial link or when files are added/removed.- Never
stowthe repo root (stow .dots,stow */) — it folds package dirs into~as stray~/common,~/gui, … symlinks.install.shonly ever links package contents. - New host: create a top-level
<name>/package (at least ahost.fish), commit, then./install.sh. If it's terminal-only, skip the package and justtouch ~/.config/dots-headless(it runscommononly).
Tooling (bin/)
| Command | What it does |
|---|---|
./install.sh [host] |
Deploy common → gui → <host>. Flags: -n dry-run, -a adopt (first run), -D/--unstow revert (remove this host's symlinks; repo + real files kept), -h help. Self-heals: headless hosts get any stray gui unstowed; fails loudly (exit 1) if a package couldn't stow. |
./bin/dotsync [-n] |
Sync with least churn: git pull, re-stow only if files were added/removed. |
./bin/reconcile-hyde.sh [-i|--relink] [host] |
After a HyDE update overwrote ~/.config/hypr, reconcile HyDE's new files against your dots versions (report / -i keep·take·merge each / --relink re-symlink). |
How per-host differences are expressed
| Difference | Mechanism |
|---|---|
| Monitors | <h>/.config/hypr/monitors.conf (sourced by the shared hyprland.conf) |
| NVIDIA | wm/.config/hypr/nvidia.conf, pulled in by wm's host.conf (fl/lw have no NVIDIA) |
| Shell aliases | shared config.fish sources ~/.config/fish/host.fish (per host) |
| Bash extras | shared .bashrc sources ~/.bashrc.host (optional, per host) |
| fl's Hyprland | overrides ~9 hypr files (hyprland.conf, keybinds, windowrules, theme colors, monitors); inherits the rest from gui |
| Unique apps | in the owning host overlay (fl: llamacpp/evremap; lw: xremap) |
aichat, pi, and opencode are single shared configs in common (LAN-first, with the cloud
provider also listed so any host can switch) — no per-host overrides.
HyDE dependency (important for GUI hosts)
The Hyprland config is HyDE-based. gui/.config/hypr/* is
your customization layer, but it sources HyDE's framework:
source = ~/.local/share/hyde/hyprland.conf — which defines $mainMod and other variables and is
not tracked by dots (HyDE generates it). So a GUI host must have HyDE installed, or Hyprland
fails to start (source globbing error + invalid mod $mainMod).
- Install/update HyDE with its own installer (
~/HyDE/Scripts/install.sh); it also pulls the required apps (xdg-desktop-portal-hyprland,xdg-desktop-portal-gtk,hyprpolkitagent). - HyDE's
-r/restore overwrites~/.config/hypr— usebin/reconcile-hyde.shafterward to fold its new defaults back against your dots configs. - Wallpapers and generated themes are HyDE-owned, never in dots (
**/hyde/themes/and**/wallpapers/are gitignored — they once bloated a source repo to 255 MB). A fresh machine gets them from HyDE, not from this repo.
Waybar (two worlds: HyDE-generated vs the standalone rig)
HyDE owns ~/.config/waybar/config.jsonc AND style.css — its waybar.py writes both on
any --set/--update, through the stow symlinks, i.e. into this repo's checkout. So dots
track neither. HyDE-managed customization goes via:
gui/.config/waybar/layouts/*.jsonc— dots-tracked layouts; switch withhyde-shell waybar --set <name>.gui/.config/waybar/modules/*.jsonc— module defs, globbed into the generated config.
The custom cava rig is NOT HyDE-managed: layouts/custom.jsonc + layouts/custom.css form a
self-contained two-bar config launched raw (waybar -c …custom.jsonc -s …custom.css — exec-once,
the Ctrl+Alt+W / Super+Shift+R binds, and the wbar abbr). Never route it through
hyde-shell waybar --set — waybar.py would clobber the repo's files as above. HyDE only reads
from layouts/, which is what makes them safe to track. On a rig host, HyDE's own bar (systemd
user unit hyde-<XDG_SESSION_DESKTOP>-bar.service, auto-restarting) must be disabled once:
systemctl --user stop <unit>; systemctl --user mask <unit>.
Note: the Arch waybar build has no native cava module — the rig uses the script module
custom/cava running cava-waybar.sh (self-contained, no HyDE dependency).
Secrets
Never committed. Put DUSKADIY_API_KEY etc. in ~/.config/fish/conf.d/secrets.fish (gitignored).
pi's auth.json is not tracked — it only ever holds $VAR references, but pi's /login
rewrites it with the literal key, so one stray /login would commit a real token. A new host
bootstraps it with cp common/.pi/agent/auth.json.example common/.pi/agent/auth.json, then
./install.sh. Also gitignored: fish_variables,
greet.txt, pi agent runtime/state, tmux/plugins/, .claude/ (Claude Code project dir/worktrees),
*.bak/*.log.
Merge notes (history — the one-time unification)
The four former per-machine repos had drifted; the merge made some judgment calls:
config.fish/.bashrcwere hand-reconciled into a neutral shared base + per-hosthost.fish/.bashrc.host(removed a committedfnm_multishellsruntime PATH, stray absolute home-dir paths, duplicate git aliases).pilives at~/.pi(repo.pi/);tmuxat~/.config/tmux/(delete a legacy~/.tmux.confif present after migrating).- Appearance drift was unified to one canonical (mostly wm): bat, btop, cava, fastfetch, lazygit, mpv, pyradio, waybar, zellij, most of nvim.
- nvim is shared entirely (no host overrides); plugin set /
lazy-lock.jsonare shared — run:Lazy restoreper machine once (obeys the lockfile pins —:Lazy updateon one machine + commit is how versions move forward; other machines converge withrestore). zellijis one shared config with vim keybinds (h/j/k/l) + theguvbox-darktheme.misc/{clis,tuis}.txtlauncher inventories use wm's lists incommon; a machine needing its own can override in its overlay.
Third-party content and licensing
The repo is GPL-3.0 (LICENSE). What that does and doesn't cover:
hypr/,waybar/and the other desktop configs are derived from HyDE (and, further back, legacy Hyprdots). HyDE is itself GPL-3.0, so the derivation and this repo's licence agree — no conflict, and the Hyprdots-era ASCII banners in those files are left in place as attribution.common/.config/aichat/dark.tmThemeis One Half Dark by Son A. Pham, MIT. The licence header at the top of that file is the attribution — don't strip it.common/.config/fastfetch/pngs/holds project and distro marks (Arch, the Open Source Initiative and Open Source Hardware logos, …). Trademarks aren't relicensable, so GPL-3 does not extend to them; they are bundled as nominative use, the way most dotfiles repos do.gui/.config/hypr/hyprlock/assets/— the lock screen background and logo. EXIF is stripped, so the files carry no provenance; confirm these are yours to redistribute before publishing, or swap them for something you know the origin of.