Files
dots/README.md
T
2026-08-13 01:11:41 +02:00

197 lines
12 KiB
Markdown

# dots
Unified dotfiles for all machines: one shared base + per-host overlays, deployed with
[GNU Stow](https://www.gnu.org/software/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)
```fish
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:
```fish
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/dotsync`** does this safely: `git pull`, then re-link via `install.sh` **only if** the pull
added/removed/renamed files (pure edits skip the re-stow). `dotsync -n` previews incoming changes.
- **`install.sh` is a (re)deploy tool, not a sync tool** — its `stow -R` deletes+recreates every
symlink, which is what makes apps reload. Only needed for the initial link or when files are
added/removed.
- **Never `stow` the repo root** (`stow .dots`, `stow */`) — it folds package dirs into `~` as stray
`~/common`, `~/gui`, … symlinks. `install.sh` only ever links package *contents*.
- **New host:** create a top-level `<name>/` package (at least a `host.fish`), commit, then
`./install.sh`. If it's terminal-only, skip the package and just `touch ~/.config/dots-headless`
(it runs `common` only).
## 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](https://github.com/HyDE-Project/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` — use **`bin/reconcile-hyde.sh`** afterward 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 with `hyde-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` / `.bashrc`** were hand-reconciled into a neutral shared base + per-host
`host.fish` / `.bashrc.host` (removed a committed `fnm_multishells` runtime PATH, stray
absolute home-dir paths, duplicate git aliases).
- **`pi` lives at `~/.pi`** (repo `.pi/`); **`tmux` at `~/.config/tmux/`** (delete a legacy
`~/.tmux.conf` if 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.json` are shared — run
`:Lazy restore` per machine once (obeys the lockfile pins — `:Lazy update` on one machine +
commit is how versions move forward; other machines converge with `restore`).
- **`zellij`** is one shared config with **vim keybinds** (h/j/k/l) + the `guvbox-dark` theme.
- **`misc/{clis,tuis}.txt`** launcher inventories use wm's lists in `common`; 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](https://github.com/HyDE-Project/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.tmTheme`** is *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.