# More apps that update themselves — 2026-09-30 (evening, by day) Brief: the operator's day brief of 2026-09-30 evening (Parts A–D). Architecture read first: `architecture/09-update-architecture.md` §3 decisions 6, 13, 14, 17, 22, 30, §6.4 parts 4–7, §6.5. Baselines (live Gitea, 16:49 CEST): controller `d48da6c` (0.283.1), agent `d766666` (0.138.0), felhom.eu `2866f6a`, catalog `4c552cf`. **Venues.** Bench = LXC 9401 on demo-hp, made fresh at 16:52 (6 cores, 10 GiB, **swap 0** — the stricter venue, R-733), harness v4, 10-minute memory watch (`bench/B1-bench-create.txt`). Box = scratch guest 9202 (controller 0.283.1, swap 512 MiB) pointed at the drill catalog (`box/B0`, `box/B1`), the product's guarded Update. The drill force-push was refused once by the permission check and then allowed by the operator. ## The Part table | Part | done / not done / changed | why | |---|---|---| | **A1 calibre-web** | **done** — v4.0.6 → v4.0.8, first ladder `53a4a1d` | front door = the Upload button (HTTP form), read back through OPDS + the served EPUB. The ingest folder was NOT used (it would be a file planted in a mount). `files_may_change`: the library DB `metadata.db` (+`-shm`, `-wal`) in the books folder; the book file did not change | | **A2 gitea** | **done** — 1.27.0 → 1.27.3, first ladder `d7ba60c` | the installer-form POST (no CSRF on that form), through the setup gate on the box. 28.0.0 not touched | | **B1 emby, ghost, home-assistant** | **done** — `3e4aa77`, `9a78e3b`, `15f4d59` | each had a newer tag inside its line (re-checked) | | **B2 uptime-kuma, wger, crafty-controller** | **done** — `35dd5cf`, `4ad32aa` (+ fix `7a4ff48`), `e1f0179` | new fixtures; wger needed a template fix first (R-738) | | **B2 wanderer** | **not done** | the bench cannot run it at all: its web server calls the DB at a public https name (R-739). The meilisearch question is NOT measured | | **B3 time left** | **done** — outline `b0b2514`, rallly `8d3a35a`, zipline 4.6.1 → 4.7.0 `a9700e2` → 4.8.0 `fb87030` | zipline 4.8.0 refuses a database that skipped 4.7.x (R-742); the box undid the direct jump itself | | **C the same-name fix gap** | **done** — row R-740, STATUS decision, `09` decision 30 dated note | measured from source, a unit walk, the registry and demo-hp; **nothing built** | | **D immich's older step** | **done (changed)** — re-proven at 768M, step files rewritten `48440ce` | the brief's route needed a writer mode that did not exist → `upgrade-test.py --restep` `63a96b0`, with tests | **Night-updatable apps (the audit's method (a)): 30 + 2 conditional at the start → 35 + 3 conditional** (38 carry a proven step; 15 have no ladder). ## Per app | app | step | bench (harness v4, swap 0, 10-min watch) | anon peak (cgroup peak) | abort | box 9202 (guarded Update) | marks | catalog | |---|---|---|---|---|---|---|---| | calibre-web | v4.0.6 → v4.0.8 | proven, read back before/after True/True, kills 0 | calibre-web 19.3 % (38.9 %) | starts-and-serves | done in 64.6 s, read back True | files_may_change | `53a4a1d` | | gitea | 1.27.0 → 1.27.3 | proven, read back before/after True/True, kills 0 | gitea 26.6 % (52.9 %) | refuses | done in 21.6 s, read back True | — | `d7ba60c` | | emby | 4.11.0.3 → 4.11.0.4 | proven, read back before/after True/True, kills 0 | emby 4.7 % (7.3 %) | starts-and-serves | done in 23.6 s, read back True | — | `3e4aa77` | | ghost | 6.65.0-alpine → 6.67.0-alpine | proven, read back before/after True/True, kills 0 | ghost 26.7 % (33.9 %) | starts-and-serves | done in 107.7 s, read back True | — | `9a78e3b` | | home-assistant | 2026.9.3 → 2026.9.4 | proven, read back before/after True/True, kills 0 | home-assistant 32.0 % (47.4 %) | starts-and-serves | done in 113.8 s, read back True | — | `15f4d59` | | uptime-kuma | 2.4.0 → 2.5.5 | proven, read back before/after True/True, kills 0 | uptime-kuma 43.3 % (49.7 %) | starts-and-serves | done in 33.9 s, read back True | — | `35dd5cf` | | wger | 2.6 → 2.7 | proven, read back before/after True/True, kills 0 | wger 49.8 % (78.6 %) | starts-data-gone | done in 46.2 s, read back True | — | `4ad32aa (+ fix 7a4ff48)` | | crafty-controller | 4.10.7 → 4.11.0 | proven, read back before/after True/True, kills 0 | crafty-controller 2.7 % (8.0 %) | starts-and-serves | done in 51.3 s, read back True | — | `e1f0179` | | outline | 1.9.1 → 1.10.1 | proven, read back before/after True/True, kills 0 | outline 33.5 % (37.7 %), outline-postgres 5.4 % (62.1 %), outline-redis 2.6 % (7.1 %) | starts-and-serves | done in 89.2 s, read back True | — | `b0b2514` | | rallly | 4.11.1 → 4.15.3 | proven, read back before/after True/True, kills 0 | rallly 60.6 % (64.0 %), rallly-postgres 2.1 % (65.8 %) | starts-data-gone | done in 64.7 s, read back True | — | `8d3a35a` | | zipline | 4.6.1 → 4.7.0 | proven, first try (after the fixture was fixed; see the direct jump below) | zipline 41.7 % | starts-and-serves | done in 103.6 s, read back True | — | `a9700e2` | | zipline | 4.7.0 → 4.8.0 | proven (third attempt: two FROM pulls failed on the bench's full disk) | zipline 34.3 % | starts-and-serves | done in 32.8 s, read back True | — | `fb87030` | | zipline | 4.6.1 → 4.8.0 (direct) | **failed** — 4.8.0 restarts: „cannot safely migrate from prisma to drizzle" | — | starts-and-serves | **undone** by the box in 20 s | — | not written (R-742) | | wger | 2.6 → 2.7 WITHOUT the migrations switch | box only | — | — | **done, then login 500** (12 migrations unapplied) | — | not written; fixed `7a4ff48` (R-738) | Files behind `files_may_change`: calibre-web — `media/books/metadata.db`, `metadata.db-shm`, `metadata.db-wal` (the library database; the book file unchanged; `A/calibre-names/`). On 9202 the same step changed no file in that folder (`box/calibre-web-files-{before,after,diff}.txt`). Memory figures: the anon peak is the basis of `memory_tight` (decision 22); the cgroup peak includes the file cache. ## Part C — the same-name security fix (R-740) `C/C1` demo-hp 9201 read-only digests (18 of 18 equal) · `C/C2` the unit walk on a scratch copy of the controller (the leg skips `no_test_record`; the Update button takes the tested digest; a same-tag re-test entry WOULD be pressed by the leg) · `C/C3` the floating lines' last pushes · `C/C4` no ladder has a same-tag re-test, and the writer refuses one. The decision is in `STATUS.md`. ## Part D — immich's step `0b8272068aab36bf` `D/D1` hub read-only: `/apps` lists 14 apps over the 3 reporting boxes, bookstack on 2 (positive control), **immich on none**. `D/D2` the geodata inside each server image: v3.0.3 `2026-07-13`, v3.2.2 `2026-08-30`, v3.2.4 `2026-09-28` — so the v3.0.3 → v3.2.2 step re-runs the import. `D/template-immich@0b82-768` = the step file with ONE line changed (512M → 768M) and its `.felhom.yml` `mem_limit` 4096M → 4480M. `D/D3` + `D/evidence/D-immich-0b82-768/`: bench, swap 0 — `Starting geodata import` 19:19:47, `Geodata import completed` 19:20:04, the database's `oom_kill` 0 from its birth, the album read back, 10-min watch 0 kills (anon peak 29.6 %). Written by `upgrade-test.py --restep` (catalog `63a96b0`), commit `48440ce`. ## Tools `tools/` — copies of today's walk/bench tools with this folder's paths, plus `benchsync.sh` (catalog scripts + templates → bench), `probe_up.sh` (a hand probe), `recheck.py` (upstream tags), `digestread.py` (Part C, read-only), `floatpush.py` (Part C), `partd.py` + `q4.sh` (Part D and the late queue).