Most entries are a data-loss bug that was found in testing and must not come back: filter ordering, harvest-before-pull, seen-gated deletion attribution, edits-beat-deletes, POSIX sh on DSM, and the never-2>/dev/null-an-rsync rule. Symlinked as AGENTS.md so a future agent session loads it automatically.
119 lines
5.3 KiB
Markdown
119 lines
5.3 KiB
Markdown
# 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/<hostname> 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
|
|
.rules -> AGENTS.md invariants + gotchas for future changes (auto-loaded by agents)
|
|
```
|
|
|
|
## 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 <path>` | 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/<app>/`, 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 <app-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>/.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
|
|
```
|