From 022f6993ba94669902ea8361fd21a6266061459c Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sat, 15 Aug 2026 16:29:51 +0200 Subject: [PATCH] Plan the dots feature for preserving and syncing user configs A whitelisted bare git repo over $HOME driven only by constrained omarchy dots commands: local snapshot history at batch boundaries plus squash-published state sync across machines. Design survived adversarial review; rejected alternatives and rationale recorded in the plan. Co-Authored-By: Claude Fable 5 --- plans/dots.md | 206 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 plans/dots.md diff --git a/plans/dots.md b/plans/dots.md new file mode 100644 index 00000000..07bfab02 --- /dev/null +++ b/plans/dots.md @@ -0,0 +1,206 @@ +# Plan: Dots — preserve and sync user configs out of the box + +Revision 3. Rev 2 incorporated adversarial review by codex (xhigh) and grok; +rev 3 names the feature **dots** (`omarchy dots ...`) and adds the +multi-machine sync design. + +## Problem + +Omarchy declares `~/.config` "your files" but does little to preserve them: + +- `omarchy-refresh-config` litters `*.bak.` files next to originals. +- `omarchy-reinstall-configs` clobbers everything back to `/etc/skel` defaults. +- Snapper only snapshots the `root` config; recovering one config file from a + root snapshot is not a workflow, and `/home` may not be covered at all. +- The manual punts to a YouTube video about Stow. + +There is no way to answer "what did the last update change about my configs?", +"restore my bindings from last week," or "make my new laptop feel like my +desktop." + +## Rejected approaches + +- **`git init ~/.config`**: dumping ground — Chromium profile, fcitx5 state, + app tokens, machine churn. A `.git` there gets discovered by editors and + prompts. Can't cover `~/.bashrc` or `~/.XCompose`. +- **`~/.config/omarchy` only**: too narrow; misses hypr, terminals, `.bashrc`. +- **Stow**: inverted model requiring file migration; organization, not history. +- **chezmoi / yadm**: third-party DSLs we'd be wrapping; overkill. +- **Raw git passthrough / lazygit over `$HOME`** (rejected in review): + `git clean -fd`, `reset --hard`, or "stage all" against a `$HOME` work tree + is a home-directory-eraser; `git add -A` can ingest `~/.ssh` and the object + store into itself (`status.showUntrackedFiles no` only affects `status`); + `remote add` + `push` silently defeats local-only. Experts can construct the + raw invocation themselves; Omarchy will not bless it. +- **Distributed git across machines**: two machines auto-committing timer and + update snapshots into a shared branch conflict constantly. History and sync + are different products (see Sync below). + +## Chosen design: bare repo over `$HOME`, driven only by constrained commands + +```bash +git init --bare ~/.local/share/omarchy/dots.git # mode 0700 +``` + +No `.git` in any directory tools walk; files stay plain files in place. All +access goes through one internal helper that runs git **hermetically**: + +- Repo-local config only: synthetic identity (`Omarchy `), + `commit.gpgsign=false`, `core.hooksPath` disabled, `--no-verify`, + `GIT_CONFIG_GLOBAL=/dev/null` so user signing/hooks/templates/excludes and a + `$HOME/.gitignore` can never break or intercept an automatic commit. +- Never export `GIT_DIR`/`GIT_WORK_TREE` (would leak into `omarchy-plugin-*`, + `omarchy-theme-update`, `omarchy-update-dev`, user hooks). +- Serialized via a lock; concurrent refresh/update/manual snapshots queue. +- **Best-effort everywhere**: a failed snapshot warns and continues. History + is optional; updates are not. Never on the failure path of + `omarchy-update`/`omarchy-migrate` (`set -e` there must not see git errors). + +### Audited manifest, not a derived whitelist + +Tracking is `git add -f --pathspec-from-file=` only. The manifest is +a hand-audited file shipped with Omarchy — explicitly **not** derived from +`$OMARCHY_PATH/config` (which ships Chromium Preferences, fcitx5, +`opencode/opencode.json` where users put API keys, etc.): + +- Include: `.config/hypr/*.{lua,conf}`, `.config/omarchy/shell.json`, + `.config/omarchy/extensions/**`, `.config/omarchy/hooks/**`, + terminal configs (alacritty/foot/ghostty/kitty), `.config/btop/btop.conf`, + `.config/starship.toml`, `.bashrc`, `.XCompose`. +- Exclude (deliberately): `.config/omarchy/plugins/**` and + `.config/omarchy/themes/**` (nested git clones managed by + `omarchy-plugin-*` / `omarchy-theme-update`), backgrounds (multi-MB + binaries), `.config/btop/themes/` (symlink into `~/.local/state`), anything + Chromium/fcitx5/opencode/xournalpp. +- **Two tiers**: every manifest path is history-tracked, but some are marked + `local` — machine-specific files that sync must never touch. At minimum + `monitors.lua`; likely hardware quirks like `dell-haptic.conf`. This is the + lightweight answer to chezmoi's hostname templates: exclude, no DSL. +- Once committed, a secret lives in the object store forever; "no remote" is + not a secrets story. The manifest is the deny line — nothing outside it is + ever staged, and there is no command that stages more. + +### Dotfile-manager detection: stand down, don't fight + +`manual/31-dotfiles.md` sends users to Stow today, and migrations deliberately +write *through* symlinks. For those users git records an unchanged symlink, so +snapshots would be silent no-ops — and `restore` could stomp a symlink with a +regular file. Therefore: + +- At seed time and before each snapshot: if manifest paths are symlinks or a + known manager (yadm, chezmoi, existing bare-repo alias, `~/.git`) is + detected, mark the repo dormant and say so in `omarchy dots status`. +- `restore` refuses type changes (symlink↔file) and paths outside the + manifest. +- These users keep `.bak` behavior, which genuinely works for them (copies + content, not links). + +### Snapshot points: batch boundaries, before *and* after + +The unit is a labeled snapshot pair around each *batch* mutation, via an +internal `omarchy-dots-snapshot "