# homehub — operator notes Operational doc. **Why** each decision exists is in [`DESIGN.md`](DESIGN.md); this file is only the operational half. ``` ~/homehub/ bin/homehub the tool (~750 lines of bash + one python merge) config machine-specific settings (per-machine, not synced) rules/denylist DEFAULT DENY, emitted first, wins over everything rules/sets.d/*.list named path-lists ("sets") rules/hosts/ which sets this machine joins lib/merge_bookmarks.py class C union-merge (jq + python3 only) systemd/homehub-*.{service,timer} DESIGN.md rationale for everything below ``` ## Install (per machine) ```sh git clone https://gitea.lue.dk/druppy/homehub.git ~/homehub && cd ~/homehub cp rules/hosts/_laptop-template rules/hosts/$(hostname -s) # pick your sets ./bin/homehub setup-key # once, see warning below ssh-copy-id -i ~/.ssh/homehub_ed25519.pub nas.lue.dk # YOU run this ./bin/homehub setup ./bin/homehub sync # DRY: prints what would move, writes nothing homehub sync --exec # first real run sudo loginctl enable-linger $USER # headless boxes only ~/homehub/bin/homehub install # systemd user timers ``` `homehub install` copies the units into `~/.config/systemd/user` and enables `homehub-sync.timer` (10 min) and `homehub-fresh.timer` (30 min). ## Commands | Command | What | |---|---| | `sync [--exec]` | pull → apply deletions → harvest deletions → push. **Dry unless `--exec`.** | | `status` | last sync per machine, sets, pending dry-run lists, repo manifest, bookmark exports | | `fresh [--dry]` | the 24 h alert check; only printer of alerts | | `git inventory\|list\|missing\|clone [--exec] [--lazy]` | class B | | `bookmarks [--write]` | class C export + union merge | | `forget ` | drop tombstones so a deleted path can return | | `filter` | dump the generated rsync filter | | `setup`, `setup-key`, `install` | one-time plumbing | ## The rules that matter when editing config 1. **Default deny.** `rules/denylist` is emitted first, then the host's set includes, then `- *`. A path in a set that sits under a deny rule can never sync — the tool warns (`shadowed: ...`) when you write one. 2. **Set syntax.** One home-relative path per line; trailing `/` = subtree. `/Documents/` → `- /Documents/***`; `build/` (no leading slash) matches at any depth. 3. **Never add a git repo path to a set.** Repos are class B (`homehub git inventory`). 4. **`~/.config` is unreachable by design** (it is denied wholesale). State you want shared goes under `~/.local/state/shared//`, with the real config path symlinked to it. `~/.local/state/homehub/` is homehub's *private* scratch and is denied too — syncing it would make machines overwrite each other's generated filters forever. 5. **Deletions never propagate as `--delete`.** They become tombstones on the hub, are reaped after `GRACE_DAYS`, and always lose to a competing edit. ## Alerting `homehub fresh` alerts only when a machine is **on** (heartbeat < 3 h) but has not synced for > 24 h, and never for network-class rsync/ssh exit codes — so a laptop away for three weeks is silent indefinitely. Delivery auto-detects `msmtp`, then `sendmail`, else appends to `~/.local/state/homehub/alerts.out`. To wire it to your own mail server: ```sh # /etc/msmtprc (or ~/.config/msmtp/config) defaults tls on tls_starttls on account homehub host mail.lue.dk port 587 auth on user homehub@lue.dk from homehub@lue.dk password ``` Then `msmtp -a homehub you@lue.dk` is picked up automatically. On Debian: `apt install msmtp msmtp-mta`. A machine whose **own** config is broken (ssh key revoked, hub disk full) records a local marker in `~/.local/state/homehub/broken.*` and alerts about itself even with the hub unreachable — otherwise a dead key would produce no heartbeat, no marker, and therefore no alert, forever. ## Known limits (deliberate, not bugs) * **mtime is the arbiter.** Two machines editing the same file while both offline resolve by mtime, so clocks must be NTP-synced (`timedatectl` on Debian, `ntpd` on NixOS). The overwritten copy is kept in `/.hub/prev/YYYY-MM/`. * **rsync 3.1.2 on DSM 6/7** — no `--ignore-older`; the design uses `--update` only. * `--checksum` is off, so a file edited to the same size in the same second as a previous sync may be missed. Re-run `rsync -mc` by hand if you ever suspect it. * Unpushed commits are advisory in the manifest and never transferred. A dead laptop disk loses them. * Empty bookmark folders do not survive the union merge (there is nothing to key on). * Symlinks travel as symlinks (`-l`, never `-L`); ones pointing into `~/src`, `~/git` or `/tmp` will dangle on a machine that has not cloned the target. ## Troubleshooting ```sh ~/homehub/bin/homehub status # ages, sets, manifest journalctl --user -u homehub-sync.service -n 50 cat ~/.local/state/homehub/{pull,push}.err cat ~/.local/state/homehub/broken.* # self-detected breakage ssh nas.lue.dk 'cat /volume1/homes/bl/homehub/.hub/log.tsv | tail' ~/homehub/bin/homehub filter | grep -n shadowed ```