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).