night 2026-10-22: README (backup.volumes excluded, the capped second-drive copy), CONTEXT, REPORT — four fixes on main, not released (0.340.0 next)
gates / gates (push) Successful in 1m51s

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-10-22 18:11:45 +02:00
parent ae394670c1
commit df78b6caae
3 changed files with 43 additions and 20 deletions
+2
View File
@@ -1,5 +1,7 @@
# CONTEXT.md — Project Memory
> **2026-10-22 night — four fixes on `main`, NOT released (night brief: no release). The next release is 0.340.0, MinAgent 0.158.1.** R-49 (D21 = B): `.felhom.yml` `backup.volumes` class `excluded` (`6a17c95`); R-1187(c): an import over an installed app that is not running is refused (`d5f88a2`, reproduced and proven fixed on 9202); R-1189(d): `nightchain.CatchUp.After` runs the off-site proof + integrity once after a catch-up (`d5f88a2`); R-1214: the second-drive copy runs in a 512 MB memory-capped container for clean `/mnt/` paths with no symlink (`03c0a69`, `ae39467`). 9202 ran rc.1–rc.4 and is back on 0.339.0. rc tags pushed: 0.340.0-rc.1..rc.4 (never offered: not X.Y.Z).
> **2026-10-20 — v0.336.0 is the floor (MinAgent 0.158.1, 08:54 UTC).** R-893 option A (operator D20): a failed off-site restore
> copies the app's live volumes first and puts them back exactly, then starts the app (proven on 9202: schema, data and row-count
> md5 equal before and after a forced failure); R-1169 the held sentence. Follow-up row: the put-back's ERROR log level and an
+28 -20
View File
@@ -1,22 +1,30 @@
# REPORT — controller v0.339.0 released (2026-10-22 day brief)
# REPORT — night 2026-10-22: four fixes on `main`, not released
**What:** R-1199 reopened — a rebuilt box never overwrites the household's saved mail list; R-1216 (D28 = A) — the
install page's swap sentence only when host swap is really short. Design: `08-alarm-ladder.md` (the 2026-10-22
`[DESIGN]` paragraph), `03-host-agent.md` (guest swap), `05` (`customer_notifications`). **MinAgent 0.158.1**; needs hub
≥ 0.165.0 (deployed first, operator yes).
**What:** the night brief's controller parts. Nothing released (the brief forbids a controller release tonight).
Design read: `07-backup-architecture.md` §6.1.1 (catch-up), §6.2 (capture set), §10.2; `09` §6.5 (drill catalog).
Next release **0.340.0**, **MinAgent 0.158.1** (unchanged).
- **Found:** v0.338.0's start-up default push replaced demo-felhom's own 13-event list on the hub with the 17 defaults
(07:54:55 UTC) and would repeat at every start. Now a box with no list of its own reads the hub's: adopts a
household's (nothing pushed), pushes its default once only when no household saved one, sends nothing when the hub
cannot be read. Every push names its `source`. Red-proved against v0.338.0's rule.
- **Fixed without a row:** an empty saved list read back as the 17 defaults (`omitempty`; Tester 1 and demo-hp hold
that file shape) — now `[]` persists and a saved list with no events reads as empty; the add-only seeds are marked
done on a fresh settings file (they re-added a just-switched-off mail at a new box's second start). Red-proved.
- **R-1216:** `hostSwapRuleShort` = no host swap, or < 90 % of min(guest swap, 8192 MB); the hub's case table copied
byte-identical (`testdata/host_swap_rule_cases.json`, felhom.eu `host_swap_rule_gate.py`). Red-proved.
- **Release:** rc.1 on 9202 (`sha256:7d4c8852…`; no hub link there — start-up and pages only: healthy, 0 errors,
`/login` 200). `0.339.0` from `fc5a20f` (CI 2209 green): `sha256:fee71aafceb4a7d5f24ae4a6a273a8d51eb26e0550fe9d12d4d86955f6a343a8`.
Per-customer floors → Tester 1, demo-hp, demo-felhom on 0.339.0 within 40 s; 10/24/9 containers, 0 unhealthy, 0
restarting, 0 errors; https-only every name 301. demo-felhom logged „adopted the household's saved list from the hub
(13 events …) — nothing pushed"; its box file and the hub row now agree. Global floor 0.339.0 / 0.158.1 (read back
with the WAL, 11:14:21 UTC); per-customer floors cleared. Evidence felhom.eu `documentation/audits/day-2026-10-22/`.
- **R-49 (operator D21 = B), `6a17c95`.** `.felhom.yml` `backup.volumes: [{name, class: excluded}]` leaves a named
volume out of every backup (`appbackup.ExcludedDockerVolumes` → `backup.Manager.dumpableVolumes`); declared names
only, a list that would exclude every named volume is refused whole. Red-proved (`TestR49_*`, both packages) and
the R-591 deep-copy alias test caught a shared slice (fixed in `deepCopyStack`). 9202: Immich unit
1 160 553 832 → 336 788 031 B; with the cache volume removed Immich's ML answered after 9.2 s.
- **R-1187 (c), `d5f88a2`.** A `.fab` import over an installed app that is not running merged the old volume data with
the bundle's. Reproduced on 9202 rc.1 (a marker written after the export survived the import; the running control
replaced it); now refused with a Hungarian sentence, the app untouched (rc.2: markers unchanged; the running path
still replaces). Red-proved (`r1187c_test.go`). Evidence felhom.eu `audits/night-2026-10-22/P3-candidates/r1187c-9202.txt`.
- **R-1189 (d), `d5f88a2`.** The off-site proof (05:30) and integrity check (06:00) never ran on a box that is off
then. `nightchain.CatchUp.After` now runs both once after the catch-up's legs; the false comment in `main.go`
corrected. Red-proved (`nightchain/r1189d_test.go`, `cmd/controller/r1189d_wiring_test.go`).
- **R-1214, `03c0a69` + `ae39467`.** The 03:30 second-drive copy's page cache pushed the apps out of memory.
`rsyncMirror` runs rsync in a 512 MB memory-capped container of the controller's own image, only for clean `/mnt/`
paths with no symlink (a security review found the symlink case; fixed and tested). 9202 (4 GB, 1.8 GB held, 6 GB):
full stall 5.7 / 2.5 s → 0.37 / 0.32 s; peak „full" 16.8 / 6.9 % → 0.9 / 1.0 %. A real `POST /api/backup/tier2` on
rc.4: 6 capped containers, each exit 0; Immich's copy equals its source (0 differences, 44 = 44 files).
`docker_run_volume_path_gate.py` allow-list entry with its WHY.
- **Gates:** `controller_gates.py --fast` green before each push; CI by commit — see felhom.eu
`REPORT-night-2026-10-22.md`.
- **Teardown (9202):** throwaway uptime-kuma removed through the product (`.fab` deleted); hog container and 19 GB of
test files removed; memory 25898 MB / swap 512 MB again; Immich's `.felhom.yml` back to its pre-night file; controller
back on **0.339.0** (healthy); the drill catalog equals live main again. Host: `/tmp/r1214*` removed on demo-hp. Hub:
9202 has no hub link — nothing.
+13
View File
@@ -1392,6 +1392,19 @@ retired in v0.126.0 when the moved blocks were legitimately rewritten onto the s
fallback is a state-only tier (unit + mandatory; optional skipped, honest reason). **NETWORK (NAS)
storage is never a tier-2 target** — pinned or auto (F-6C-1: rsync `-og` under root_squash →
wrong-owner restore). Restore reads v2 behind the marker gate; a pre-v2 copy is refused.
- **The copy runs in a memory-capped container (unreleased, rides 0.340.0; R-1214).** A plain `rsync` charges its page
cache to the guest and pushes the apps' working set out (on the 8 GB box: memory „full" pressure > 20 % for minutes at
03:30). `rsyncMirror` now runs `rsync -a --delete` in `docker run --rm --memory 512m --memory-swap 512m --network none`
of the controller's own image, the source mounted read-only — only when both paths are clean, under `/mnt/`, and
contain no symlink (`tier2CappedPath`, `filepath.EvalSymlinks(p) == p`: a link could point the mount elsewhere). A
docker start error (exit 125) falls back to plain rsync; any other failure is returned. Measured on 9202 (4 GB, 1.8 GB
held, 6 GB copy): full stall 5.7 s → 0.37 s. Tests `r1214_capped_copy_test.go`.
- **A template may leave a named volume out of every backup (unreleased, rides 0.340.0; R-49, operator D21 = B).**
`.felhom.yml` `backup.volumes: [{name: <volume>, class: excluded}]` — every named volume is dumped except
`<project>_<name>` (`appbackup.ExcludedDockerVolumes` → `backup.Manager.dumpableVolumes`), so it leaves Tier 1, Tier 2
and off-site at once and the stale clean-up removes the old tar. Only `excluded` acts; an undeclared name is ignored
and logged; a list that would exclude ALL of an app's named volumes is refused whole. First user: Immich's
`immich_ml_cache` (model weights; Immich re-downloads them). Design: `07` §6.2.
**Per-app Tier 2 contents by app type:**