diff --git a/documentation/audits/night-2026-09-24/A1/10-9202-deploy-0.269.0.txt b/documentation/audits/night-2026-09-24/A1/10-9202-deploy-0.269.0.txt
new file mode 100644
index 00000000..40537912
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/A1/10-9202-deploy-0.269.0.txt
@@ -0,0 +1,3 @@
+gitea.dooplex.hu/admin/felhom-controller:0.269.0
+gitea.dooplex.hu/admin/felhom-controller:0.269.0 Up 6 seconds (healthy)
+
diff --git a/documentation/audits/night-2026-09-24/A1/11-copy.json b/documentation/audits/night-2026-09-24/A1/11-copy.json
new file mode 100644
index 00000000..7657df79
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/A1/11-copy.json
@@ -0,0 +1,41 @@
+{
+ "controller": "gitea.dooplex.hu/admin/felhom-controller:0.269.0",
+ "press_a": {
+ "accepted": true,
+ "http": "202",
+ "phases": [
+ {
+ "t": 0.0,
+ "phase": "safety-dump",
+ "label": "Adatb\u00e1zis pillanatk\u00e9p\u2026",
+ "updating": true,
+ "error": null,
+ "hold": null
+ },
+ {
+ "t": 2.1,
+ "phase": "pulling",
+ "label": "\u00daj verzi\u00f3 let\u00f6lt\u00e9se\u2026",
+ "updating": true,
+ "error": null,
+ "hold": null
+ },
+ {
+ "t": 4.1,
+ "phase": "failed",
+ "label": "A friss\u00edt\u00e9s nem siker\u00fclt",
+ "updating": false,
+ "error": "Az \u00faj verzi\u00f3 let\u00f6lt\u00e9se nem siker\u00fclt, ez\u00e9rt a friss\u00edt\u00e9s elmaradt. Az alkalmaz\u00e1s a kor\u00e1bbi verzi\u00f3val fut tov\u00e1bb.",
+ "hold": null
+ }
+ ],
+ "duration_s": 4.1,
+ "final_phase": "failed",
+ "update_error": "Az \u00faj verzi\u00f3 let\u00f6lt\u00e9se nem siker\u00fclt, ez\u00e9rt a friss\u00edt\u00e9s elmaradt. Az alkalmaz\u00e1s a kor\u00e1bbi verzi\u00f3val fut tov\u00e1bb.",
+ "hold_reason": null,
+ "state": "running"
+ },
+ "log_a": "2026/09/24 10:19:49 backup.go:467: [DEBUG] groupStacksByDrive: /mnt/felhom-drives/scratch_hdd/userdata/nextcloud \u2192 [nextcloud]\n2026/09/24 10:19:49 recovery_unit.go:224: [INFO] [backup] Recovery unit captured for nextcloud \u2192 /mnt/felhom-drives/scratch_hdd/userdata/nextcloud/backups/primary/nextcloud (images=3, secrets-referenced=3, data_keys=0, portable-carried=2/2, withheld=1)\n2026/09/24 10:20:48 update.go:775: [INFO] [stacks] update nextcloud: precondition met \u2014 Tier 2 (second drive) copy from 2026-09-24T09:12:08Z (1h9m0s old, limit 24h0m0s)\n2026/09/24 10:20:48 dbdump.go:266: [DEBUG] DumpOne: starting dump for container=nextcloud-db, stack=nextcloud, dbType=mariadb, dest=/mnt/felhom-drives/scratch_hdd/userdata/nextcloud/backups/primary/nextcloud/db-dumps/pre-restore-20260924T102048Z-nextcloud-mariadb.sql\n2026/09/24 10:20:49 dbdump.go:410: [INFO] [backup] DB dump: nextcloud-db \u2192 pre-restore-20260924T102048Z-nextcloud-mariadb.sql (854.3 KB, 478ms, 131 tables)\n2026/09/24 10:20:49 update_guard.go:478: [INFO] [backup] update safety dump for nextcloud: 1 file(s) [/mnt/felhom-drives/scratch_hdd/userdata/nextcloud/backups/primary/nextcloud/db-dumps/pre-restore-20260924T102048Z-nextcloud-mariadb.sql]\n2026/09/24 10:20:49 update.go:807: [INFO] [stacks] update nextcloud: safety dump done (1 file(s)) [/mnt/felhom-drives/scratch_hdd/userdata/nextcloud/backups/primary/nextcloud/db-dumps/pre-restore-20260924T102048Z-nextcloud-mariadb.sql]\n",
+ "mirror": "== /mnt/felhom-drives/scratch_hdd/userdata/romm/backups/secondary/nextcloud\nhdd\nrecovery-unit\nfelhom-seed-1-7cb158.txt\nfelhom-seed-0-e19728.txt\nfelhom-seed-2-8620d3.txt\n",
+ "record": "{\"enabled\": true, \"method\": \"rsync\", \"destination_path\": \"/mnt/felhom-drives/scratch_hdd/userdata/romm\", \"schedule\": \"daily\", \"last_run\": \"2026-09-24T09:12:08Z\", \"last_status\": \"ok\", \"last_success\": \"2026-09-24T09:12:08Z\", \"success_tracked\": true, \"unit_package_date\": \"2026-09-24T09:12:06Z\", \"last_duration\": \"1s\", \"last_size_human\": \"1.0 GB\"}\n"
+}
\ No newline at end of file
diff --git a/documentation/audits/night-2026-09-24/A1/11-copy.log b/documentation/audits/night-2026-09-24/A1/11-copy.log
new file mode 100644
index 00000000..89afabbc
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/A1/11-copy.log
@@ -0,0 +1,25 @@
+12:20:43 drill: notag (backing-up + Tier 2, pull fails)
+12:20:48 [6] Update -> 202 {'ok': True, 'data': {'accepted': True, 'completed': False}, 'message': 'Frissítés elindult – az állapot a kártyán követhető'}
+12:20:48 + 0.0s phase=safety-dump label=Adatbázis pillanatkép… err=None hold=None
+12:20:50 + 2.1s phase=pulling label=Új verzió letöltése… err=None hold=None
+12:20:52 + 4.1s phase=failed label=A frissítés nem sikerült err=Az új verzió letöltése nem sikerült, ezért a frissítés elmaradt. Az alkalmazás a korábbi verzióval fut tovább. hold=None
+12:20:56 log (a):
+2026/09/24 10:19:49 backup.go:467: [DEBUG] groupStacksByDrive: /mnt/felhom-drives/scratch_hdd/userdata/nextcloud → [nextcloud]
+2026/09/24 10:19:49 recovery_unit.go:224: [INFO] [backup] Recovery unit captured for nextcloud → /mnt/felhom-drives/scratch_hdd/userdata/nextcloud/backups/primary/nextcloud (images=3, secrets-referenced=3, data_keys=0, portable-carried=2/2, withheld=1)
+2026/09/24 10:20:48 update.go:775: [INFO] [stacks] update nextcloud: precondition met — Tier 2 (second drive) copy from 2026-09-24T09:12:08Z (1h9m0s old, limit 24h0m0s)
+2026/09/24 10:20:48 dbdump.go:266: [DEBUG] DumpOne: starting dump for container=nextcloud-db, stack=nextcloud, dbType=mariadb, dest=/mnt/felhom-drives/scratch_hdd/userdata/nextcloud/backups/primary/nextcloud/db-dumps/pre-restore-20260924T102048Z-nextcloud-mariadb.sql
+2026/09/24 10:20:49 dbdump.go:410: [INFO] [backup] DB dump: nextcloud-db → pre-restore-20260924T102048Z-nextcloud-mariadb.sql (854.3 KB, 478ms, 131 tables)
+2026/09/24 10:20:49 update_guard.go:478: [INFO] [backup] update safety dump for nextcloud: 1 file(s) [/mnt/felhom-drives/scratch_hdd/userdata/nextcloud/backups/primary/nextcloud/db-dumps/pre-restore-20260924T102048Z-nextcloud-mariadb.sql]
+2026/09/24 10:20:49 update.go:807: [INFO] [stacks] update nextcloud: safety dump done (1 file(s)) [/mnt/felhom-drives/scratch_hdd/userdata/nextcloud/backups/primary/nextcloud/db-dumps/pre-restore-20260924T102048Z-nextcloud-mariadb.sql]
+
+12:20:57 drill: back to 34.0.4
+12:21:00 mirrors:
+== /mnt/felhom-drives/scratch_hdd/userdata/romm/backups/secondary/nextcloud
+hdd
+recovery-unit
+felhom-seed-1-7cb158.txt
+felhom-seed-0-e19728.txt
+felhom-seed-2-8620d3.txt
+
+12:21:03 Tier-2 record: {"enabled": true, "method": "rsync", "destination_path": "/mnt/felhom-drives/scratch_hdd/userdata/romm", "schedule": "daily", "last_run": "2026-09-24T09:12:08Z", "last_status": "ok", "last_success": "2026-09-24T09:12:08Z", "success_tracked": true, "unit_package_date": "2026-09-24T09:12:06Z", "last_duration": "1s", "last_size_human": "1.0 GB"}
+
diff --git a/documentation/audits/night-2026-09-24/A1/11.stdout b/documentation/audits/night-2026-09-24/A1/11.stdout
new file mode 100644
index 00000000..89afabbc
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/A1/11.stdout
@@ -0,0 +1,25 @@
+12:20:43 drill: notag (backing-up + Tier 2, pull fails)
+12:20:48 [6] Update -> 202 {'ok': True, 'data': {'accepted': True, 'completed': False}, 'message': 'Frissítés elindult – az állapot a kártyán követhető'}
+12:20:48 + 0.0s phase=safety-dump label=Adatbázis pillanatkép… err=None hold=None
+12:20:50 + 2.1s phase=pulling label=Új verzió letöltése… err=None hold=None
+12:20:52 + 4.1s phase=failed label=A frissítés nem sikerült err=Az új verzió letöltése nem sikerült, ezért a frissítés elmaradt. Az alkalmazás a korábbi verzióval fut tovább. hold=None
+12:20:56 log (a):
+2026/09/24 10:19:49 backup.go:467: [DEBUG] groupStacksByDrive: /mnt/felhom-drives/scratch_hdd/userdata/nextcloud → [nextcloud]
+2026/09/24 10:19:49 recovery_unit.go:224: [INFO] [backup] Recovery unit captured for nextcloud → /mnt/felhom-drives/scratch_hdd/userdata/nextcloud/backups/primary/nextcloud (images=3, secrets-referenced=3, data_keys=0, portable-carried=2/2, withheld=1)
+2026/09/24 10:20:48 update.go:775: [INFO] [stacks] update nextcloud: precondition met — Tier 2 (second drive) copy from 2026-09-24T09:12:08Z (1h9m0s old, limit 24h0m0s)
+2026/09/24 10:20:48 dbdump.go:266: [DEBUG] DumpOne: starting dump for container=nextcloud-db, stack=nextcloud, dbType=mariadb, dest=/mnt/felhom-drives/scratch_hdd/userdata/nextcloud/backups/primary/nextcloud/db-dumps/pre-restore-20260924T102048Z-nextcloud-mariadb.sql
+2026/09/24 10:20:49 dbdump.go:410: [INFO] [backup] DB dump: nextcloud-db → pre-restore-20260924T102048Z-nextcloud-mariadb.sql (854.3 KB, 478ms, 131 tables)
+2026/09/24 10:20:49 update_guard.go:478: [INFO] [backup] update safety dump for nextcloud: 1 file(s) [/mnt/felhom-drives/scratch_hdd/userdata/nextcloud/backups/primary/nextcloud/db-dumps/pre-restore-20260924T102048Z-nextcloud-mariadb.sql]
+2026/09/24 10:20:49 update.go:807: [INFO] [stacks] update nextcloud: safety dump done (1 file(s)) [/mnt/felhom-drives/scratch_hdd/userdata/nextcloud/backups/primary/nextcloud/db-dumps/pre-restore-20260924T102048Z-nextcloud-mariadb.sql]
+
+12:20:57 drill: back to 34.0.4
+12:21:00 mirrors:
+== /mnt/felhom-drives/scratch_hdd/userdata/romm/backups/secondary/nextcloud
+hdd
+recovery-unit
+felhom-seed-1-7cb158.txt
+felhom-seed-0-e19728.txt
+felhom-seed-2-8620d3.txt
+
+12:21:03 Tier-2 record: {"enabled": true, "method": "rsync", "destination_path": "/mnt/felhom-drives/scratch_hdd/userdata/romm", "schedule": "daily", "last_run": "2026-09-24T09:12:08Z", "last_status": "ok", "last_success": "2026-09-24T09:12:08Z", "success_tracked": true, "unit_package_date": "2026-09-24T09:12:06Z", "last_duration": "1s", "last_size_human": "1.0 GB"}
+
diff --git a/documentation/audits/night-2026-09-24/A1/12-backup-max-age-1m.txt b/documentation/audits/night-2026-09-24/A1/12-backup-max-age-1m.txt
new file mode 100644
index 00000000..5fee830c
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/A1/12-backup-max-age-1m.txt
@@ -0,0 +1,5 @@
+update:$
+ health_timeout: 90s$
+ backup_max_age: 1m$
+Up 15 seconds (healthy)
+
diff --git a/documentation/audits/night-2026-09-24/A1/20.stdout b/documentation/audits/night-2026-09-24/A1/20.stdout
new file mode 100644
index 00000000..265502a2
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/A1/20.stdout
@@ -0,0 +1,41 @@
+12:23:09 drill: notag
+12:23:14 [6] Update -> 202 {'ok': True, 'data': {'accepted': True, 'completed': False}, 'message': 'Frissítés elindult – az állapot a kártyán követhető'}
+12:23:14 + 0.0s phase=backing-up label=Biztonsági mentés készül a frissítés előtt… err=None hold=None
+12:23:29 + 15.4s phase=safety-dump label=Adatbázis pillanatkép… err=None hold=None
+12:23:35 + 20.5s phase=pulling label=Új verzió letöltése… err=None hold=None
+12:23:37 + 22.6s phase=failed label=A frissítés nem sikerült err=Az új verzió letöltése nem sikerült, ezért a frissítés elmaradt. Az alkalmazás a korábbi verzióval fut tovább. hold=None
+12:23:40 log (a):
+2026/09/24 10:20:48 update.go:775: [INFO] [stacks] update nextcloud: precondition met — Tier 2 (second drive) copy from 2026-09-24T09:12:08Z (1h9m0s old, limit 24h0m0s)
+2026/09/24 10:23:14 update.go:777: [INFO] [stacks] update nextcloud: no usable copy on any tier — younger than 1m0s and not older than this install's deploy (2026-09-24T09:09:53Z) (found: Tier 2 (second drive) at 2026-09-24T09:12:08Z (1h11m0s old); Tier 1 (own recovery unit) at 2026-09-24T10:21:21Z (2m0s old)) — backing up first
+2026/09/24 10:23:14 update_guard.go:360: [INFO] [backup] update pre-backup for nextcloud: starting (DB dump → volume dump → unit capture → Tier 2)
+2026/09/24 10:23:29 tier2.go:425: [INFO] [backup] Tier 2 copied nextcloud → /mnt/sys_drive/felhom-data/backups/secondary/nextcloud (1.0 GB, 1 leg(s), 1s) [SSD: state-only]
+2026/09/24 10:23:29 update.go:792: [INFO] [stacks] update nextcloud: precondition met after the backup — Tier 2 (second drive) copy from 2026-09-24T10:23:29Z
+
+12:23:41 drill: back to 34.0.4
+12:23:44 Tier-2 record: {"enabled": true, "method": "rsync", "destination_path": "/mnt/sys_drive/felhom-data", "schedule": "daily", "last_run": "2026-09-24T10:23:29Z", "last_status": "ok", "last_success": "2026-09-24T10:23:29Z", "success_tracked": true, "unit_package_date": "2026-09-24T10:21:21Z", "last_duration": "1s", "last_size_human": "1.0 GB"}
+
+12:23:48 mirror seeds + devices:
+felhom-seed-2-8620d3.txt
+felhom-seed-1-7cb158.txt
+felhom-seed-0-e19728.txt
+/mnt/sys_drive/felhom-data dev=1793
+/mnt/felhom-drives/scratch_hdd/userdata/nextcloud dev=66304
+
+12:23:51 (b) DELETE felhom-seed-0-e19728.txt -> 204
+12:23:51 (b) PUT felhom-seed-1-7cb158.txt (newer than the copy) -> True
+12:23:53 nextcloud files read back: 2/3 (negative control http=404)
+12:23:54 drill: redis 7-alpine -> 7.2-alpine + probe 8999 (the failing edge)
+12:23:59 (c) Update -> 202
+12:23:59 + 0.0s phase=safety-dump
+12:24:01 + 2.6s phase=pulling
+12:24:04 + 5.3s phase=copying
+12:24:13 + 14.2s phase=starting
+12:24:14 + 15.8s phase=undoing
+12:24:53 + 54.2s phase=undone
+12:24:53 (c) held: {"update_phase": "undone", "hold_reason": null, "hold_no_whole_copy": null, "hold_kind": null, "state": "running"}
+12:24:53 (c) [hu] None
+12:24:53 (c) [en] None
+12:24:54 drill: back to the head + the real probe
+12:24:59 (d) POST /backup/tier2/unit-restore -> HTTP/2 302 ['location: /backups/apps?flash=flash.restore.full_started']
+12:24:59 restore-status (True, None)
+12:25:37 restore-status (False, None)
diff --git a/documentation/audits/night-2026-09-24/A1/21-readback.json b/documentation/audits/night-2026-09-24/A1/21-readback.json
new file mode 100644
index 00000000..380a9efb
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/A1/21-readback.json
@@ -0,0 +1,32 @@
+{
+ "restore_status": {
+ "ok": true,
+ "data": {
+ "running": false,
+ "op": "tier2-whole-restore",
+ "stack": "nextcloud",
+ "started_at": "2026-09-24T10:24:59.064176013Z",
+ "last": {
+ "op": "tier2-whole-restore",
+ "stack": "nextcloud",
+ "ok": true,
+ "message": "A(z) nextcloud: 3 adatkötet és az adatbázis visszaállítva — az alkalmazás újraindult. A meghajtó fájljai: 1 visszahozva, 0 kicserélve (a régebbi élő példány megmaradt mellette, .felhom-… végződéssel), 1 újabb példány érintetlenül hagyva, 137 változatlan. Fájl nem lett törölve.",
+ "finished_at": "2026-09-24T10:25:37.128885903Z"
+ },
+ "last_recent": true
+ }
+ },
+ "log": "2026/09/24 10:24:59 handlers.go:2063: [WARN] [web] Tier-2 WHOLE restore requested (async, files + database): stack=nextcloud from 172.18.0.9:38812\n2026/09/24 10:24:59 tier2_whole.go:304: [WARN] [backup] WHOLE restore for nextcloud from the second drive /mnt/sys_drive/felhom-data/backups/secondary/nextcloud: files by the four rules (never delete, never overwrite newer), then the unit\n2026/09/24 10:25:01 tier2_whole.go:326: [INFO] [backup] whole restore nextcloud: files restored=1 replaced=0 (live kept beside as *.felhom-20260924T102500Z) kept-newer=1 unchanged=137\n2026/09/24 10:25:01 restore.go:152: [INFO] [backup] Restoring Docker volume nextcloud_nextcloud_db_data for nextcloud\n2026/09/24 10:25:02 restore.go:152: [INFO] [backup] Restoring Docker volume nextcloud_nextcloud_html for nextcloud\n2026/09/24 10:25:12 restore.go:152: [INFO] [backup] Restoring Docker volume nextcloud_nextcloud_redis_data for nextcloud\n2026/09/24 10:25:37 restore_unit.go:464: [INFO] [backup] Restore-from-unit completed: nextcloud — 3 volume(s) of 3 listed, 1 database(s) of 1 listed\n2026/09/24 10:25:37 handlers.go:2074: [INFO] [web] Tier-2 whole restore completed (async): stack=nextcloud in 38.0640377s (files restored 1, replaced 0, kept-newer 1, unchanged 137; volumes 3/3, dbs 1/1)\n2026/09/24 10:29:18 settings.go:1814: [WARN] [settings] restore hold SET for gokapi — the app stays stopped until it is cleared\n",
+ "account": true,
+ "file0_back": true,
+ "file1_kept_edit": true,
+ "file2_unchanged": true,
+ "occ_scan": "| Folders | Files | New | Updated | Removed | Errors | Elapsed time |\n+---------+-------+-----+---------+---------+--------+--------------+\n| 11 | 69 | 0 | 8 | 0 | 0 | 00:00:00 |\n+---------+-------+-----+---------+---------+--------+--------------+\n",
+ "file0_back_after_scan": true,
+ "kept_beside": "",
+ "final": {
+ "state": "running",
+ "hold_reason": null,
+ "update_phase": "undone"
+ }
+}
\ No newline at end of file
diff --git a/documentation/audits/night-2026-09-24/A1/21-readback.log b/documentation/audits/night-2026-09-24/A1/21-readback.log
new file mode 100644
index 00000000..c22e0845
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/A1/21-readback.log
@@ -0,0 +1,21 @@
+12:34:30 restore-status: {"ok": true, "data": {"running": false, "op": "tier2-whole-restore", "stack": "nextcloud", "started_at": "2026-09-24T10:24:59.064176013Z", "last": {"op": "tier2-whole-restore", "stack": "nextcloud", "ok": true, "message": "A(z) nextcloud: 3 adatkötet és az adatbázis visszaállítva — az alkalmazás újraindult. A meghajtó fájljai: 1 visszahozva, 0 kicserélve (a régebbi élő példány megmaradt mellette, .felhom-… végződéssel), 1 újabb példány érintetlenül hagyva, 137 változatlan. Fájl nem lett törölve.", "finished_at": "2026-09-24T10:25:37.128885903Z"}, "last_recent": true}}
+12:34:34 log:
+2026/09/24 10:24:59 handlers.go:2063: [WARN] [web] Tier-2 WHOLE restore requested (async, files + database): stack=nextcloud from 172.18.0.9:38812
+2026/09/24 10:24:59 tier2_whole.go:304: [WARN] [backup] WHOLE restore for nextcloud from the second drive /mnt/sys_drive/felhom-data/backups/secondary/nextcloud: files by the four rules (never delete, never overwrite newer), then the unit
+2026/09/24 10:25:01 tier2_whole.go:326: [INFO] [backup] whole restore nextcloud: files restored=1 replaced=0 (live kept beside as *.felhom-20260924T102500Z) kept-newer=1 unchanged=137
+2026/09/24 10:25:01 restore.go:152: [INFO] [backup] Restoring Docker volume nextcloud_nextcloud_db_data for nextcloud
+2026/09/24 10:25:02 restore.go:152: [INFO] [backup] Restoring Docker volume nextcloud_nextcloud_html for nextcloud
+2026/09/24 10:25:12 restore.go:152: [INFO] [backup] Restoring Docker volume nextcloud_nextcloud_redis_data for nextcloud
+2026/09/24 10:25:37 restore_unit.go:464: [INFO] [backup] Restore-from-unit completed: nextcloud — 3 volume(s) of 3 listed, 1 database(s) of 1 listed
+2026/09/24 10:25:37 handlers.go:2074: [INFO] [web] Tier-2 whole restore completed (async): stack=nextcloud in 38.0640377s (files restored 1, replaced 0, kept-newer 1, unchanged 137; volumes 3/3, dbs 1/1)
+2026/09/24 10:29:18 settings.go:1814: [WARN] [settings] restore hold SET for gokapi — the app stays stopped until it is cleared
+
+12:34:41 nextcloud: readback of the seeded user found=True
+12:34:43 file0 (deleted after the copy) back=True (200); file1 (edited after the copy) kept the edit=True; file2 unchanged=True
+12:34:46 occ files:scan (the app sees its files): | Folders | Files | New | Updated | Removed | Errors | Elapsed time |
++---------+-------+-----+---------+---------+--------+--------------+
+| 11 | 69 | 0 | 8 | 0 | 0 | 00:00:00 |
++---------+-------+-----+---------+---------+--------+--------------+
+
+12:34:47 file0 after occ scan: 200 True
+12:34:50 final {"state": "running", "hold_reason": null, "update_phase": "undone"} | kept beside: ''
diff --git a/documentation/audits/night-2026-09-24/A1/30-held-then-whole.json b/documentation/audits/night-2026-09-24/A1/30-held-then-whole.json
new file mode 100644
index 00000000..dc1b0a82
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/A1/30-held-then-whole.json
@@ -0,0 +1,35 @@
+{
+ "controller": "gitea.dooplex.hu/admin/felhom-controller:0.269.0",
+ "held": {
+ "update_phase": "failed",
+ "hold_reason": "A frissítés nem sikerült, és az automatikus visszaállítás sem. Az adatok az új változat által hagyott állapotban vannak. A(z) nextcloud frissítése 2026-09-24 12:41-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: második meghajtó, 2026-09-24 12:39 — ez a másolat a beállításokat, az adatbázist és a fájlokat tartalmazza.",
+ "hold_no_whole_copy": null,
+ "hold_kind": "update_failed",
+ "state": "stopped"
+ },
+ "page_hu": "A frissítés nem sikerült, és az automatikus visszaállítás sem. Az adatok az új változat által hagyott állapotban vannak. A(z) nextcloud frissítése 2026-09-24 12:41-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: második meghajtó, 2026-09-24 12:39 — ez a másolat a beállításokat, az adatbázist és a fájlokat tartalmazza. Mentések",
+ "page_en": "The update did not succeed, and the automatic undo did not either. The data is as the new version left it. The update of nextcloud at 2026-09-24 12:41 did not succeed, and the app did not start on the new version. The app stays stopped for safety, so that its data is not damaged. It can be restored on the Backups page from this backup: second drive, 2026-09-24 12:39 — this copy holds the settings, the database and the files. Backups",
+ "hdd_data": {
+ "code": "200",
+ "keep_data_only": null
+ },
+ "restore_msg": [
+ false,
+ null
+ ],
+ "restore_log": "2026/09/24 10:41:18 handlers.go:2063: [WARN] [web] Tier-2 WHOLE restore requested (async, files + database): stack=nextcloud from 172.18.0.9:52346\n2026/09/24 10:41:18 tier2_whole.go:304: [WARN] [backup] WHOLE restore for nextcloud from the second drive /mnt/sys_drive/felhom-data/backups/secondary/nextcloud: files by the four rules (never delete, never overwrite newer), then the unit\n2026/09/24 10:41:18 tier2_whole.go:326: [INFO] [backup] whole restore nextcloud: files restored=0 replaced=0 (live kept beside as *.felhom-20260924T104118Z) kept-newer=0 unchanged=142\n2026/09/24 10:41:52 restore_unit.go:464: [INFO] [backup] Restore-from-unit completed: nextcloud — 3 volume(s) of 3 listed, 1 database(s) of 1 listed\n2026/09/24 10:41:52 settings.go:1848: [INFO] [settings] restore hold CLEARED for nextcloud\n2026/09/24 10:41:52 update_guard.go:609: [INFO] [backup] nextcloud: restore completed — the update hold (set 2026-09-24T10:41:12Z) is CLEARED\n2026/09/24 10:41:52 handlers.go:2074: [INFO] [web] Tier-2 whole restore completed (async): stack=nextcloud in 33.797173087s (files restored 0, replaced 0, kept-newer 0, unchanged 142; volumes 3/3, dbs 1/1)\n",
+ "account": true,
+ "files_after": [
+ {
+ "felhom-seed-0-e19728.txt": true,
+ "felhom-seed-2-8620d3.txt": true
+ },
+ "404"
+ ],
+ "kept_beside": "",
+ "final": {
+ "state": "unhealthy",
+ "hold_reason": null,
+ "update_phase": null
+ }
+}
\ No newline at end of file
diff --git a/documentation/audits/night-2026-09-24/A1/30-held-then-whole.log b/documentation/audits/night-2026-09-24/A1/30-held-then-whole.log
new file mode 100644
index 00000000..04d4c86d
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/A1/30-held-then-whole.log
@@ -0,0 +1,34 @@
+12:38:51 drill: redis 7-alpine -> 7.4-alpine + probe 8999 (the failing edge)
+12:38:56 (c) Update -> 202
+12:38:56 + 0.0s phase=backing-up
+12:39:19 + 23.2s phase=safety-dump
+12:39:22 + 25.8s phase=pulling
+12:39:23 + 26.9s phase=copying
+12:39:33 + 36.9s phase=starting
+12:39:39 + 43.2s phase=verifying
+12:39:43 >>> marker removed:
+copy: nextcloud_nextcloud_db_data.pre-update-20260924T103924Z
+data
+
+12:41:10 + 134.0s phase=undoing
+12:41:12 + 136.1s phase=failed
+12:41:12 (c) held: {"update_phase": "failed", "hold_reason": "A frissítés nem sikerült, és az automatikus visszaállítás sem. Az adatok az új változat által hagyott állapotban vannak. A(z) nextcloud frissítése 2026-09-24 12:41-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: második meghajtó, 2026-09-24 12:39 — ez a másolat a beállításokat, az adatbázist és a fájlokat tartalmazza.", "hold_no_whole_copy": null, "hold_kind": "update_failed", "state": "stopped"}
+12:41:12 (c) [hu] 'A frissítés nem sikerült, és az automatikus visszaállítás sem. Az adatok az új változat által hagyott állapotban vannak. A(z) nextcloud frissítése 2026-09-24 12:41-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: második meghajtó, 2026-09-24 12:39 — ez a másolat a beállításokat, az adatbázist és a fájlokat tartalmazza. Mentések'
+12:41:12 (c) [en] 'The update did not succeed, and the automatic undo did not either. The data is as the new version left it. The update of nextcloud at 2026-09-24 12:41 did not succeed, and the app did not start on the new version. The app stays stopped for safety, so that its data is not damaged. It can be restored on the Backups page from this backup: second drive, 2026-09-24 12:39 — this copy holds the settings, the database and the files. Backups'
+12:41:12 (c2) hdd-data -> {"code": "200", "keep_data_only": null}
+12:41:13 drill: back to the head + the real probe
+12:41:18 (d) POST /backup/tier2/unit-restore -> HTTP/2 302 ['location: /backups/apps?flash=flash.restore.full_started']
+12:41:18 restore-status (True, None)
+12:41:52 restore-status (False, None)
+12:41:56 (d) log:
+2026/09/24 10:41:18 handlers.go:2063: [WARN] [web] Tier-2 WHOLE restore requested (async, files + database): stack=nextcloud from 172.18.0.9:52346
+2026/09/24 10:41:18 tier2_whole.go:304: [WARN] [backup] WHOLE restore for nextcloud from the second drive /mnt/sys_drive/felhom-data/backups/secondary/nextcloud: files by the four rules (never delete, never overwrite newer), then the unit
+2026/09/24 10:41:18 tier2_whole.go:326: [INFO] [backup] whole restore nextcloud: files restored=0 replaced=0 (live kept beside as *.felhom-20260924T104118Z) kept-newer=0 unchanged=142
+2026/09/24 10:41:52 restore_unit.go:464: [INFO] [backup] Restore-from-unit completed: nextcloud — 3 volume(s) of 3 listed, 1 database(s) of 1 listed
+2026/09/24 10:41:52 settings.go:1848: [INFO] [settings] restore hold CLEARED for nextcloud
+2026/09/24 10:41:52 update_guard.go:609: [INFO] [backup] nextcloud: restore completed — the update hold (set 2026-09-24T10:41:12Z) is CLEARED
+2026/09/24 10:41:52 handlers.go:2074: [INFO] [web] Tier-2 whole restore completed (async): stack=nextcloud in 33.797173087s (files restored 0, replaced 0, kept-newer 0, unchanged 142; volumes 3/3, dbs 1/1)
+
+12:42:03 nextcloud: readback of the seeded user found=True
+12:42:05 nextcloud files read back: 2/2 (negative control http=404)
+12:42:08 (e) final {"state": "unhealthy", "hold_reason": null, "update_phase": null} kept-beside:
diff --git a/documentation/audits/night-2026-09-24/A1/30.stdout b/documentation/audits/night-2026-09-24/A1/30.stdout
new file mode 100644
index 00000000..04d4c86d
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/A1/30.stdout
@@ -0,0 +1,34 @@
+12:38:51 drill: redis 7-alpine -> 7.4-alpine + probe 8999 (the failing edge)
+12:38:56 (c) Update -> 202
+12:38:56 + 0.0s phase=backing-up
+12:39:19 + 23.2s phase=safety-dump
+12:39:22 + 25.8s phase=pulling
+12:39:23 + 26.9s phase=copying
+12:39:33 + 36.9s phase=starting
+12:39:39 + 43.2s phase=verifying
+12:39:43 >>> marker removed:
+copy: nextcloud_nextcloud_db_data.pre-update-20260924T103924Z
+data
+
+12:41:10 + 134.0s phase=undoing
+12:41:12 + 136.1s phase=failed
+12:41:12 (c) held: {"update_phase": "failed", "hold_reason": "A frissítés nem sikerült, és az automatikus visszaállítás sem. Az adatok az új változat által hagyott állapotban vannak. A(z) nextcloud frissítése 2026-09-24 12:41-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: második meghajtó, 2026-09-24 12:39 — ez a másolat a beállításokat, az adatbázist és a fájlokat tartalmazza.", "hold_no_whole_copy": null, "hold_kind": "update_failed", "state": "stopped"}
+12:41:12 (c) [hu] 'A frissítés nem sikerült, és az automatikus visszaállítás sem. Az adatok az új változat által hagyott állapotban vannak. A(z) nextcloud frissítése 2026-09-24 12:41-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: második meghajtó, 2026-09-24 12:39 — ez a másolat a beállításokat, az adatbázist és a fájlokat tartalmazza. Mentések'
+12:41:12 (c) [en] 'The update did not succeed, and the automatic undo did not either. The data is as the new version left it. The update of nextcloud at 2026-09-24 12:41 did not succeed, and the app did not start on the new version. The app stays stopped for safety, so that its data is not damaged. It can be restored on the Backups page from this backup: second drive, 2026-09-24 12:39 — this copy holds the settings, the database and the files. Backups'
+12:41:12 (c2) hdd-data -> {"code": "200", "keep_data_only": null}
+12:41:13 drill: back to the head + the real probe
+12:41:18 (d) POST /backup/tier2/unit-restore -> HTTP/2 302 ['location: /backups/apps?flash=flash.restore.full_started']
+12:41:18 restore-status (True, None)
+12:41:52 restore-status (False, None)
+12:41:56 (d) log:
+2026/09/24 10:41:18 handlers.go:2063: [WARN] [web] Tier-2 WHOLE restore requested (async, files + database): stack=nextcloud from 172.18.0.9:52346
+2026/09/24 10:41:18 tier2_whole.go:304: [WARN] [backup] WHOLE restore for nextcloud from the second drive /mnt/sys_drive/felhom-data/backups/secondary/nextcloud: files by the four rules (never delete, never overwrite newer), then the unit
+2026/09/24 10:41:18 tier2_whole.go:326: [INFO] [backup] whole restore nextcloud: files restored=0 replaced=0 (live kept beside as *.felhom-20260924T104118Z) kept-newer=0 unchanged=142
+2026/09/24 10:41:52 restore_unit.go:464: [INFO] [backup] Restore-from-unit completed: nextcloud — 3 volume(s) of 3 listed, 1 database(s) of 1 listed
+2026/09/24 10:41:52 settings.go:1848: [INFO] [settings] restore hold CLEARED for nextcloud
+2026/09/24 10:41:52 update_guard.go:609: [INFO] [backup] nextcloud: restore completed — the update hold (set 2026-09-24T10:41:12Z) is CLEARED
+2026/09/24 10:41:52 handlers.go:2074: [INFO] [web] Tier-2 whole restore completed (async): stack=nextcloud in 33.797173087s (files restored 0, replaced 0, kept-newer 0, unchanged 142; volumes 3/3, dbs 1/1)
+
+12:42:03 nextcloud: readback of the seeded user found=True
+12:42:05 nextcloud files read back: 2/2 (negative control http=404)
+12:42:08 (e) final {"state": "unhealthy", "hold_reason": null, "update_phase": null} kept-beside:
diff --git a/documentation/audits/night-2026-09-24/A2/10-held-no-copy-keep-data.json b/documentation/audits/night-2026-09-24/A2/10-held-no-copy-keep-data.json
new file mode 100644
index 00000000..25bb86cc
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/A2/10-held-no-copy-keep-data.json
@@ -0,0 +1,49 @@
+{
+ "controller": "gitea.dooplex.hu/admin/felhom-controller:0.269.0",
+ "held": {
+ "update_phase": "failed",
+ "hold_reason": "A(z) nextcloud frissítése nem sikerült, és az automatikus visszaállítás sem. Ezen a dobozon nincs olyan másolat, amely az alkalmazást a fájljaival együtt vissza tudná hozni. A Felhom ügyfélszolgálatát értesítettük — addig ne indítsd újra, és ne töröld az adatait.",
+ "hold_no_whole_copy": true,
+ "hold_kind": "update_failed",
+ "state": "stopped"
+ },
+ "page_hu": "A(z) nextcloud frissítése nem sikerült, és az automatikus visszaállítás sem. Ezen a dobozon nincs olyan másolat, amely az alkalmazást a fájljaival együtt vissza tudná hozni. A Felhom ügyfélszolgálatát értesítettük — addig ne indítsd újra, és ne töröld az adatait.",
+ "page_en": "The update of nextcloud did not work, and neither did the automatic undo. This box has no copy that can bring the app back together with its files. Felhom support has been told — until then, do not restart the app or delete its data.",
+ "hdd_data": {
+ "code": "200",
+ "keep_data_only": true
+ },
+ "remove_hu": {
+ "code": "409",
+ "error": "Amíg az ügyfélszolgálat foglalkozik az alkalmazással, az adatai nem törölhetők — az alkalmazást az adatai megtartásával eltávolíthatod."
+ },
+ "remove_en": {
+ "code": "409",
+ "error": "While support is working on this app, its data cannot be deleted — you can remove the app and keep its data."
+ },
+ "remove_backups_en": {
+ "code": "409",
+ "error": "While support is working on this app, its data cannot be deleted — you can remove the app and keep its data."
+ },
+ "still_deployed": true,
+ "mirror_back": "hdd\nrecovery-unit\n",
+ "restore_msg": [
+ false,
+ null
+ ],
+ "restore_log": "2026/09/24 10:48:06 handlers.go:2063: [WARN] [web] Tier-2 WHOLE restore requested (async, files + database): stack=nextcloud from 172.18.0.9:57508\n2026/09/24 10:48:06 tier2_whole.go:304: [WARN] [backup] WHOLE restore for nextcloud from the second drive /mnt/sys_drive/felhom-data/backups/secondary/nextcloud: files by the four rules (never delete, never overwrite newer), then the unit\n2026/09/24 10:48:06 tier2_whole.go:326: [INFO] [backup] whole restore nextcloud: files restored=0 replaced=0 (live kept beside as *.felhom-20260924T104806Z) kept-newer=0 unchanged=142\n2026/09/24 10:48:40 restore_unit.go:464: [INFO] [backup] Restore-from-unit completed: nextcloud — 3 volume(s) of 3 listed, 1 database(s) of 1 listed\n2026/09/24 10:48:40 settings.go:1848: [INFO] [settings] restore hold CLEARED for nextcloud\n2026/09/24 10:48:40 update_guard.go:609: [INFO] [backup] nextcloud: restore completed — the update hold (set 2026-09-24T10:47:57Z) is CLEARED\n2026/09/24 10:48:40 handlers.go:2074: [INFO] [web] Tier-2 whole restore completed (async): stack=nextcloud in 33.559200635s (files restored 0, replaced 0, kept-newer 0, unchanged 142; volumes 3/3, dbs 1/1)\n",
+ "account": true,
+ "files_after": [
+ {
+ "felhom-seed-0-e19728.txt": true,
+ "felhom-seed-2-8620d3.txt": true
+ },
+ "404"
+ ],
+ "kept_beside": "",
+ "final": {
+ "state": "running",
+ "hold_reason": null,
+ "update_phase": null
+ }
+}
\ No newline at end of file
diff --git a/documentation/audits/night-2026-09-24/A2/10-held-no-copy-keep-data.log b/documentation/audits/night-2026-09-24/A2/10-held-no-copy-keep-data.log
new file mode 100644
index 00000000..3b9ad5ce
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/A2/10-held-no-copy-keep-data.log
@@ -0,0 +1,40 @@
+12:43:54 drill: redis 7-alpine -> 7.4.0-alpine + probe 8999 (the failing edge)
+12:43:59 (c) Update -> 202
+12:43:59 + 0.0s phase=backing-up
+12:44:16 + 16.8s phase=safety-dump
+12:44:18 + 18.9s phase=pulling
+12:44:21 + 22.1s phase=copying
+12:44:31 + 32.1s phase=starting
+12:44:38 + 38.9s phase=verifying
+12:44:42 >>> marker removed:
+copy: nextcloud_nextcloud_db_data.pre-update-20260924T103924Z
+data
+second-drive mirror moved aside (a one-drive box)
+
+12:46:09 + 129.7s phase=undoing
+12:47:57 + 237.4s phase=failed
+12:47:57 (c) held: {"update_phase": "failed", "hold_reason": "A(z) nextcloud frissítése nem sikerült, és az automatikus visszaállítás sem. Ezen a dobozon nincs olyan másolat, amely az alkalmazást a fájljaival együtt vissza tudná hozni. A Felhom ügyfélszolgálatát értesítettük — addig ne indítsd újra, és ne töröld az adatait.", "hold_no_whole_copy": true, "hold_kind": "update_failed", "state": "stopped"}
+12:47:57 (c) [hu] 'A(z) nextcloud frissítése nem sikerült, és az automatikus visszaállítás sem. Ezen a dobozon nincs olyan másolat, amely az alkalmazást a fájljaival együtt vissza tudná hozni. A Felhom ügyfélszolgálatát értesítettük — addig ne indítsd újra, és ne töröld az adatait.'
+12:47:57 (c) [en] 'The update of nextcloud did not work, and neither did the automatic undo. This box has no copy that can bring the app back together with its files. Felhom support has been told — until then, do not restart the app or delete its data.'
+12:47:57 (c2) hdd-data -> {"code": "200", "keep_data_only": true}
+12:47:57 (c3) [hu] Remove with data -> 409 'Amíg az ügyfélszolgálat foglalkozik az alkalmazással, az adatai nem törölhetők — az alkalmazást az adatai megtartásával eltávolíthatod.'
+12:47:57 (c3) [en] Remove with data -> 409 'While support is working on this app, its data cannot be deleted — you can remove the app and keep its data.'
+12:47:57 (c3) [en] Remove with backups -> 409
+12:47:57 (c3) still deployed: True
+12:48:00 (c4) mirror moved back: hdd recovery-unit
+12:48:01 drill: back to the head + the real probe
+12:48:06 (d) POST /backup/tier2/unit-restore -> HTTP/2 302 ['location: /backups/apps?flash=flash.restore.full_started']
+12:48:06 restore-status (True, None)
+12:48:40 restore-status (False, None)
+12:48:44 (d) log:
+2026/09/24 10:48:06 handlers.go:2063: [WARN] [web] Tier-2 WHOLE restore requested (async, files + database): stack=nextcloud from 172.18.0.9:57508
+2026/09/24 10:48:06 tier2_whole.go:304: [WARN] [backup] WHOLE restore for nextcloud from the second drive /mnt/sys_drive/felhom-data/backups/secondary/nextcloud: files by the four rules (never delete, never overwrite newer), then the unit
+2026/09/24 10:48:06 tier2_whole.go:326: [INFO] [backup] whole restore nextcloud: files restored=0 replaced=0 (live kept beside as *.felhom-20260924T104806Z) kept-newer=0 unchanged=142
+2026/09/24 10:48:40 restore_unit.go:464: [INFO] [backup] Restore-from-unit completed: nextcloud — 3 volume(s) of 3 listed, 1 database(s) of 1 listed
+2026/09/24 10:48:40 settings.go:1848: [INFO] [settings] restore hold CLEARED for nextcloud
+2026/09/24 10:48:40 update_guard.go:609: [INFO] [backup] nextcloud: restore completed — the update hold (set 2026-09-24T10:47:57Z) is CLEARED
+2026/09/24 10:48:40 handlers.go:2074: [INFO] [web] Tier-2 whole restore completed (async): stack=nextcloud in 33.559200635s (files restored 0, replaced 0, kept-newer 0, unchanged 142; volumes 3/3, dbs 1/1)
+
+12:48:51 nextcloud: readback of the seeded user found=True
+12:48:53 nextcloud files read back: 2/2 (negative control http=404)
+12:48:56 (e) final {"state": "running", "hold_reason": null, "update_phase": null} kept-beside:
diff --git a/documentation/audits/night-2026-09-24/A2/10.stdout b/documentation/audits/night-2026-09-24/A2/10.stdout
new file mode 100644
index 00000000..3b9ad5ce
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/A2/10.stdout
@@ -0,0 +1,40 @@
+12:43:54 drill: redis 7-alpine -> 7.4.0-alpine + probe 8999 (the failing edge)
+12:43:59 (c) Update -> 202
+12:43:59 + 0.0s phase=backing-up
+12:44:16 + 16.8s phase=safety-dump
+12:44:18 + 18.9s phase=pulling
+12:44:21 + 22.1s phase=copying
+12:44:31 + 32.1s phase=starting
+12:44:38 + 38.9s phase=verifying
+12:44:42 >>> marker removed:
+copy: nextcloud_nextcloud_db_data.pre-update-20260924T103924Z
+data
+second-drive mirror moved aside (a one-drive box)
+
+12:46:09 + 129.7s phase=undoing
+12:47:57 + 237.4s phase=failed
+12:47:57 (c) held: {"update_phase": "failed", "hold_reason": "A(z) nextcloud frissítése nem sikerült, és az automatikus visszaállítás sem. Ezen a dobozon nincs olyan másolat, amely az alkalmazást a fájljaival együtt vissza tudná hozni. A Felhom ügyfélszolgálatát értesítettük — addig ne indítsd újra, és ne töröld az adatait.", "hold_no_whole_copy": true, "hold_kind": "update_failed", "state": "stopped"}
+12:47:57 (c) [hu] 'A(z) nextcloud frissítése nem sikerült, és az automatikus visszaállítás sem. Ezen a dobozon nincs olyan másolat, amely az alkalmazást a fájljaival együtt vissza tudná hozni. A Felhom ügyfélszolgálatát értesítettük — addig ne indítsd újra, és ne töröld az adatait.'
+12:47:57 (c) [en] 'The update of nextcloud did not work, and neither did the automatic undo. This box has no copy that can bring the app back together with its files. Felhom support has been told — until then, do not restart the app or delete its data.'
+12:47:57 (c2) hdd-data -> {"code": "200", "keep_data_only": true}
+12:47:57 (c3) [hu] Remove with data -> 409 'Amíg az ügyfélszolgálat foglalkozik az alkalmazással, az adatai nem törölhetők — az alkalmazást az adatai megtartásával eltávolíthatod.'
+12:47:57 (c3) [en] Remove with data -> 409 'While support is working on this app, its data cannot be deleted — you can remove the app and keep its data.'
+12:47:57 (c3) [en] Remove with backups -> 409
+12:47:57 (c3) still deployed: True
+12:48:00 (c4) mirror moved back: hdd recovery-unit
+12:48:01 drill: back to the head + the real probe
+12:48:06 (d) POST /backup/tier2/unit-restore -> HTTP/2 302 ['location: /backups/apps?flash=flash.restore.full_started']
+12:48:06 restore-status (True, None)
+12:48:40 restore-status (False, None)
+12:48:44 (d) log:
+2026/09/24 10:48:06 handlers.go:2063: [WARN] [web] Tier-2 WHOLE restore requested (async, files + database): stack=nextcloud from 172.18.0.9:57508
+2026/09/24 10:48:06 tier2_whole.go:304: [WARN] [backup] WHOLE restore for nextcloud from the second drive /mnt/sys_drive/felhom-data/backups/secondary/nextcloud: files by the four rules (never delete, never overwrite newer), then the unit
+2026/09/24 10:48:06 tier2_whole.go:326: [INFO] [backup] whole restore nextcloud: files restored=0 replaced=0 (live kept beside as *.felhom-20260924T104806Z) kept-newer=0 unchanged=142
+2026/09/24 10:48:40 restore_unit.go:464: [INFO] [backup] Restore-from-unit completed: nextcloud — 3 volume(s) of 3 listed, 1 database(s) of 1 listed
+2026/09/24 10:48:40 settings.go:1848: [INFO] [settings] restore hold CLEARED for nextcloud
+2026/09/24 10:48:40 update_guard.go:609: [INFO] [backup] nextcloud: restore completed — the update hold (set 2026-09-24T10:47:57Z) is CLEARED
+2026/09/24 10:48:40 handlers.go:2074: [INFO] [web] Tier-2 whole restore completed (async): stack=nextcloud in 33.559200635s (files restored 0, replaced 0, kept-newer 0, unchanged 142; volumes 3/3, dbs 1/1)
+
+12:48:51 nextcloud: readback of the seeded user found=True
+12:48:53 nextcloud files read back: 2/2 (negative control http=404)
+12:48:56 (e) final {"state": "running", "hold_reason": null, "update_phase": null} kept-beside:
diff --git a/documentation/audits/night-2026-09-24/A2/11-applied-meta-wrong-probe.txt b/documentation/audits/night-2026-09-24/A2/11-applied-meta-wrong-probe.txt
new file mode 100644
index 00000000..d51ec417
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/A2/11-applied-meta-wrong-probe.txt
@@ -0,0 +1,62 @@
+## now: applied-meta and stack .felhom.yml probe port
+2026-09-24 10:46:18.306558751 +0000 applied-meta/.felhom.yml
+2026-09-24 10:48:16.019124190 +0000 .felhom.yml
+applied-meta/.felhom.yml:106: port: 8999
+.felhom.yml:106: port: 8999
+
+## the controller log 10:38-10:49 (nextcloud: pin, applied, undo, hold, restore)
+ 1 2026/09/24 10:38:51 sync.go:409: [INFO] [sync] Updated nextcloud/.felhom.yml
+ 1 2026/09/24 10:39:21 undo.go:273: [INFO] [stacks] update nextcloud: the undo copy will hold 3 named volume(s), 928.8 MiB
+ 1 2026/09/24 10:39:21 pin.go:373: [INFO] [stacks] update nextcloud: pin advanced to /opt/docker/felhom-controller/data/catalog-cache/templates/nextcloud/docker-compose.yml (nextcloud=nextcloud:34.0.4-apache, nextcloud-db=mariadb:12.3, nextcloud-redis=redis:7.4-alpine)
+ 1 2026/09/24 10:39:25 undo.go:423: [INFO] [stacks] update nextcloud: copied nextcloud_nextcloud_db_data → nextcloud_nextcloud_db_data.pre-update-20260924T103924Z in 950ms
+ 1 2026/09/24 10:39:28 healthprobe.go:190: [WARN] Health probe nextcloud: API GET :8999/status.php → Get "http://nextcloud:8999/status.php": dial tcp: lookup nextcloud on 127.0.0.11:53: no such host
+ 1 2026/09/24 10:39:32 undo.go:423: [INFO] [stacks] update nextcloud: copied nextcloud_nextcloud_html → nextcloud_nextcloud_html.pre-update-20260924T103924Z in 6.779s
+ 1 2026/09/24 10:39:33 undo.go:423: [INFO] [stacks] update nextcloud: copied nextcloud_nextcloud_redis_data → nextcloud_nextcloud_redis_data.pre-update-20260924T103924Z in 435ms
+ 17 2026/09/24 10:39:49 healthprobe.go:190: [WARN] Health probe nextcloud: API GET :8999/status.php → Get "http://nextcloud:8999/status.php": dial tcp 172.18.0.5:8999: connect: connection refused
+ 1 2026/09/24 10:41:10 update.go:961: [INFO] [stacks] update nextcloud: kept 6855 bytes of the app's own log at /opt/docker/stacks/nextcloud/hold-logs/20260924T104110Z/compose-logs.txt before stopping it (R-621)
+ 1 2026/09/24 10:41:10 update.go:1260: [INFO] [stacks] update nextcloud: phase undoing
+ 1 2026/09/24 10:41:10 undo.go:501: [WARN] [stacks] update nextcloud: UNDO — putting back the previous version and its 3 volume copy(ies) (reason: not healthy: not healthy within 1m30s (last: health check failing))
+ 1 2026/09/24 10:41:12 undo.go:507: [ERROR] [stacks] update nextcloud: the undo copy nextcloud_nextcloud_db_data.pre-update-20260924T103924Z has no finished-marker — it is cut off or missing; NOTHING is put back
+ 1 2026/09/24 10:41:12 update.go:981: [ERROR] [stacks] update nextcloud: the UNDO failed too (untouched) — HOLDING the app; the undo copies are kept: [{nextcloud_nextcloud_db_data nextcloud_nextcloud_db_data.pre-update-20260924T103924Z} {nextcloud_nextcloud_html nextcloud_nextcloud_html.pre-update-20260924T103924Z} {nextcloud_nextcloud_redis_data nextcloud_nextcloud_redis_data.pre-update-20260924T103924Z}]
+ 1 2026/09/24 10:41:12 settings.go:1814: [WARN] [settings] restore hold SET for nextcloud — the app stays stopped until it is cleared
+ 1 2026/09/24 10:41:12 update_guard.go:560: [WARN] [backup] nextcloud is HELD STOPPED after a failed update (restore point: tier 2 "második meghajtó", 2026-09-24T10:39:14Z; holds: "a beállításokat, az adatbázist és a fájlokat tartalmazza"; undo: "untouched")
+ 1 2026/09/24 10:41:12 settings.go:1814: [WARN] [settings] restore hold SET for nextcloud — the app stays stopped until it is cleared
+ 1 2026/09/24 10:41:12 undo.go:619: [INFO] [stacks] update nextcloud: event app_update_held (hold recorded: true)
+ 1 2026/09/24 10:41:14 sync.go:409: [INFO] [sync] Updated nextcloud/.felhom.yml
+ 1 2026/09/24 10:41:28 pin.go:93: [INFO] [stacks] pin nextcloud: nextcloud=nextcloud:34.0.4-apache, nextcloud-db=mariadb:12.3, nextcloud-redis=redis:7-alpine
+ 1 2026/09/24 10:41:52 restore_unit.go:464: [INFO] [backup] Restore-from-unit completed: nextcloud — 3 volume(s) of 3 listed, 1 database(s) of 1 listed
+ 1 2026/09/24 10:41:52 settings.go:1848: [INFO] [settings] restore hold CLEARED for nextcloud
+ 1 2026/09/24 10:41:52 update_guard.go:609: [INFO] [backup] nextcloud: restore completed — the update hold (set 2026-09-24T10:41:12Z) is CLEARED
+ 9 2026/09/24 10:41:58 healthprobe.go:190: [WARN] Health probe nextcloud: API GET :8999/status.php → Get "http://nextcloud:8999/status.php": dial tcp 172.18.0.5:8999: connect: connection refused
+ 1 2026/09/24 10:44:13 offbox_reconstitute.go:319: [INFO] [backup] nextcloud: pruned old undo copy pre-restore-20260924T102048Z-nextcloud-mariadb.sql (keeping the newest 3)
+ 1 2026/09/24 10:44:18 undo.go:273: [INFO] [stacks] update nextcloud: the undo copy will hold 3 named volume(s), 928.8 MiB
+ 1 2026/09/24 10:44:18 pin.go:373: [INFO] [stacks] update nextcloud: pin advanced to /opt/docker/felhom-controller/data/catalog-cache/templates/nextcloud/docker-compose.yml (nextcloud=nextcloud:34.0.4-apache, nextcloud-db=mariadb:12.3, nextcloud-redis=redis:7.4.0-alpine)
+ 1 2026/09/24 10:44:24 undo.go:423: [INFO] [stacks] update nextcloud: copied nextcloud_nextcloud_db_data → nextcloud_nextcloud_db_data.pre-update-20260924T104423Z in 929ms
+ 1 2026/09/24 10:44:31 undo.go:423: [INFO] [stacks] update nextcloud: copied nextcloud_nextcloud_html → nextcloud_nextcloud_html.pre-update-20260924T104423Z in 7.147s
+ 1 2026/09/24 10:44:31 undo.go:423: [INFO] [stacks] update nextcloud: copied nextcloud_nextcloud_redis_data → nextcloud_nextcloud_redis_data.pre-update-20260924T104423Z in 577ms
+ 18 2026/09/24 10:44:48 healthprobe.go:190: [WARN] Health probe nextcloud: API GET :8999/status.php → Get "http://nextcloud:8999/status.php": dial tcp 172.18.0.5:8999: connect: connection refused
+ 1 2026/09/24 10:46:08 update.go:961: [INFO] [stacks] update nextcloud: kept 6773 bytes of the app's own log at /opt/docker/stacks/nextcloud/hold-logs/20260924T104608Z/compose-logs.txt before stopping it (R-621)
+ 1 2026/09/24 10:46:08 update.go:1260: [INFO] [stacks] update nextcloud: phase undoing
+ 1 2026/09/24 10:46:08 undo.go:501: [WARN] [stacks] update nextcloud: UNDO — putting back the previous version and its 3 volume copy(ies) (reason: not healthy: not healthy within 1m30s (last: health check failing))
+ 1 2026/09/24 10:46:18 pin.go:93: [INFO] [stacks] pin nextcloud: nextcloud=nextcloud:34.0.4-apache, nextcloud-db=mariadb:12.3, nextcloud-redis=redis:7-alpine
+ 1 2026/09/24 10:46:24 [ERROR] [stacks] .felhom.yml backup block rejected in /opt/docker/stacks/nextcloud/pre-update-meta: docker-compose.yml unreadable: stat /opt/docker/stacks/nextcloud/pre-update-meta/docker-compose.yml: no such file or directory
+ 17 2026/09/24 10:46:34 healthprobe.go:190: [WARN] Health probe nextcloud: API GET :8999/status.php → Get "http://nextcloud:8999/status.php": dial tcp 172.18.0.5:8999: connect: connection refused
+ 1 2026/09/24 10:47:55 undo.go:535: [ERROR] [stacks] update nextcloud: the previous version did not come up after the undo: not healthy within 1m30s (last: health check failing)
+ 1 2026/09/24 10:47:55 update.go:961: [INFO] [stacks] update nextcloud: kept 6798 bytes of the app's own log at /opt/docker/stacks/nextcloud/hold-logs/20260924T104755Z/compose-logs.txt before stopping it (R-621)
+ 1 2026/09/24 10:47:57 update.go:981: [ERROR] [stacks] update nextcloud: the UNDO failed too (not_started) — HOLDING the app; the undo copies are kept: [{nextcloud_nextcloud_db_data nextcloud_nextcloud_db_data.pre-update-20260924T104423Z} {nextcloud_nextcloud_html nextcloud_nextcloud_html.pre-update-20260924T104423Z} {nextcloud_nextcloud_redis_data nextcloud_nextcloud_redis_data.pre-update-20260924T104423Z}]
+ 1 2026/09/24 10:47:57 settings.go:1814: [WARN] [settings] restore hold SET for nextcloud — the app stays stopped until it is cleared
+ 1 2026/09/24 10:47:57 update_guard.go:715: [ERROR] [backup] nextcloud is HELD STOPPED after a failed update and NO copy on this box brings it back whole (seen: [tier 1 at 2026-09-24T10:44:17Z]; drive files declared: true; undo: "not_started") — support must act (R-659)
+ 1 2026/09/24 10:47:57 undo.go:619: [INFO] [stacks] update nextcloud: event app_update_held (hold recorded: true)
+ 1 2026/09/24 10:48:02 sync.go:409: [INFO] [sync] Updated nextcloud/.felhom.yml
+ 1 2026/09/24 10:48:16 pin.go:93: [INFO] [stacks] pin nextcloud: nextcloud=nextcloud:34.0.4-apache, nextcloud-db=mariadb:12.3, nextcloud-redis=redis:7-alpine
+ 1 2026/09/24 10:48:40 restore_unit.go:464: [INFO] [backup] Restore-from-unit completed: nextcloud — 3 volume(s) of 3 listed, 1 database(s) of 1 listed
+ 1 2026/09/24 10:48:40 settings.go:1848: [INFO] [settings] restore hold CLEARED for nextcloud
+ 1 2026/09/24 10:48:40 update_guard.go:609: [INFO] [backup] nextcloud: restore completed — the update hold (set 2026-09-24T10:47:57Z) is CLEARED
+
+## undo-copy volumes left
+nextcloud_nextcloud_db_data.pre-update-20260924T103924Z
+nextcloud_nextcloud_db_data.pre-update-20260924T104423Z
+nextcloud_nextcloud_html.pre-update-20260924T103924Z
+nextcloud_nextcloud_html.pre-update-20260924T104423Z
+nextcloud_nextcloud_redis_data.pre-update-20260924T103924Z
+nextcloud_nextcloud_redis_data.pre-update-20260924T104423Z
diff --git a/documentation/audits/night-2026-09-24/A3/10-hub-0123-rollout.txt b/documentation/audits/night-2026-09-24/A3/10-hub-0123-rollout.txt
new file mode 100644
index 00000000..721366c0
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/A3/10-hub-0123-rollout.txt
@@ -0,0 +1,2 @@
+2026/09/24 11:39:35 [INFO] felhom-hub 0.123.0 starting
+2026/09/24 11:39:35 [INFO] [store] app_stopped_unhealthy added to 3 household(s)' notification prefs (one-time, add-only): [demo-felhom _resend-rotation-test demo-hp]
diff --git a/documentation/audits/night-2026-09-24/A3/20-gokapi-live.txt b/documentation/audits/night-2026-09-24/A3/20-gokapi-live.txt
new file mode 100644
index 00000000..32e3c216
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/A3/20-gokapi-live.txt
@@ -0,0 +1,4 @@
+2026/09/24 10:29:18 main.go:3671: [WARN] [deadapp] gokapi: crash_loop — 6 in 10m0s; STOPPING it (decision 28)
+2026/09/24 10:29:18 update_guard.go:776: [WARN] [backup] gokapi is STOPPED by the box: crash_loop (trip 1 within 24h0m0s) — Start gives it one more try (decision 28)
+
+
diff --git a/documentation/audits/night-2026-09-24/A3/21-gokapi-page-event.txt b/documentation/audits/night-2026-09-24/A3/21-gokapi-page-event.txt
new file mode 100644
index 00000000..ac61a171
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/A3/21-gokapi-page-event.txt
@@ -0,0 +1,7 @@
+API {"state": "stopped", "hold_reason": "Az alkalmazást leállítottuk, mert újra és újra összeomlott (vagy elfogyott a memóriája). Az Indítás gombbal újra megpróbálhatod.", "hold_kind": "unhealthy_stop", "update_phase": null}
+hu ('Az alkalmazást leállítottuk, mert újra és újra összeomlott (vagy elfogyott a memóriája). Az Indítás gombbal újra megpróbálhatod.', 'Indítás')
+hu app page: Az alkalmazást leállítottuk, mert újra és újra összeomlott (vagy elfogyott a memóriája). Az Indítás gombbal újra megpróbálhatod. | restore-needed badge: False
+en ('We stopped the app because it kept crashing (or ran out of memory). Press Start to try again.', 'Start')
+en app page: We stopped the app because it kept crashing (or ran out of memory). Press Start to try again. | restore-needed badge: False
+2026/09/24 10:29:18 notifier.go:1255: [WARN] notifier disabled (no hub configured): DROPPED event app_stopped_unhealthy (severity warning) — further app_stopped_unhealthy events are logged at DEBUG only
+
diff --git a/documentation/audits/night-2026-09-24/A3/30-start-then-trip2.json b/documentation/audits/night-2026-09-24/A3/30-start-then-trip2.json
new file mode 100644
index 00000000..8ea2bf43
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/A3/30-start-then-trip2.json
@@ -0,0 +1,18 @@
+{
+ "controller": "gitea.dooplex.hu/admin/felhom-controller:0.269.0",
+ "before": {
+ "state": "stopped",
+ "hold_kind": "unhealthy_stop",
+ "hold_reason": "Az alkalmazást leállítottuk, mert újra és újra összeomlott (vagy elfogyott a memóriája). Az Indítás gombbal újra megpróbálhatod."
+ },
+ "after_start": {
+ "state": "restarting",
+ "hold_kind": null,
+ "hold_reason": null
+ },
+ "tripped_after_s": 37,
+ "page_hu": "Az alkalmazást ismét leállítottuk, mert újra és újra összeomlott (vagy elfogyott a memóriája). A Felhom ügyfélszolgálatát értesítettük. Az Indítás gombbal újra megpróbálhatod.",
+ "page_en": "We stopped the app again because it kept crashing (or ran out of memory). Felhom support has been told. Press Start to try again.",
+ "log": "2026/09/24 10:37:15 settings.go:2677: [INFO] [settings] unhealthy-stop hold LIFTED for gokapi (Start)\n2026/09/24 10:37:15 router.go:615: [INFO] [api] gokapi: the unhealthy stop is LIFTED by Start — one more try (decision 28)\n2026/09/24 10:37:18 scheduler.go:67: [DEBUG] [scheduler] job deadapp-check: execution starting\n2026/09/24 10:37:48 scheduler.go:67: [DEBUG] [scheduler] job deadapp-check: execution starting\n2026/09/24 10:37:48 main.go:3671: [WARN] [deadapp] gokapi: crash_loop — 9 in 10m0s; STOPPING it (decision 28)\n2026/09/24 10:37:48 settings.go:1814: [WARN] [settings] restore hold SET for gokapi — the app stays stopped until it is cleared\n2026/09/24 10:37:48 update_guard.go:776: [WARN] [backup] gokapi is STOPPED by the box: crash_loop (trip 2 within 24h0m0s) — Start gives it one more try (decision 28)\n2026/09/24 10:37:48 notifier.go:1259: [DEBUG] notifier disabled: dropped event app_stopped_unhealthy (severity warning)\n",
+ "start_failed_lines": "0"
+}
\ No newline at end of file
diff --git a/documentation/audits/night-2026-09-24/A3/30-start-then-trip2.log b/documentation/audits/night-2026-09-24/A3/30-start-then-trip2.log
new file mode 100644
index 00000000..910345ae
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/A3/30-start-then-trip2.log
@@ -0,0 +1,18 @@
+12:37:15 before {"state": "stopped", "hold_kind": "unhealthy_stop", "hold_reason": "Az alkalmazást leállítottuk, mert újra és újra összeomlott (vagy elfogyott a memóriája). Az Indítás gombbal újra megpróbálhatod."}
+12:37:16 Start -> 200 {"ok": true, "data": {"state": "starting"}, "message": "Stack gokapi start requested — state now: starting"}
+12:37:21 after start {"state": "restarting", "hold_kind": null, "hold_reason": null}
+12:37:25 + 4s state=restarting hold=None restarts: /gokapi 6
+12:37:58 + 37s state=stopped hold=unhealthy_stop restarts:
+12:37:58 [hu] 'Az alkalmazást ismét leállítottuk, mert újra és újra összeomlott (vagy elfogyott a memóriája). A Felhom ügyfélszolgálatát értesítettük. Az Indítás gombbal újra megpróbálhatod.'
+12:37:58 [en] 'We stopped the app again because it kept crashing (or ran out of memory). Felhom support has been told. Press Start to try again.'
+12:38:02 log:
+2026/09/24 10:37:15 settings.go:2677: [INFO] [settings] unhealthy-stop hold LIFTED for gokapi (Start)
+2026/09/24 10:37:15 router.go:615: [INFO] [api] gokapi: the unhealthy stop is LIFTED by Start — one more try (decision 28)
+2026/09/24 10:37:18 scheduler.go:67: [DEBUG] [scheduler] job deadapp-check: execution starting
+2026/09/24 10:37:48 scheduler.go:67: [DEBUG] [scheduler] job deadapp-check: execution starting
+2026/09/24 10:37:48 main.go:3671: [WARN] [deadapp] gokapi: crash_loop — 9 in 10m0s; STOPPING it (decision 28)
+2026/09/24 10:37:48 settings.go:1814: [WARN] [settings] restore hold SET for gokapi — the app stays stopped until it is cleared
+2026/09/24 10:37:48 update_guard.go:776: [WARN] [backup] gokapi is STOPPED by the box: crash_loop (trip 2 within 24h0m0s) — Start gives it one more try (decision 28)
+2026/09/24 10:37:48 notifier.go:1259: [DEBUG] notifier disabled: dropped event app_stopped_unhealthy (severity warning)
+
+12:38:05 app_start_failed lines: 0
diff --git a/documentation/audits/night-2026-09-24/A3/30.stdout b/documentation/audits/night-2026-09-24/A3/30.stdout
new file mode 100644
index 00000000..910345ae
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/A3/30.stdout
@@ -0,0 +1,18 @@
+12:37:15 before {"state": "stopped", "hold_kind": "unhealthy_stop", "hold_reason": "Az alkalmazást leállítottuk, mert újra és újra összeomlott (vagy elfogyott a memóriája). Az Indítás gombbal újra megpróbálhatod."}
+12:37:16 Start -> 200 {"ok": true, "data": {"state": "starting"}, "message": "Stack gokapi start requested — state now: starting"}
+12:37:21 after start {"state": "restarting", "hold_kind": null, "hold_reason": null}
+12:37:25 + 4s state=restarting hold=None restarts: /gokapi 6
+12:37:58 + 37s state=stopped hold=unhealthy_stop restarts:
+12:37:58 [hu] 'Az alkalmazást ismét leállítottuk, mert újra és újra összeomlott (vagy elfogyott a memóriája). A Felhom ügyfélszolgálatát értesítettük. Az Indítás gombbal újra megpróbálhatod.'
+12:37:58 [en] 'We stopped the app again because it kept crashing (or ran out of memory). Felhom support has been told. Press Start to try again.'
+12:38:02 log:
+2026/09/24 10:37:15 settings.go:2677: [INFO] [settings] unhealthy-stop hold LIFTED for gokapi (Start)
+2026/09/24 10:37:15 router.go:615: [INFO] [api] gokapi: the unhealthy stop is LIFTED by Start — one more try (decision 28)
+2026/09/24 10:37:18 scheduler.go:67: [DEBUG] [scheduler] job deadapp-check: execution starting
+2026/09/24 10:37:48 scheduler.go:67: [DEBUG] [scheduler] job deadapp-check: execution starting
+2026/09/24 10:37:48 main.go:3671: [WARN] [deadapp] gokapi: crash_loop — 9 in 10m0s; STOPPING it (decision 28)
+2026/09/24 10:37:48 settings.go:1814: [WARN] [settings] restore hold SET for gokapi — the app stays stopped until it is cleared
+2026/09/24 10:37:48 update_guard.go:776: [WARN] [backup] gokapi is STOPPED by the box: crash_loop (trip 2 within 24h0m0s) — Start gives it one more try (decision 28)
+2026/09/24 10:37:48 notifier.go:1259: [DEBUG] notifier disabled: dropped event app_stopped_unhealthy (severity warning)
+
+12:38:05 app_start_failed lines: 0
diff --git a/documentation/audits/night-2026-09-24/A3/40-first-start-restarts.txt b/documentation/audits/night-2026-09-24/A3/40-first-start-restarts.txt
new file mode 100644
index 00000000..6e4d53d8
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/A3/40-first-start-restarts.txt
@@ -0,0 +1,17 @@
+# Does a crash-loop threshold separate a crash loop from a slow first start? (brief: "check against every
+# app's first-start restarts in the drill evidence"). Read 2026-09-24 12:50 CEST over documentation/audits/.
+$ grep -rhoE '"restarts": *[0-9]+|RestartCount[=: ]+[0-9]+' documentation/audits | sort | uniq -c
+ 1831 "restarts": 0 <- harness state samples (172 files: bench to-/abort-states, memory samples)
+ 11 RestartCount=0
+ 1 RestartCount=9 SPIKE-app-update-2026-09-01.md:225 — a deliberately broken image (alpine:3.20), 37 s after start
+ 1 RestartCount=6 evidence-chaos-night-2026-09-17/round-1-notes.txt:18 — nextcloud, corrupt image layer (ExitCode 127): BROKEN
+ 1 RestartCount=12 evidence-chaos-night-2026-09-17/phase0-recovery.txt:213 — immich microservices, DB connection dropped at first-start import on a 6 GB guest; left broken that night
+ 1 RestartCount 9 evidence-drill-0243-2026-09-16/phase2-m1-oom.txt:23 — paperless at a 128M cap: OOM loop
+Also measured tonight: 40 containers on both demo boxes + 9202 — no healthy container above 1 restart (A3/02).
+
+VERDICT: no sample shows a HEALTHY app restarting during a first start. Every count >= 6 is a broken app
+(crash image, corrupt layer, OOM cap) — the thing decision 28 stops. The one borderline case is immich's
+first-start import (12 restarts): it did not recover that night, so stopping it would have been correct,
+but it is the shape a slow-but-recovering first start would take. Row filed (immich first-start watch).
+Docker's back-off matters more than the count: a FRESH container restarts fast (9 in 32 s, A3/30), a
+long-running loop slows to ~1/min (A3/04) — so 10 in 10 min misses a steady loop; 6 catches both.
diff --git a/documentation/audits/night-2026-09-24/B/01-compose-accepts-digests.txt b/documentation/audits/night-2026-09-24/B/01-compose-accepts-digests.txt
new file mode 100644
index 00000000..a5964d31
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/B/01-compose-accepts-digests.txt
@@ -0,0 +1,46 @@
+# Part B — does Docker Compose accept tag@digest in EVERY template's image line? (2026-09-24T12:45:17+02:00)
+# Method: a temporary (uncommitted, deleted) go test called the product's stacks.RenderWithLadderDigests over every
+# live-catalog template @ cf7cf84; each rendered file and its original parsed with
+# 'docker compose -f X config -q --no-interpolate' (parse only, no container; DooPlex Docker Compose version v5.3.1).
+templates rendered: 53; carrying @sha256 after render: 25 apps
+every tested digest of the head's ladder entry present in the render: 25 apps, 37 digests, 0 missing
+compose config: 25 ok, 0 differ from the original's result
+
+## the rendered image lines
+actualbudget.yml: image: actualbudget/actual-server:26.9.0@sha256:552beab3dec8c93d46b8b9245612d63c3f123b8a45063a474f53e229b17621d3
+bookstack.yml: image: lscr.io/linuxserver/bookstack:26.05.5@sha256:189c796273469115cf810e53f450e15b93184018e4728cd65fcaef8aa93584bc
+bookstack.yml: image: mariadb:12.3@sha256:805c8e104bd563d5bfa24fadd3f31cd419ea859cb5277f32b5dbf2db714f9ed1
+ghost.yml: image: ghost:6.65.0-alpine@sha256:65788e5e2c5478319f8fc65ac12816dbc941d1a9c834c87190cf1d2d1b79d4e2
+navidrome.yml: image: deluan/navidrome:0.64.1@sha256:df22d661b8d0322c33754d999b567150df9e131a47ae53be71fed0154bf8cef5
+grafana.yml: image: grafana/grafana:13.2.2@sha256:ac461fb352abc50da10a51c7d02462e9c05488f11f53f14b3ad79a8145f638a0
+mealie.yml: image: ghcr.io/mealie-recipes/mealie:v3.27.0@sha256:ba24b88462380fb59a6c7d04c6d9e607e0b6b04e31306592181186bcdab1952b
+nextcloud.yml: image: nextcloud:34.0.4-apache@sha256:a5ace30c695afe48c2c406e940ee7886a81e13fa382e57cd68b2416d1a66914c
+nextcloud.yml: image: mariadb:12.3@sha256:805c8e104bd563d5bfa24fadd3f31cd419ea859cb5277f32b5dbf2db714f9ed1
+nextcloud.yml: image: redis:7-alpine@sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499
+komga.yml: image: gotson/komga:1.27.1@sha256:9cf102f5fb78e2e020700c53eca757e4f4d4e347121b0a16380822760337cca9
+wishlist.yml: image: ghcr.io/cmintey/wishlist:v0.67.1@sha256:4b89bfb5b57ffb087c33978d21b09048e5c37f4506326e9ccd2705a0a1d86548
+radarr.yml: image: lscr.io/linuxserver/radarr:6.4.4@sha256:adb6c09d6b729ea5e642c99cea35af72702ef476bf4763f153299ac5db9f0b4f
+romm.yml: image: rommapp/romm:5.3.1@sha256:0d66b4ea152a237c7f24b95e87459f46d3c596a7c0b35a82f26bc971487278b3
+romm.yml: image: mariadb:11.8@sha256:79d59758afc91b89b120b0a8904d637f5a3b3e1c4900f29b740d6d46c72fef68
+romm.yml: image: redis:7-alpine@sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499
+privatebin.yml: image: privatebin/pdo:2.0.6@sha256:4c141b2326f8b353598ce9ce7507a9cfecf2dad5c60a39fea903d430e296d8f5
+audiobookshelf.yml: image: ghcr.io/advplyr/audiobookshelf:2.36.1@sha256:3528a93b6442ffe54bd46771bbbab7c97084e1101071586d9dc2254f30bb4358
+vikunja.yml: image: vikunja/vikunja:2.6.0@sha256:417ada6f94e81f0267aa2f007d0a811fc82d38dd2aa58351e3ea520ca01c2ea5
+docmost.yml: image: docmost/docmost:0.96.0@sha256:b56947fcfd08aab8fae12a377e1792784786adbf8b96e4281f14ef4fc072685a
+docmost.yml: image: postgres:16-alpine@sha256:721873c34ceb9f8d8fc265984940dc982404c105f19ad51be9fdc5970a6080ea
+docmost.yml: image: redis:7-alpine@sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499
+home-assistant.yml: image: ghcr.io/home-assistant/home-assistant:2026.9.3@sha256:d8922685169707fd91e8b9729902d975f06157d005e422874d201e0261dda196
+immich.yml: image: ghcr.io/immich-app/immich-server:v3.2.2@sha256:79cc1623323d5894922686d8743b4780181428f98eecbfb58ce12c41ef02d1ea
+immich.yml: image: ghcr.io/immich-app/immich-machine-learning:v3.2.2@sha256:60dfcf266a9ef3b7376f5678e8c980d4fb61db5fc48c078fe8a326ab1535d60d
+immich.yml: image: ghcr.io/immich-app/postgres:16-vectorchord0.4.3-pgvectors0.2.0@sha256:1a078b237c1d9b420b0ee59147386b4aa60d3a07a8e6a402fc84a57e41b043a4
+immich.yml: image: redis:7-alpine@sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499
+opengist.yml: image: ghcr.io/thomiceli/opengist:1.15@sha256:7edc91273ee7d10a17406da8a08c803158baaff984c39b789c1313473d068f21
+papra.yml: image: ghcr.io/papra-hq/papra:26.6.2-rootless@sha256:a281cb44176dbe5323e0f7ea2d6fd34d58914a3a8525c36437a086d1d7c4fef8
+sonarr.yml: image: lscr.io/linuxserver/sonarr:4.0.20@sha256:a5c1a5fecbef946927ab90ad68df319ac5fe644057e5fc18cd993f01ac07b2b2
+termix.yml: image: ghcr.io/lukegus/termix:2.8.0@sha256:25e8a0eb39f45c9ac5e8e7615fd84a0380018ea012317bc665b86458d900b4b9
+emby.yml: image: emby/embyserver:4.11.0.3@sha256:2ff130d624a97782474e7de791875d2871d613b2afaddc73b3e11eb0c9caa770
+kimai.yml: image: kimai/kimai2:apache-2.57.0@sha256:efa66c5eadf948b94d8031e9436feae491d90b2ceaa3d5e45e4d7c1a59dd8c38
+kimai.yml: image: mariadb:11.8@sha256:79d59758afc91b89b120b0a8904d637f5a3b3e1c4900f29b740d6d46c72fef68
+n8n.yml: image: n8nio/n8n:2.41.1@sha256:52f147bba97908fe58e616738f2f0485696ac7b5670a1baf169ccf1ff3173de7
+tandoor.yml: image: ghcr.io/tandoorrecipes/recipes:2.6.15@sha256:2e759dd1478a2ed119ee474e28522079fb1cfa50b3fd25cba89f6b9a67abad72
+tandoor.yml: image: postgres:16-alpine@sha256:721873c34ceb9f8d8fc265984940dc982404c105f19ad51be9fdc5970a6080ea
diff --git a/documentation/audits/night-2026-09-24/B/10-floating-tag.json b/documentation/audits/night-2026-09-24/B/10-floating-tag.json
new file mode 100644
index 00000000..a80b9bbe
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/B/10-floating-tag.json
@@ -0,0 +1,224 @@
+{
+ "controller": "gitea.dooplex.hu/admin/felhom-controller:0.269.0",
+ "1-control": {
+ "state": "running",
+ "installed": {
+ "nextcloud": {
+ "ref": "nextcloud:34.0.4-apache",
+ "digest": "sha256:a5ace30c695afe48c2c406e940ee7886a81e13fa382e57cd68b2416d1a66914c",
+ "at": "2026-09-24T10:48:31Z"
+ },
+ "nextcloud-db": {
+ "ref": "mariadb:12.3",
+ "digest": "sha256:805c8e104bd563d5bfa24fadd3f31cd419ea859cb5277f32b5dbf2db714f9ed1",
+ "at": "2026-09-24T10:48:31Z"
+ },
+ "nextcloud-redis": {
+ "ref": "redis:7-alpine",
+ "digest": "sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499",
+ "at": "2026-09-24T10:48:31Z"
+ }
+ },
+ "pinned": {
+ "nextcloud": "nextcloud:34.0.4-apache",
+ "nextcloud-db": "mariadb:12.3",
+ "nextcloud-redis": "redis:7-alpine"
+ },
+ "catalog_images": {
+ "nextcloud": "nextcloud:34.0.4-apache",
+ "nextcloud-db": "mariadb:12.3",
+ "nextcloud-redis": "redis:7-alpine"
+ },
+ "catalog_digests": {
+ "nextcloud": "sha256:a5ace30c695afe48c2c406e940ee7886a81e13fa382e57cd68b2416d1a66914c",
+ "nextcloud-db": "sha256:805c8e104bd563d5bfa24fadd3f31cd419ea859cb5277f32b5dbf2db714f9ed1",
+ "nextcloud-redis": "sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499"
+ },
+ "catalog_tested_at": "2026-09-23T20:16:04Z",
+ "badges": {
+ "hu": [
+ {
+ "title": "Ez az alkalmazás a legfrissebb elérhető változatot futtatja.",
+ "text": "Naprakész"
+ }
+ ],
+ "en": [
+ {
+ "title": "This app is running the newest version available.",
+ "text": "Up to date"
+ }
+ ]
+ },
+ "compose_redis": "103: image: redis:7-alpine@sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499",
+ "running_redis": "redis:7-alpine@sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499 sha256:f84b0c4678011602b9b98c227a4dcd5468bf8b088b02fdd4165cb7758bad8058"
+ },
+ "ladder_edit": {
+ "old": "sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499",
+ "new": "sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098",
+ "tested_at": "2026-09-24T10:50:04Z"
+ },
+ "2-behind": {
+ "state": "running",
+ "installed": {
+ "nextcloud": {
+ "ref": "nextcloud:34.0.4-apache",
+ "digest": "sha256:a5ace30c695afe48c2c406e940ee7886a81e13fa382e57cd68b2416d1a66914c",
+ "at": "2026-09-24T10:48:31Z"
+ },
+ "nextcloud-db": {
+ "ref": "mariadb:12.3",
+ "digest": "sha256:805c8e104bd563d5bfa24fadd3f31cd419ea859cb5277f32b5dbf2db714f9ed1",
+ "at": "2026-09-24T10:48:31Z"
+ },
+ "nextcloud-redis": {
+ "ref": "redis:7-alpine",
+ "digest": "sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499",
+ "at": "2026-09-24T10:48:31Z"
+ }
+ },
+ "pinned": {
+ "nextcloud": "nextcloud:34.0.4-apache",
+ "nextcloud-db": "mariadb:12.3",
+ "nextcloud-redis": "redis:7-alpine"
+ },
+ "catalog_images": {
+ "nextcloud": "nextcloud:34.0.4-apache",
+ "nextcloud-db": "mariadb:12.3",
+ "nextcloud-redis": "redis:7-alpine"
+ },
+ "catalog_digests": {
+ "nextcloud": "sha256:a5ace30c695afe48c2c406e940ee7886a81e13fa382e57cd68b2416d1a66914c",
+ "nextcloud-db": "sha256:805c8e104bd563d5bfa24fadd3f31cd419ea859cb5277f32b5dbf2db714f9ed1",
+ "nextcloud-redis": "sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098"
+ },
+ "catalog_tested_at": "2026-09-24T10:50:04Z",
+ "badges": {
+ "hu": [
+ {
+ "title": "Újabb változat érhető el ehhez az alkalmazáshoz. A frissítés indításához nyomd meg a Frissítés gombot.",
+ "text": "Frissítés elérhető — 1 napja"
+ }
+ ],
+ "en": [
+ {
+ "title": "A newer version of this app is available. Select the Update button to start it.",
+ "text": "Update available — 1 day ago"
+ }
+ ]
+ },
+ "compose_redis": "103: image: redis:7-alpine@sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098",
+ "running_redis": "redis:7-alpine@sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499 sha256:f84b0c4678011602b9b98c227a4dcd5468bf8b088b02fdd4165cb7758bad8058"
+ },
+ "update": {
+ "accepted": true,
+ "http": "202",
+ "phases": [
+ {
+ "t": 0.0,
+ "phase": "backing-up",
+ "label": "Biztonsági mentés készül a frissítés előtt…",
+ "updating": true,
+ "error": null,
+ "hold": null
+ },
+ {
+ "t": 16.4,
+ "phase": "safety-dump",
+ "label": "Adatbázis pillanatkép…",
+ "updating": true,
+ "error": null,
+ "hold": null
+ },
+ {
+ "t": 18.5,
+ "phase": "copying",
+ "label": "Az adatok másolása a frissítés előtt…",
+ "updating": true,
+ "error": null,
+ "hold": null
+ },
+ {
+ "t": 27.7,
+ "phase": "starting",
+ "label": "Indítás az új verzióval…",
+ "updating": true,
+ "error": null,
+ "hold": null
+ },
+ {
+ "t": 33.9,
+ "phase": "verifying",
+ "label": "Működés ellenőrzése…",
+ "updating": true,
+ "error": null,
+ "hold": null
+ },
+ {
+ "t": 45.1,
+ "phase": "done",
+ "label": "Frissítve",
+ "updating": false,
+ "error": null,
+ "hold": null
+ }
+ ],
+ "duration_s": 45.2,
+ "final_phase": "done",
+ "update_error": null,
+ "hold_reason": null,
+ "state": "running"
+ },
+ "3-after": {
+ "state": "running",
+ "installed": {
+ "nextcloud": {
+ "ref": "nextcloud:34.0.4-apache",
+ "digest": "sha256:a5ace30c695afe48c2c406e940ee7886a81e13fa382e57cd68b2416d1a66914c",
+ "at": "2026-09-24T10:48:31Z"
+ },
+ "nextcloud-db": {
+ "ref": "mariadb:12.3",
+ "digest": "sha256:805c8e104bd563d5bfa24fadd3f31cd419ea859cb5277f32b5dbf2db714f9ed1",
+ "at": "2026-09-24T10:48:31Z"
+ },
+ "nextcloud-redis": {
+ "ref": "redis:7-alpine",
+ "digest": "sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098",
+ "at": "2026-09-24T10:50:50Z"
+ }
+ },
+ "pinned": {
+ "nextcloud": "nextcloud:34.0.4-apache",
+ "nextcloud-db": "mariadb:12.3",
+ "nextcloud-redis": "redis:7-alpine"
+ },
+ "catalog_images": {
+ "nextcloud": "nextcloud:34.0.4-apache",
+ "nextcloud-db": "mariadb:12.3",
+ "nextcloud-redis": "redis:7-alpine"
+ },
+ "catalog_digests": {
+ "nextcloud": "sha256:a5ace30c695afe48c2c406e940ee7886a81e13fa382e57cd68b2416d1a66914c",
+ "nextcloud-db": "sha256:805c8e104bd563d5bfa24fadd3f31cd419ea859cb5277f32b5dbf2db714f9ed1",
+ "nextcloud-redis": "sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098"
+ },
+ "catalog_tested_at": "2026-09-24T10:50:04Z",
+ "badges": {
+ "hu": [
+ {
+ "title": "Ez az alkalmazás a legfrissebb elérhető változatot futtatja.",
+ "text": "Naprakész"
+ }
+ ],
+ "en": [
+ {
+ "title": "This app is running the newest version available.",
+ "text": "Up to date"
+ }
+ ]
+ },
+ "compose_redis": "103: image: redis:7-alpine@sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098",
+ "running_redis": "redis:7-alpine@sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098 sha256:7d06252fad43690f61cbb858628b933fd85f048aaba4c05e577274b2c77cd7cb"
+ },
+ "pull_log": "2026/09/24 10:43:59 update.go:762: [INFO] [stacks] update nextcloud: ladder — the installed version nextcloud=nextcloud:34.0.4-apache, nextcloud-db=mariadb:12.3, nextcloud-redis=redis:7-alpine matches no update_ladder entry (2 entries) — an app older than the ladder has no record to climb; the catalog's current definition\n2026/09/24 10:44:17 update.go:807: [INFO] [stacks] update nextcloud: safety dump done (1 file(s)) [/mnt/felhom-drives/scratch_hdd/userdata/nextcloud/backups/primary/nextcloud/db-dumps/pre-restore-20260924T104416Z-nextcloud-mariadb.sql]\n2026/09/24 10:44:18 pin.go:373: [INFO] [stacks] update nextcloud: pin advanced to /opt/docker/felhom-controller/data/catalog-cache/templates/nextcloud/docker-compose.yml (nextcloud=nextcloud:34.0.4-apache, nextcloud-db=mariadb:12.3, nextcloud-redis=redis:7.4.0-alpine)\n2026/09/24 10:44:18 update.go:1260: [INFO] [stacks] update nextcloud: phase pulling\n2026/09/24 10:48:31 installed.go:420: [INFO] [stacks] installed-images nextcloud: recorded 3 service(s) (nextcloud=nextcloud:34.0.4-apache (sha256:a5ace30c695a…), nextcloud-db=mariadb:12.3 (sha256:805c8e104bd5…), nextcloud-redis=redis:7-alpine (sha256:858f009f9709…))\n2026/09/24 10:50:37 update.go:762: [INFO] [stacks] update nextcloud: ladder — the installed version nextcloud=nextcloud:34.0.4-apache, nextcloud-db=mariadb:12.3, nextcloud-redis=redis:7-alpine matches no update_ladder entry (2 entries) — an app older than the ladder has no record to climb; the catalog's current definition\n2026/09/24 10:50:50 installed.go:420: [INFO] [stacks] installed-images nextcloud: recorded 3 service(s) (nextcloud=nextcloud:34.0.4-apache (sha256:a5ace30c695a…), nextcloud-db=mariadb:12.3 (sha256:805c8e104bd5…), nextcloud-redis=redis:7-alpine (sha256:c35af3bbcef5…))\n2026/09/24 10:50:54 update.go:807: [INFO] [stacks] update nextcloud: safety dump done (1 file(s)) [/mnt/felhom-drives/scratch_hdd/userdata/nextcloud/backups/primary/nextcloud/db-dumps/pre-restore-20260924T105053Z-nextcloud-mariadb.sql]\n2026/09/24 10:50:55 pin.go:373: [INFO] [stacks] update nextcloud: pin advanced to /opt/docker/felhom-controller/data/catalog-cache/templates/nextcloud/docker-compose.yml (nextcloud=nextcloud:34.0.4-apache, nextcloud-db=mariadb:12.3, nextcloud-redis=redis:7-alpine)\n2026/09/24 10:50:55 update.go:1260: [INFO] [stacks] update nextcloud: phase pulling\n"
+}
\ No newline at end of file
diff --git a/documentation/audits/night-2026-09-24/B/10-floating-tag.log b/documentation/audits/night-2026-09-24/B/10-floating-tag.log
new file mode 100644
index 00000000..811ba057
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/B/10-floating-tag.log
@@ -0,0 +1,28 @@
+12:50:04 [1-control] {"state": "running", "badges": {"hu": [{"title": "Ez az alkalmazás a legfrissebb elérhető változatot futtatja.", "text": "Naprakész"}], "en": [{"title": "This app is running the newest version available.", "text": "Up to date"}]}, "compose_redis": "103: image: redis:7-alpine@sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499", "running_redis": "redis:7-alpine@sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499 sha256:f84b0c4678011602b9b98c227a4dcd5468bf8b088b02fdd4165cb7758bad8058"}
+12:50:04 [1-control] installed={"nextcloud": {"ref": "nextcloud:34.0.4-apache", "digest": "sha256:a5ace30c695afe48c2c406e940ee7886a81e13fa382e57cd68b2416d1a66914c", "at": "2026-09-24T10:48:31Z"}, "nextcloud-db": {"ref": "mariadb:12.3", "digest": "sha256:805c8e104bd563d5bfa24fadd3f31cd419ea859cb5277f32b5dbf2db714f9ed1", "at": "2026-09-24T10:48:31Z"}, "nextcloud-redis": {"ref": "redis:7-alpine", "digest": "sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499", "at": "2026-09-24T10:48:31Z"}} pinned={"nextcloud": "nextcloud:34.0.4-apache", "nextcloud-db": "mariadb:12.3", "nextcloud-redis": "redis:7-alpine"}
+12:50:05 drill: ladder head entry digest sha256:858f009f9709 -> sha256:c35af3bbcef5
+12:50:16 [2-behind] {"state": "running", "badges": {"hu": [{"title": "Ez az alkalmazás a legfrissebb elérhető változatot futtatja.", "text": "Naprakész"}], "en": [{"title": "This app is running the newest version available.", "text": "Up to date"}]}, "compose_redis": "103: image: redis:7-alpine@sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499", "running_redis": "redis:7-alpine@sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499 sha256:f84b0c4678011602b9b98c227a4dcd5468bf8b088b02fdd4165cb7758bad8058"}
+12:50:16 [2-behind] installed={"nextcloud": {"ref": "nextcloud:34.0.4-apache", "digest": "sha256:a5ace30c695afe48c2c406e940ee7886a81e13fa382e57cd68b2416d1a66914c", "at": "2026-09-24T10:48:31Z"}, "nextcloud-db": {"ref": "mariadb:12.3", "digest": "sha256:805c8e104bd563d5bfa24fadd3f31cd419ea859cb5277f32b5dbf2db714f9ed1", "at": "2026-09-24T10:48:31Z"}, "nextcloud-redis": {"ref": "redis:7-alpine", "digest": "sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499", "at": "2026-09-24T10:48:31Z"}} pinned={"nextcloud": "nextcloud:34.0.4-apache", "nextcloud-db": "mariadb:12.3", "nextcloud-redis": "redis:7-alpine"}
+12:50:37 [2-behind] {"state": "running", "badges": {"hu": [{"title": "Újabb változat érhető el ehhez az alkalmazáshoz. A frissítés indításához nyomd meg a Frissítés gombot.", "text": "Frissítés elérhető — 1 napja"}], "en": [{"title": "A newer version of this app is available. Select the Update button to start it.", "text": "Update available — 1 day ago"}]}, "compose_redis": "103: image: redis:7-alpine@sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098", "running_redis": "redis:7-alpine@sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499 sha256:f84b0c4678011602b9b98c227a4dcd5468bf8b088b02fdd4165cb7758bad8058"}
+12:50:37 [2-behind] installed={"nextcloud": {"ref": "nextcloud:34.0.4-apache", "digest": "sha256:a5ace30c695afe48c2c406e940ee7886a81e13fa382e57cd68b2416d1a66914c", "at": "2026-09-24T10:48:31Z"}, "nextcloud-db": {"ref": "mariadb:12.3", "digest": "sha256:805c8e104bd563d5bfa24fadd3f31cd419ea859cb5277f32b5dbf2db714f9ed1", "at": "2026-09-24T10:48:31Z"}, "nextcloud-redis": {"ref": "redis:7-alpine", "digest": "sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499", "at": "2026-09-24T10:48:31Z"}} pinned={"nextcloud": "nextcloud:34.0.4-apache", "nextcloud-db": "mariadb:12.3", "nextcloud-redis": "redis:7-alpine"}
+12:50:37 [6] Update -> 202 {'ok': True, 'data': {'accepted': True, 'completed': False}, 'message': 'Frissítés elindult – az állapot a kártyán követhető'}
+12:50:37 + 0.0s phase=backing-up label=Biztonsági mentés készül a frissítés előtt… err=None hold=None
+12:50:54 + 16.4s phase=safety-dump label=Adatbázis pillanatkép… err=None hold=None
+12:50:56 + 18.5s phase=copying label=Az adatok másolása a frissítés előtt… err=None hold=None
+12:51:05 + 27.7s phase=starting label=Indítás az új verzióval… err=None hold=None
+12:51:11 + 33.9s phase=verifying label=Működés ellenőrzése… err=None hold=None
+12:51:23 + 45.1s phase=done label=Frissítve err=None hold=None
+12:51:34 [3-after] {"state": "running", "badges": {"hu": [{"title": "Ez az alkalmazás a legfrissebb elérhető változatot futtatja.", "text": "Naprakész"}], "en": [{"title": "This app is running the newest version available.", "text": "Up to date"}]}, "compose_redis": "103: image: redis:7-alpine@sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098", "running_redis": "redis:7-alpine@sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098 sha256:7d06252fad43690f61cbb858628b933fd85f048aaba4c05e577274b2c77cd7cb"}
+12:51:34 [3-after] installed={"nextcloud": {"ref": "nextcloud:34.0.4-apache", "digest": "sha256:a5ace30c695afe48c2c406e940ee7886a81e13fa382e57cd68b2416d1a66914c", "at": "2026-09-24T10:48:31Z"}, "nextcloud-db": {"ref": "mariadb:12.3", "digest": "sha256:805c8e104bd563d5bfa24fadd3f31cd419ea859cb5277f32b5dbf2db714f9ed1", "at": "2026-09-24T10:48:31Z"}, "nextcloud-redis": {"ref": "redis:7-alpine", "digest": "sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098", "at": "2026-09-24T10:50:50Z"}} pinned={"nextcloud": "nextcloud:34.0.4-apache", "nextcloud-db": "mariadb:12.3", "nextcloud-redis": "redis:7-alpine"}
+12:51:37 log:
+2026/09/24 10:43:59 update.go:762: [INFO] [stacks] update nextcloud: ladder — the installed version nextcloud=nextcloud:34.0.4-apache, nextcloud-db=mariadb:12.3, nextcloud-redis=redis:7-alpine matches no update_ladder entry (2 entries) — an app older than the ladder has no record to climb; the catalog's current definition
+2026/09/24 10:44:17 update.go:807: [INFO] [stacks] update nextcloud: safety dump done (1 file(s)) [/mnt/felhom-drives/scratch_hdd/userdata/nextcloud/backups/primary/nextcloud/db-dumps/pre-restore-20260924T104416Z-nextcloud-mariadb.sql]
+2026/09/24 10:44:18 pin.go:373: [INFO] [stacks] update nextcloud: pin advanced to /opt/docker/felhom-controller/data/catalog-cache/templates/nextcloud/docker-compose.yml (nextcloud=nextcloud:34.0.4-apache, nextcloud-db=mariadb:12.3, nextcloud-redis=redis:7.4.0-alpine)
+2026/09/24 10:44:18 update.go:1260: [INFO] [stacks] update nextcloud: phase pulling
+2026/09/24 10:48:31 installed.go:420: [INFO] [stacks] installed-images nextcloud: recorded 3 service(s) (nextcloud=nextcloud:34.0.4-apache (sha256:a5ace30c695a…), nextcloud-db=mariadb:12.3 (sha256:805c8e104bd5…), nextcloud-redis=redis:7-alpine (sha256:858f009f9709…))
+2026/09/24 10:50:37 update.go:762: [INFO] [stacks] update nextcloud: ladder — the installed version nextcloud=nextcloud:34.0.4-apache, nextcloud-db=mariadb:12.3, nextcloud-redis=redis:7-alpine matches no update_ladder entry (2 entries) — an app older than the ladder has no record to climb; the catalog's current definition
+2026/09/24 10:50:50 installed.go:420: [INFO] [stacks] installed-images nextcloud: recorded 3 service(s) (nextcloud=nextcloud:34.0.4-apache (sha256:a5ace30c695a…), nextcloud-db=mariadb:12.3 (sha256:805c8e104bd5…), nextcloud-redis=redis:7-alpine (sha256:c35af3bbcef5…))
+2026/09/24 10:50:54 update.go:807: [INFO] [stacks] update nextcloud: safety dump done (1 file(s)) [/mnt/felhom-drives/scratch_hdd/userdata/nextcloud/backups/primary/nextcloud/db-dumps/pre-restore-20260924T105053Z-nextcloud-mariadb.sql]
+2026/09/24 10:50:55 pin.go:373: [INFO] [stacks] update nextcloud: pin advanced to /opt/docker/felhom-controller/data/catalog-cache/templates/nextcloud/docker-compose.yml (nextcloud=nextcloud:34.0.4-apache, nextcloud-db=mariadb:12.3, nextcloud-redis=redis:7-alpine)
+2026/09/24 10:50:55 update.go:1260: [INFO] [stacks] update nextcloud: phase pulling
+
diff --git a/documentation/audits/night-2026-09-24/B/10.stdout b/documentation/audits/night-2026-09-24/B/10.stdout
new file mode 100644
index 00000000..811ba057
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/B/10.stdout
@@ -0,0 +1,28 @@
+12:50:04 [1-control] {"state": "running", "badges": {"hu": [{"title": "Ez az alkalmazás a legfrissebb elérhető változatot futtatja.", "text": "Naprakész"}], "en": [{"title": "This app is running the newest version available.", "text": "Up to date"}]}, "compose_redis": "103: image: redis:7-alpine@sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499", "running_redis": "redis:7-alpine@sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499 sha256:f84b0c4678011602b9b98c227a4dcd5468bf8b088b02fdd4165cb7758bad8058"}
+12:50:04 [1-control] installed={"nextcloud": {"ref": "nextcloud:34.0.4-apache", "digest": "sha256:a5ace30c695afe48c2c406e940ee7886a81e13fa382e57cd68b2416d1a66914c", "at": "2026-09-24T10:48:31Z"}, "nextcloud-db": {"ref": "mariadb:12.3", "digest": "sha256:805c8e104bd563d5bfa24fadd3f31cd419ea859cb5277f32b5dbf2db714f9ed1", "at": "2026-09-24T10:48:31Z"}, "nextcloud-redis": {"ref": "redis:7-alpine", "digest": "sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499", "at": "2026-09-24T10:48:31Z"}} pinned={"nextcloud": "nextcloud:34.0.4-apache", "nextcloud-db": "mariadb:12.3", "nextcloud-redis": "redis:7-alpine"}
+12:50:05 drill: ladder head entry digest sha256:858f009f9709 -> sha256:c35af3bbcef5
+12:50:16 [2-behind] {"state": "running", "badges": {"hu": [{"title": "Ez az alkalmazás a legfrissebb elérhető változatot futtatja.", "text": "Naprakész"}], "en": [{"title": "This app is running the newest version available.", "text": "Up to date"}]}, "compose_redis": "103: image: redis:7-alpine@sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499", "running_redis": "redis:7-alpine@sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499 sha256:f84b0c4678011602b9b98c227a4dcd5468bf8b088b02fdd4165cb7758bad8058"}
+12:50:16 [2-behind] installed={"nextcloud": {"ref": "nextcloud:34.0.4-apache", "digest": "sha256:a5ace30c695afe48c2c406e940ee7886a81e13fa382e57cd68b2416d1a66914c", "at": "2026-09-24T10:48:31Z"}, "nextcloud-db": {"ref": "mariadb:12.3", "digest": "sha256:805c8e104bd563d5bfa24fadd3f31cd419ea859cb5277f32b5dbf2db714f9ed1", "at": "2026-09-24T10:48:31Z"}, "nextcloud-redis": {"ref": "redis:7-alpine", "digest": "sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499", "at": "2026-09-24T10:48:31Z"}} pinned={"nextcloud": "nextcloud:34.0.4-apache", "nextcloud-db": "mariadb:12.3", "nextcloud-redis": "redis:7-alpine"}
+12:50:37 [2-behind] {"state": "running", "badges": {"hu": [{"title": "Újabb változat érhető el ehhez az alkalmazáshoz. A frissítés indításához nyomd meg a Frissítés gombot.", "text": "Frissítés elérhető — 1 napja"}], "en": [{"title": "A newer version of this app is available. Select the Update button to start it.", "text": "Update available — 1 day ago"}]}, "compose_redis": "103: image: redis:7-alpine@sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098", "running_redis": "redis:7-alpine@sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499 sha256:f84b0c4678011602b9b98c227a4dcd5468bf8b088b02fdd4165cb7758bad8058"}
+12:50:37 [2-behind] installed={"nextcloud": {"ref": "nextcloud:34.0.4-apache", "digest": "sha256:a5ace30c695afe48c2c406e940ee7886a81e13fa382e57cd68b2416d1a66914c", "at": "2026-09-24T10:48:31Z"}, "nextcloud-db": {"ref": "mariadb:12.3", "digest": "sha256:805c8e104bd563d5bfa24fadd3f31cd419ea859cb5277f32b5dbf2db714f9ed1", "at": "2026-09-24T10:48:31Z"}, "nextcloud-redis": {"ref": "redis:7-alpine", "digest": "sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499", "at": "2026-09-24T10:48:31Z"}} pinned={"nextcloud": "nextcloud:34.0.4-apache", "nextcloud-db": "mariadb:12.3", "nextcloud-redis": "redis:7-alpine"}
+12:50:37 [6] Update -> 202 {'ok': True, 'data': {'accepted': True, 'completed': False}, 'message': 'Frissítés elindult – az állapot a kártyán követhető'}
+12:50:37 + 0.0s phase=backing-up label=Biztonsági mentés készül a frissítés előtt… err=None hold=None
+12:50:54 + 16.4s phase=safety-dump label=Adatbázis pillanatkép… err=None hold=None
+12:50:56 + 18.5s phase=copying label=Az adatok másolása a frissítés előtt… err=None hold=None
+12:51:05 + 27.7s phase=starting label=Indítás az új verzióval… err=None hold=None
+12:51:11 + 33.9s phase=verifying label=Működés ellenőrzése… err=None hold=None
+12:51:23 + 45.1s phase=done label=Frissítve err=None hold=None
+12:51:34 [3-after] {"state": "running", "badges": {"hu": [{"title": "Ez az alkalmazás a legfrissebb elérhető változatot futtatja.", "text": "Naprakész"}], "en": [{"title": "This app is running the newest version available.", "text": "Up to date"}]}, "compose_redis": "103: image: redis:7-alpine@sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098", "running_redis": "redis:7-alpine@sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098 sha256:7d06252fad43690f61cbb858628b933fd85f048aaba4c05e577274b2c77cd7cb"}
+12:51:34 [3-after] installed={"nextcloud": {"ref": "nextcloud:34.0.4-apache", "digest": "sha256:a5ace30c695afe48c2c406e940ee7886a81e13fa382e57cd68b2416d1a66914c", "at": "2026-09-24T10:48:31Z"}, "nextcloud-db": {"ref": "mariadb:12.3", "digest": "sha256:805c8e104bd563d5bfa24fadd3f31cd419ea859cb5277f32b5dbf2db714f9ed1", "at": "2026-09-24T10:48:31Z"}, "nextcloud-redis": {"ref": "redis:7-alpine", "digest": "sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098", "at": "2026-09-24T10:50:50Z"}} pinned={"nextcloud": "nextcloud:34.0.4-apache", "nextcloud-db": "mariadb:12.3", "nextcloud-redis": "redis:7-alpine"}
+12:51:37 log:
+2026/09/24 10:43:59 update.go:762: [INFO] [stacks] update nextcloud: ladder — the installed version nextcloud=nextcloud:34.0.4-apache, nextcloud-db=mariadb:12.3, nextcloud-redis=redis:7-alpine matches no update_ladder entry (2 entries) — an app older than the ladder has no record to climb; the catalog's current definition
+2026/09/24 10:44:17 update.go:807: [INFO] [stacks] update nextcloud: safety dump done (1 file(s)) [/mnt/felhom-drives/scratch_hdd/userdata/nextcloud/backups/primary/nextcloud/db-dumps/pre-restore-20260924T104416Z-nextcloud-mariadb.sql]
+2026/09/24 10:44:18 pin.go:373: [INFO] [stacks] update nextcloud: pin advanced to /opt/docker/felhom-controller/data/catalog-cache/templates/nextcloud/docker-compose.yml (nextcloud=nextcloud:34.0.4-apache, nextcloud-db=mariadb:12.3, nextcloud-redis=redis:7.4.0-alpine)
+2026/09/24 10:44:18 update.go:1260: [INFO] [stacks] update nextcloud: phase pulling
+2026/09/24 10:48:31 installed.go:420: [INFO] [stacks] installed-images nextcloud: recorded 3 service(s) (nextcloud=nextcloud:34.0.4-apache (sha256:a5ace30c695a…), nextcloud-db=mariadb:12.3 (sha256:805c8e104bd5…), nextcloud-redis=redis:7-alpine (sha256:858f009f9709…))
+2026/09/24 10:50:37 update.go:762: [INFO] [stacks] update nextcloud: ladder — the installed version nextcloud=nextcloud:34.0.4-apache, nextcloud-db=mariadb:12.3, nextcloud-redis=redis:7-alpine matches no update_ladder entry (2 entries) — an app older than the ladder has no record to climb; the catalog's current definition
+2026/09/24 10:50:50 installed.go:420: [INFO] [stacks] installed-images nextcloud: recorded 3 service(s) (nextcloud=nextcloud:34.0.4-apache (sha256:a5ace30c695a…), nextcloud-db=mariadb:12.3 (sha256:805c8e104bd5…), nextcloud-redis=redis:7-alpine (sha256:c35af3bbcef5…))
+2026/09/24 10:50:54 update.go:807: [INFO] [stacks] update nextcloud: safety dump done (1 file(s)) [/mnt/felhom-drives/scratch_hdd/userdata/nextcloud/backups/primary/nextcloud/db-dumps/pre-restore-20260924T105053Z-nextcloud-mariadb.sql]
+2026/09/24 10:50:55 pin.go:373: [INFO] [stacks] update nextcloud: pin advanced to /opt/docker/felhom-controller/data/catalog-cache/templates/nextcloud/docker-compose.yml (nextcloud=nextcloud:34.0.4-apache, nextcloud-db=mariadb:12.3, nextcloud-redis=redis:7-alpine)
+2026/09/24 10:50:55 update.go:1260: [INFO] [stacks] update nextcloud: phase pulling
+
diff --git a/documentation/audits/night-2026-09-24/B/20-9202-deploy-0.269.1.txt b/documentation/audits/night-2026-09-24/B/20-9202-deploy-0.269.1.txt
new file mode 100644
index 00000000..85fcaf22
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/B/20-9202-deploy-0.269.1.txt
@@ -0,0 +1,3 @@
+gitea.dooplex.hu/admin/felhom-controller:0.269.1
+gitea.dooplex.hu/admin/felhom-controller:0.269.1
+gitea.dooplex.hu/admin/felhom-controller:0.269.1 Up 20 seconds (healthy)
diff --git a/documentation/audits/night-2026-09-24/B/21-floating-tag-0.269.1.json b/documentation/audits/night-2026-09-24/B/21-floating-tag-0.269.1.json
new file mode 100644
index 00000000..174479b5
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/B/21-floating-tag-0.269.1.json
@@ -0,0 +1,233 @@
+{
+ "controller": "gitea.dooplex.hu/admin/felhom-controller:0.269.1",
+ "1-control": {
+ "state": "running",
+ "installed": {
+ "nextcloud": {
+ "ref": "nextcloud:34.0.4-apache",
+ "digest": "sha256:a5ace30c695afe48c2c406e940ee7886a81e13fa382e57cd68b2416d1a66914c",
+ "at": "2026-09-24T10:48:31Z"
+ },
+ "nextcloud-db": {
+ "ref": "mariadb:12.3",
+ "digest": "sha256:805c8e104bd563d5bfa24fadd3f31cd419ea859cb5277f32b5dbf2db714f9ed1",
+ "at": "2026-09-24T10:48:31Z"
+ },
+ "nextcloud-redis": {
+ "ref": "redis:7-alpine",
+ "digest": "sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098",
+ "at": "2026-09-24T10:50:50Z"
+ }
+ },
+ "pinned": {
+ "nextcloud": "nextcloud:34.0.4-apache",
+ "nextcloud-db": "mariadb:12.3",
+ "nextcloud-redis": "redis:7-alpine"
+ },
+ "catalog_images": {
+ "nextcloud": "nextcloud:34.0.4-apache",
+ "nextcloud-db": "mariadb:12.3",
+ "nextcloud-redis": "redis:7-alpine"
+ },
+ "catalog_digests": {
+ "nextcloud": "sha256:a5ace30c695afe48c2c406e940ee7886a81e13fa382e57cd68b2416d1a66914c",
+ "nextcloud-db": "sha256:805c8e104bd563d5bfa24fadd3f31cd419ea859cb5277f32b5dbf2db714f9ed1",
+ "nextcloud-redis": "sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098"
+ },
+ "catalog_tested_at": "2026-09-24T10:50:04Z",
+ "badges": {
+ "hu": [
+ {
+ "title": "Ez az alkalmazás a legfrissebb elérhető változatot futtatja.",
+ "text": "Naprakész"
+ }
+ ],
+ "en": [
+ {
+ "title": "This app is running the newest version available.",
+ "text": "Up to date"
+ }
+ ]
+ },
+ "compose_redis": "103: image: redis:7-alpine@sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098",
+ "running_redis": "redis:7-alpine@sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098 sha256:7d06252fad43690f61cbb858628b933fd85f048aaba4c05e577274b2c77cd7cb"
+ },
+ "ladder_edit": {
+ "old": "sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098",
+ "new": "sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499",
+ "tested_at": "2026-09-24T11:03:33Z"
+ },
+ "2-behind": {
+ "state": "running",
+ "installed": {
+ "nextcloud": {
+ "ref": "nextcloud:34.0.4-apache",
+ "digest": "sha256:a5ace30c695afe48c2c406e940ee7886a81e13fa382e57cd68b2416d1a66914c",
+ "at": "2026-09-24T10:48:31Z"
+ },
+ "nextcloud-db": {
+ "ref": "mariadb:12.3",
+ "digest": "sha256:805c8e104bd563d5bfa24fadd3f31cd419ea859cb5277f32b5dbf2db714f9ed1",
+ "at": "2026-09-24T10:48:31Z"
+ },
+ "nextcloud-redis": {
+ "ref": "redis:7-alpine",
+ "digest": "sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098",
+ "at": "2026-09-24T10:50:50Z"
+ }
+ },
+ "pinned": {
+ "nextcloud": "nextcloud:34.0.4-apache",
+ "nextcloud-db": "mariadb:12.3",
+ "nextcloud-redis": "redis:7-alpine"
+ },
+ "catalog_images": {
+ "nextcloud": "nextcloud:34.0.4-apache",
+ "nextcloud-db": "mariadb:12.3",
+ "nextcloud-redis": "redis:7-alpine"
+ },
+ "catalog_digests": {
+ "nextcloud": "sha256:a5ace30c695afe48c2c406e940ee7886a81e13fa382e57cd68b2416d1a66914c",
+ "nextcloud-db": "sha256:805c8e104bd563d5bfa24fadd3f31cd419ea859cb5277f32b5dbf2db714f9ed1",
+ "nextcloud-redis": "sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499"
+ },
+ "catalog_tested_at": "2026-09-24T11:03:33Z",
+ "badges": {
+ "hu": [
+ {
+ "title": "Újabb változat érhető el ehhez az alkalmazáshoz. A frissítés indításához nyomd meg a Frissítés gombot.",
+ "text": "Frissítés elérhető — 1 napja"
+ }
+ ],
+ "en": [
+ {
+ "title": "A newer version of this app is available. Select the Update button to start it.",
+ "text": "Update available — 1 day ago"
+ }
+ ]
+ },
+ "compose_redis": "103: image: redis:7-alpine@sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098",
+ "running_redis": "redis:7-alpine@sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098 sha256:7d06252fad43690f61cbb858628b933fd85f048aaba4c05e577274b2c77cd7cb"
+ },
+ "sync_kept_running_digest": true,
+ "update": {
+ "accepted": true,
+ "http": "202",
+ "phases": [
+ {
+ "t": 0.0,
+ "phase": "backing-up",
+ "label": "Biztonsági mentés készül a frissítés előtt…",
+ "updating": true,
+ "error": null,
+ "hold": null
+ },
+ {
+ "t": 15.4,
+ "phase": "safety-dump",
+ "label": "Adatbázis pillanatkép…",
+ "updating": true,
+ "error": null,
+ "hold": null
+ },
+ {
+ "t": 17.5,
+ "phase": "pulling",
+ "label": "Új verzió letöltése…",
+ "updating": true,
+ "error": null,
+ "hold": null
+ },
+ {
+ "t": 18.5,
+ "phase": "copying",
+ "label": "Az adatok másolása a frissítés előtt…",
+ "updating": true,
+ "error": null,
+ "hold": null
+ },
+ {
+ "t": 26.7,
+ "phase": "starting",
+ "label": "Indítás az új verzióval…",
+ "updating": true,
+ "error": null,
+ "hold": null
+ },
+ {
+ "t": 32.8,
+ "phase": "verifying",
+ "label": "Működés ellenőrzése…",
+ "updating": true,
+ "error": null,
+ "hold": null
+ },
+ {
+ "t": 44.1,
+ "phase": "done",
+ "label": "Frissítve",
+ "updating": false,
+ "error": null,
+ "hold": null
+ }
+ ],
+ "duration_s": 44.1,
+ "final_phase": "done",
+ "update_error": null,
+ "hold_reason": null,
+ "state": "running"
+ },
+ "3-after": {
+ "state": "running",
+ "installed": {
+ "nextcloud": {
+ "ref": "nextcloud:34.0.4-apache",
+ "digest": "sha256:a5ace30c695afe48c2c406e940ee7886a81e13fa382e57cd68b2416d1a66914c",
+ "at": "2026-09-24T10:48:31Z"
+ },
+ "nextcloud-db": {
+ "ref": "mariadb:12.3",
+ "digest": "sha256:805c8e104bd563d5bfa24fadd3f31cd419ea859cb5277f32b5dbf2db714f9ed1",
+ "at": "2026-09-24T10:48:31Z"
+ },
+ "nextcloud-redis": {
+ "ref": "redis:7-alpine",
+ "digest": "sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499",
+ "at": "2026-09-24T11:04:49Z"
+ }
+ },
+ "pinned": {
+ "nextcloud": "nextcloud:34.0.4-apache",
+ "nextcloud-db": "mariadb:12.3",
+ "nextcloud-redis": "redis:7-alpine"
+ },
+ "catalog_images": {
+ "nextcloud": "nextcloud:34.0.4-apache",
+ "nextcloud-db": "mariadb:12.3",
+ "nextcloud-redis": "redis:7-alpine"
+ },
+ "catalog_digests": {
+ "nextcloud": "sha256:a5ace30c695afe48c2c406e940ee7886a81e13fa382e57cd68b2416d1a66914c",
+ "nextcloud-db": "sha256:805c8e104bd563d5bfa24fadd3f31cd419ea859cb5277f32b5dbf2db714f9ed1",
+ "nextcloud-redis": "sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499"
+ },
+ "catalog_tested_at": "2026-09-24T11:03:33Z",
+ "badges": {
+ "hu": [
+ {
+ "title": "Ez az alkalmazás a legfrissebb elérhető változatot futtatja.",
+ "text": "Naprakész"
+ }
+ ],
+ "en": [
+ {
+ "title": "This app is running the newest version available.",
+ "text": "Up to date"
+ }
+ ]
+ },
+ "compose_redis": "103: image: redis:7-alpine@sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499",
+ "running_redis": "redis:7-alpine@sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499 sha256:f84b0c4678011602b9b98c227a4dcd5468bf8b088b02fdd4165cb7758bad8058"
+ },
+ "pull_log": "2026/09/24 11:04:07 update.go:762: [INFO] [stacks] update nextcloud: ladder — the installed version nextcloud=nextcloud:34.0.4-apache, nextcloud-db=mariadb:12.3, nextcloud-redis=redis:7-alpine matches no update_ladder entry (2 entries) — an app older than the ladder has no record to climb; the catalog's current definition\n2026/09/24 11:04:22 update.go:807: [INFO] [stacks] update nextcloud: safety dump done (1 file(s)) [/mnt/felhom-drives/scratch_hdd/userdata/nextcloud/backups/primary/nextcloud/db-dumps/pre-restore-20260924T110422Z-nextcloud-mariadb.sql]\n2026/09/24 11:04:24 pin.go:373: [INFO] [stacks] update nextcloud: pin advanced to /opt/docker/felhom-controller/data/catalog-cache/templates/nextcloud/docker-compose.yml (nextcloud=nextcloud:34.0.4-apache, nextcloud-db=mariadb:12.3, nextcloud-redis=redis:7-alpine)\n2026/09/24 11:04:24 update.go:1260: [INFO] [stacks] update nextcloud: phase pulling\n2026/09/24 11:04:49 installed.go:420: [INFO] [stacks] installed-images nextcloud: recorded 3 service(s) (nextcloud=nextcloud:34.0.4-apache (sha256:a5ace30c695a…), nextcloud-db=mariadb:12.3 (sha256:805c8e104bd5…), nextcloud-redis=redis:7-alpine (sha256:858f009f9709…))\n"
+}
\ No newline at end of file
diff --git a/documentation/audits/night-2026-09-24/B/21-floating-tag-0.269.1.log b/documentation/audits/night-2026-09-24/B/21-floating-tag-0.269.1.log
new file mode 100644
index 00000000..881fbb7e
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/B/21-floating-tag-0.269.1.log
@@ -0,0 +1,25 @@
+13:03:33 [1-control] {"state": "running", "badges": {"hu": [{"title": "Ez az alkalmazás a legfrissebb elérhető változatot futtatja.", "text": "Naprakész"}], "en": [{"title": "This app is running the newest version available.", "text": "Up to date"}]}, "compose_redis": "103: image: redis:7-alpine@sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098", "running_redis": "redis:7-alpine@sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098 sha256:7d06252fad43690f61cbb858628b933fd85f048aaba4c05e577274b2c77cd7cb"}
+13:03:33 [1-control] installed={"nextcloud": {"ref": "nextcloud:34.0.4-apache", "digest": "sha256:a5ace30c695afe48c2c406e940ee7886a81e13fa382e57cd68b2416d1a66914c", "at": "2026-09-24T10:48:31Z"}, "nextcloud-db": {"ref": "mariadb:12.3", "digest": "sha256:805c8e104bd563d5bfa24fadd3f31cd419ea859cb5277f32b5dbf2db714f9ed1", "at": "2026-09-24T10:48:31Z"}, "nextcloud-redis": {"ref": "redis:7-alpine", "digest": "sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098", "at": "2026-09-24T10:50:50Z"}} pinned={"nextcloud": "nextcloud:34.0.4-apache", "nextcloud-db": "mariadb:12.3", "nextcloud-redis": "redis:7-alpine"}
+13:03:35 drill: ladder head entry digest sha256:c35af3bbcef5 -> sha256:858f009f9709
+13:03:45 [2-behind] {"state": "running", "badges": {"hu": [{"title": "Ez az alkalmazás a legfrissebb elérhető változatot futtatja.", "text": "Naprakész"}], "en": [{"title": "This app is running the newest version available.", "text": "Up to date"}]}, "compose_redis": "103: image: redis:7-alpine@sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098", "running_redis": "redis:7-alpine@sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098 sha256:7d06252fad43690f61cbb858628b933fd85f048aaba4c05e577274b2c77cd7cb"}
+13:03:45 [2-behind] installed={"nextcloud": {"ref": "nextcloud:34.0.4-apache", "digest": "sha256:a5ace30c695afe48c2c406e940ee7886a81e13fa382e57cd68b2416d1a66914c", "at": "2026-09-24T10:48:31Z"}, "nextcloud-db": {"ref": "mariadb:12.3", "digest": "sha256:805c8e104bd563d5bfa24fadd3f31cd419ea859cb5277f32b5dbf2db714f9ed1", "at": "2026-09-24T10:48:31Z"}, "nextcloud-redis": {"ref": "redis:7-alpine", "digest": "sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098", "at": "2026-09-24T10:50:50Z"}} pinned={"nextcloud": "nextcloud:34.0.4-apache", "nextcloud-db": "mariadb:12.3", "nextcloud-redis": "redis:7-alpine"}
+13:04:06 [2-behind] {"state": "running", "badges": {"hu": [{"title": "Újabb változat érhető el ehhez az alkalmazáshoz. A frissítés indításához nyomd meg a Frissítés gombot.", "text": "Frissítés elérhető — 1 napja"}], "en": [{"title": "A newer version of this app is available. Select the Update button to start it.", "text": "Update available — 1 day ago"}]}, "compose_redis": "103: image: redis:7-alpine@sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098", "running_redis": "redis:7-alpine@sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098 sha256:7d06252fad43690f61cbb858628b933fd85f048aaba4c05e577274b2c77cd7cb"}
+13:04:06 [2-behind] installed={"nextcloud": {"ref": "nextcloud:34.0.4-apache", "digest": "sha256:a5ace30c695afe48c2c406e940ee7886a81e13fa382e57cd68b2416d1a66914c", "at": "2026-09-24T10:48:31Z"}, "nextcloud-db": {"ref": "mariadb:12.3", "digest": "sha256:805c8e104bd563d5bfa24fadd3f31cd419ea859cb5277f32b5dbf2db714f9ed1", "at": "2026-09-24T10:48:31Z"}, "nextcloud-redis": {"ref": "redis:7-alpine", "digest": "sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098", "at": "2026-09-24T10:50:50Z"}} pinned={"nextcloud": "nextcloud:34.0.4-apache", "nextcloud-db": "mariadb:12.3", "nextcloud-redis": "redis:7-alpine"}
+13:04:06 CHECK the sync kept the running digest in the compose before Update: True
+13:04:07 [6] Update -> 202 {'ok': True, 'data': {'accepted': True, 'completed': False}, 'message': 'Frissítés elindult – az állapot a kártyán követhető'}
+13:04:07 + 0.0s phase=backing-up label=Biztonsági mentés készül a frissítés előtt… err=None hold=None
+13:04:22 + 15.4s phase=safety-dump label=Adatbázis pillanatkép… err=None hold=None
+13:04:24 + 17.5s phase=pulling label=Új verzió letöltése… err=None hold=None
+13:04:25 + 18.5s phase=copying label=Az adatok másolása a frissítés előtt… err=None hold=None
+13:04:33 + 26.7s phase=starting label=Indítás az új verzióval… err=None hold=None
+13:04:39 + 32.8s phase=verifying label=Működés ellenőrzése… err=None hold=None
+13:04:51 + 44.1s phase=done label=Frissítve err=None hold=None
+13:05:02 [3-after] {"state": "running", "badges": {"hu": [{"title": "Ez az alkalmazás a legfrissebb elérhető változatot futtatja.", "text": "Naprakész"}], "en": [{"title": "This app is running the newest version available.", "text": "Up to date"}]}, "compose_redis": "103: image: redis:7-alpine@sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499", "running_redis": "redis:7-alpine@sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499 sha256:f84b0c4678011602b9b98c227a4dcd5468bf8b088b02fdd4165cb7758bad8058"}
+13:05:02 [3-after] installed={"nextcloud": {"ref": "nextcloud:34.0.4-apache", "digest": "sha256:a5ace30c695afe48c2c406e940ee7886a81e13fa382e57cd68b2416d1a66914c", "at": "2026-09-24T10:48:31Z"}, "nextcloud-db": {"ref": "mariadb:12.3", "digest": "sha256:805c8e104bd563d5bfa24fadd3f31cd419ea859cb5277f32b5dbf2db714f9ed1", "at": "2026-09-24T10:48:31Z"}, "nextcloud-redis": {"ref": "redis:7-alpine", "digest": "sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499", "at": "2026-09-24T11:04:49Z"}} pinned={"nextcloud": "nextcloud:34.0.4-apache", "nextcloud-db": "mariadb:12.3", "nextcloud-redis": "redis:7-alpine"}
+13:05:05 log:
+2026/09/24 11:04:07 update.go:762: [INFO] [stacks] update nextcloud: ladder — the installed version nextcloud=nextcloud:34.0.4-apache, nextcloud-db=mariadb:12.3, nextcloud-redis=redis:7-alpine matches no update_ladder entry (2 entries) — an app older than the ladder has no record to climb; the catalog's current definition
+2026/09/24 11:04:22 update.go:807: [INFO] [stacks] update nextcloud: safety dump done (1 file(s)) [/mnt/felhom-drives/scratch_hdd/userdata/nextcloud/backups/primary/nextcloud/db-dumps/pre-restore-20260924T110422Z-nextcloud-mariadb.sql]
+2026/09/24 11:04:24 pin.go:373: [INFO] [stacks] update nextcloud: pin advanced to /opt/docker/felhom-controller/data/catalog-cache/templates/nextcloud/docker-compose.yml (nextcloud=nextcloud:34.0.4-apache, nextcloud-db=mariadb:12.3, nextcloud-redis=redis:7-alpine)
+2026/09/24 11:04:24 update.go:1260: [INFO] [stacks] update nextcloud: phase pulling
+2026/09/24 11:04:49 installed.go:420: [INFO] [stacks] installed-images nextcloud: recorded 3 service(s) (nextcloud=nextcloud:34.0.4-apache (sha256:a5ace30c695a…), nextcloud-db=mariadb:12.3 (sha256:805c8e104bd5…), nextcloud-redis=redis:7-alpine (sha256:858f009f9709…))
+
diff --git a/documentation/audits/night-2026-09-24/B/21.stdout b/documentation/audits/night-2026-09-24/B/21.stdout
new file mode 100644
index 00000000..881fbb7e
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/B/21.stdout
@@ -0,0 +1,25 @@
+13:03:33 [1-control] {"state": "running", "badges": {"hu": [{"title": "Ez az alkalmazás a legfrissebb elérhető változatot futtatja.", "text": "Naprakész"}], "en": [{"title": "This app is running the newest version available.", "text": "Up to date"}]}, "compose_redis": "103: image: redis:7-alpine@sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098", "running_redis": "redis:7-alpine@sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098 sha256:7d06252fad43690f61cbb858628b933fd85f048aaba4c05e577274b2c77cd7cb"}
+13:03:33 [1-control] installed={"nextcloud": {"ref": "nextcloud:34.0.4-apache", "digest": "sha256:a5ace30c695afe48c2c406e940ee7886a81e13fa382e57cd68b2416d1a66914c", "at": "2026-09-24T10:48:31Z"}, "nextcloud-db": {"ref": "mariadb:12.3", "digest": "sha256:805c8e104bd563d5bfa24fadd3f31cd419ea859cb5277f32b5dbf2db714f9ed1", "at": "2026-09-24T10:48:31Z"}, "nextcloud-redis": {"ref": "redis:7-alpine", "digest": "sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098", "at": "2026-09-24T10:50:50Z"}} pinned={"nextcloud": "nextcloud:34.0.4-apache", "nextcloud-db": "mariadb:12.3", "nextcloud-redis": "redis:7-alpine"}
+13:03:35 drill: ladder head entry digest sha256:c35af3bbcef5 -> sha256:858f009f9709
+13:03:45 [2-behind] {"state": "running", "badges": {"hu": [{"title": "Ez az alkalmazás a legfrissebb elérhető változatot futtatja.", "text": "Naprakész"}], "en": [{"title": "This app is running the newest version available.", "text": "Up to date"}]}, "compose_redis": "103: image: redis:7-alpine@sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098", "running_redis": "redis:7-alpine@sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098 sha256:7d06252fad43690f61cbb858628b933fd85f048aaba4c05e577274b2c77cd7cb"}
+13:03:45 [2-behind] installed={"nextcloud": {"ref": "nextcloud:34.0.4-apache", "digest": "sha256:a5ace30c695afe48c2c406e940ee7886a81e13fa382e57cd68b2416d1a66914c", "at": "2026-09-24T10:48:31Z"}, "nextcloud-db": {"ref": "mariadb:12.3", "digest": "sha256:805c8e104bd563d5bfa24fadd3f31cd419ea859cb5277f32b5dbf2db714f9ed1", "at": "2026-09-24T10:48:31Z"}, "nextcloud-redis": {"ref": "redis:7-alpine", "digest": "sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098", "at": "2026-09-24T10:50:50Z"}} pinned={"nextcloud": "nextcloud:34.0.4-apache", "nextcloud-db": "mariadb:12.3", "nextcloud-redis": "redis:7-alpine"}
+13:04:06 [2-behind] {"state": "running", "badges": {"hu": [{"title": "Újabb változat érhető el ehhez az alkalmazáshoz. A frissítés indításához nyomd meg a Frissítés gombot.", "text": "Frissítés elérhető — 1 napja"}], "en": [{"title": "A newer version of this app is available. Select the Update button to start it.", "text": "Update available — 1 day ago"}]}, "compose_redis": "103: image: redis:7-alpine@sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098", "running_redis": "redis:7-alpine@sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098 sha256:7d06252fad43690f61cbb858628b933fd85f048aaba4c05e577274b2c77cd7cb"}
+13:04:06 [2-behind] installed={"nextcloud": {"ref": "nextcloud:34.0.4-apache", "digest": "sha256:a5ace30c695afe48c2c406e940ee7886a81e13fa382e57cd68b2416d1a66914c", "at": "2026-09-24T10:48:31Z"}, "nextcloud-db": {"ref": "mariadb:12.3", "digest": "sha256:805c8e104bd563d5bfa24fadd3f31cd419ea859cb5277f32b5dbf2db714f9ed1", "at": "2026-09-24T10:48:31Z"}, "nextcloud-redis": {"ref": "redis:7-alpine", "digest": "sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098", "at": "2026-09-24T10:50:50Z"}} pinned={"nextcloud": "nextcloud:34.0.4-apache", "nextcloud-db": "mariadb:12.3", "nextcloud-redis": "redis:7-alpine"}
+13:04:06 CHECK the sync kept the running digest in the compose before Update: True
+13:04:07 [6] Update -> 202 {'ok': True, 'data': {'accepted': True, 'completed': False}, 'message': 'Frissítés elindult – az állapot a kártyán követhető'}
+13:04:07 + 0.0s phase=backing-up label=Biztonsági mentés készül a frissítés előtt… err=None hold=None
+13:04:22 + 15.4s phase=safety-dump label=Adatbázis pillanatkép… err=None hold=None
+13:04:24 + 17.5s phase=pulling label=Új verzió letöltése… err=None hold=None
+13:04:25 + 18.5s phase=copying label=Az adatok másolása a frissítés előtt… err=None hold=None
+13:04:33 + 26.7s phase=starting label=Indítás az új verzióval… err=None hold=None
+13:04:39 + 32.8s phase=verifying label=Működés ellenőrzése… err=None hold=None
+13:04:51 + 44.1s phase=done label=Frissítve err=None hold=None
+13:05:02 [3-after] {"state": "running", "badges": {"hu": [{"title": "Ez az alkalmazás a legfrissebb elérhető változatot futtatja.", "text": "Naprakész"}], "en": [{"title": "This app is running the newest version available.", "text": "Up to date"}]}, "compose_redis": "103: image: redis:7-alpine@sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499", "running_redis": "redis:7-alpine@sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499 sha256:f84b0c4678011602b9b98c227a4dcd5468bf8b088b02fdd4165cb7758bad8058"}
+13:05:02 [3-after] installed={"nextcloud": {"ref": "nextcloud:34.0.4-apache", "digest": "sha256:a5ace30c695afe48c2c406e940ee7886a81e13fa382e57cd68b2416d1a66914c", "at": "2026-09-24T10:48:31Z"}, "nextcloud-db": {"ref": "mariadb:12.3", "digest": "sha256:805c8e104bd563d5bfa24fadd3f31cd419ea859cb5277f32b5dbf2db714f9ed1", "at": "2026-09-24T10:48:31Z"}, "nextcloud-redis": {"ref": "redis:7-alpine", "digest": "sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499", "at": "2026-09-24T11:04:49Z"}} pinned={"nextcloud": "nextcloud:34.0.4-apache", "nextcloud-db": "mariadb:12.3", "nextcloud-redis": "redis:7-alpine"}
+13:05:05 log:
+2026/09/24 11:04:07 update.go:762: [INFO] [stacks] update nextcloud: ladder — the installed version nextcloud=nextcloud:34.0.4-apache, nextcloud-db=mariadb:12.3, nextcloud-redis=redis:7-alpine matches no update_ladder entry (2 entries) — an app older than the ladder has no record to climb; the catalog's current definition
+2026/09/24 11:04:22 update.go:807: [INFO] [stacks] update nextcloud: safety dump done (1 file(s)) [/mnt/felhom-drives/scratch_hdd/userdata/nextcloud/backups/primary/nextcloud/db-dumps/pre-restore-20260924T110422Z-nextcloud-mariadb.sql]
+2026/09/24 11:04:24 pin.go:373: [INFO] [stacks] update nextcloud: pin advanced to /opt/docker/felhom-controller/data/catalog-cache/templates/nextcloud/docker-compose.yml (nextcloud=nextcloud:34.0.4-apache, nextcloud-db=mariadb:12.3, nextcloud-redis=redis:7-alpine)
+2026/09/24 11:04:24 update.go:1260: [INFO] [stacks] update nextcloud: phase pulling
+2026/09/24 11:04:49 installed.go:420: [INFO] [stacks] installed-images nextcloud: recorded 3 service(s) (nextcloud=nextcloud:34.0.4-apache (sha256:a5ace30c695a…), nextcloud-db=mariadb:12.3 (sha256:805c8e104bd5…), nextcloud-redis=redis:7-alpine (sha256:858f009f9709…))
+
diff --git a/documentation/audits/night-2026-09-24/PROGRESS.md b/documentation/audits/night-2026-09-24/PROGRESS.md
index 2f072e91..85499984 100644
--- a/documentation/audits/night-2026-09-24/PROGRESS.md
+++ b/documentation/audits/night-2026-09-24/PROGRESS.md
@@ -7,3 +7,21 @@ Step log. Newest at the bottom. Times CEST unless marked Z.
- 11:09 9202 repointed to the drill catalog (controller.yaml saved as .pre-night0924)
- 11:20 A1 (whole restore + R-668) and A2 (decision 27) built, tested, red-proofed
- 11:34 A3 built: detector (6 restarts/10 min — measured gokapi 1/min), stop+hold+event, Start lifts; red-proofs detector + skips
+- 11:49 A4 (R-664 box+catalog, R-665, R-662 removed) and Part B (digests) built, red-proofed; hub 0.123.0 live
+- 12:21 9202: drill setting update.backup_max_age 1m (forces the update's own backup + Tier 2); restored at teardown from .pre-night0924
+- 12:25 A1 LIVE PASS (9202, v0.269.0): whole restore from the SSD Tier-2 copy — files restored=1 kept-newer=1 unchanged=137, unit 3/3 volumes 1/1 db, 38 s; account + all three files read back (A1/21-readback.json). R-668 live: the new copy went to the SSD, not the same disk.
+- 12:29 A3 LIVE trip 1: gokapi stopped by the box „crash_loop — 6 in 10m0s", page hu/en + Start, no restore badge (A3/21).
+- 12:37 A3 LIVE Start + trip 2: Start LIFTED the hold; Docker's back-off reset on the new start → 9 restarts in 32 s → stopped again, trip 2, page hu/en says support is told; 0 app_start_failed lines (A3/30-start-then-trip2.*).
+- 12:41 A1 LIVE hold names the second drive: failing edge (redis 7.4-alpine + probe 8999), undo copy cut → HELD, page hu/en „második meghajtó … beállításokat, az adatbázist és a fájlokat" / "second drive … settings, the database and the files"; hdd-data keep_data_only absent (A2 positive control); the page's whole restore → hold CLEARED, 33.8 s, unit 3/3 1/1, account + files read back (A1/30-held-then-whole.*).
+- 12:42 Part B seen live in passing: nextcloud's compose on 9202 runs redis:7-alpine@sha256:858f… (floating tag, digest from the ladder entry).
+- 12:43 FINDING: after hold + whole restore the probe stays on the FAILED step's .felhom.yml (applied-meta 10:39:21 = port 8999; stack .felhom.yml restored from the unit, which the sync had already flowed). Steady probe heals on the next sync; applied-meta does not until a pin advance → a later failed update's undo would probe the old version with the wrong check. ROW to file.
+- 12:43 FINDING: `[ERROR] .felhom.yml backup block rejected in …/pre-update-meta: docker-compose.yml unreadable` on an undo (10:24:41). ROW to file.
+- 12:44 Part E schedule drawn (tools/chaos_schedule.py, seed 20260924, md5 of table b5ccf895…).
+- 12:50 A3/40: first-start restarts in all drill evidence — no healthy app ≥ 6; immich 12 (broken, first-start import). ROW (watch).
+ROWS TO FILE: R-668 (Tier-2 target on a missing path, fixed); applied-meta after restore; pre-update-meta ERROR noise; ladder log "older than the ladder" at head; missingFileLegsRefusal text names file restore not whole; immich first-start watch; R-644 gokapi disposition.
+- 12:48 A2 LIVE PASS: a no-whole-copy hold (second-drive mirror moved aside = a one-drive box) → page hu/en „…ügyfélszolgálatát értesítettük — addig ne indítsd újra, és ne töröld az adatait." ; hdd-data keep_data_only=true; Remove with data → 409 hu/en, with backups → 409; app still deployed; mirror back → whole restore cleared it; account + files read back (A2/10-held-no-copy-keep-data.*).
+- 12:48 FINDING CONSEQUENCE PROVEN: that hold was reached because the undo judged the (correct) old version with the FAILED step's probe (applied-meta 8999 left by the previous hold+restore) → a false hold (A2/11). Undo-copy volumes of the earlier hold (…103924Z, ~0.9 GiB) still present after its hold was cleared by a restore. ROWS.
+- 12:55 Part B: compose config accepts tag@digest for every template (25 apps, 37 digests, 0 missing, 0 differ) — B/01.
+- 12:51 Part B LIVE on 0.269.0: badge Behind for a newer tested digest of a floating tag, Update pulled it, record + badge current (B/10). FINDING: the SYNC wrote the new digest into the running app's compose BEFORE Update → a restart would pull it unguarded. FIXED v0.269.1 (CarryDigests), red-proofed (redproofs/B-sync-carry.txt), pushed 5b1b191, image built, 9202 on 0.269.1 (B/20).
+- 13:05 Part B LIVE PASS on 0.269.1: sync KEPT the running digest before Update (True), badge Behind hu/en, Update done 44 s, running digest = the tested one, badge current (B/21-floating-tag-0.269.1.*).
+- DECISION (CC, unattended): a second controller release tonight (0.269.1) instead of shipping the bypass with the floor — first in the morning note.
diff --git a/documentation/audits/night-2026-09-24/redproofs/A1-r668.txt b/documentation/audits/night-2026-09-24/redproofs/A1-r668.txt
index 0f26abc3..b0e6248a 100644
--- a/documentation/audits/night-2026-09-24/redproofs/A1-r668.txt
+++ b/documentation/audits/night-2026-09-24/redproofs/A1-r668.txt
@@ -1,5 +1,5 @@
--- FAIL: TestR668_MissingStoragePathIsNotASecondDrive (0.01s)
- r668_missing_path_test.go:45: chose the missing path /tmp/TestR668_MissingStoragePathIsNotASecondDrive1761835767/001/drive-gone as the second drive
+ r668_missing_path_test.go:38: chose the missing path /tmp/TestR668_MissingStoragePathIsNotASecondDrive2803100938/001/drive-gone as the second drive
FAIL
-FAIL gitea.dooplex.hu/admin/felhom-controller/internal/backup 0.014s
+FAIL gitea.dooplex.hu/admin/felhom-controller/internal/backup 0.015s
FAIL
diff --git a/documentation/audits/night-2026-09-24/redproofs/A4-r664-catalog-gate.txt b/documentation/audits/night-2026-09-24/redproofs/A4-r664-catalog-gate.txt
new file mode 100644
index 00000000..48e2d581
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/redproofs/A4-r664-catalog-gate.txt
@@ -0,0 +1,2 @@
+ XX FACT: the step has its compose but no .felhom.yml rc=0 (expected 1)
+ XX DECOY: the step's .felhom.yml exists by NAME and holds no healthcheck rc=0 (expected 1)
diff --git a/documentation/audits/night-2026-09-24/redproofs/A4-r665-r664.txt b/documentation/audits/night-2026-09-24/redproofs/A4-r665-r664.txt
new file mode 100644
index 00000000..046c4c7f
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/redproofs/A4-r665-r664.txt
@@ -0,0 +1,7 @@
+--- FAIL: TestR665_TheVerifyUsesTheNewVersionsOwnFile (0.01s)
+ r665_verify_meta_test.go:39: verified with probe port 1111, want the catalog's 2222 (1111 = the restored file, R-665)
+--- FAIL: TestR664_AStepIsVerifiedWithItsOwnFile (0.01s)
+ r665_verify_meta_test.go:54: step B verified with probe port 1111, want its own 3333
+FAIL
+FAIL gitea.dooplex.hu/admin/felhom-controller/internal/stacks 0.024s
+FAIL
diff --git a/documentation/audits/night-2026-09-24/redproofs/B-digests.txt b/documentation/audits/night-2026-09-24/redproofs/B-digests.txt
new file mode 100644
index 00000000..27ef5f22
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/redproofs/B-digests.txt
@@ -0,0 +1,6 @@
+--- FAIL: TestDigest_FloatingTagBehindOnlyForANewerTestedDigest (0.00s)
+ digest_test.go:57: newer tested digest: 1, want 2
+--- FAIL: TestDigest_TheUpdateRendersTheStepsTestedDigest (0.01s)
+ digest_test.go:74: the live compose does not pin the tested digest:
+--- FAIL: TestDigest_SyncerRendersTheTestedDigest (0.00s)
+ digest_render_test.go:36: no digest in the rendered compose:
diff --git a/documentation/audits/night-2026-09-24/redproofs/B-sync-carry.txt b/documentation/audits/night-2026-09-24/redproofs/B-sync-carry.txt
new file mode 100644
index 00000000..119b3041
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/redproofs/B-sync-carry.txt
@@ -0,0 +1,10 @@
+# Red-proof — the sync keeps a DEPLOYED app's running digest (v0.269.1; live finding night 2026-09-24 Part B)
+# Mutation: the call site in internal/sync/sync.go rendered the ladder's newest digest for every app again
+# (`if false && s.renderPlanFn != nil && s.renderPlanFn(appName).Deployed {`)
+--- FAIL: TestDigest_SyncerKeepsTheRunningDigest (0.00s)
+ --- FAIL: TestDigest_SyncerKeepsTheRunningDigest/runs_an_older_digest (0.00s)
+ digest_render_test.go:65: the sync MOVED the running digest (want "@sha256:bbbb…", never "sha256:aaaa…")
+ --- FAIL: TestDigest_SyncerKeepsTheRunningDigest/runs_no_digest (0.00s)
+ digest_render_test.go:65: the sync MOVED the running digest (want "image: nextcloud:31.0.14-apache\n", never "sha256:aaaa…")
+FAIL
+# Fix restored → ok internal/sync; git diff of the mutation: none.
diff --git a/documentation/audits/night-2026-09-24/tools/chaos_schedule.py b/documentation/audits/night-2026-09-24/tools/chaos_schedule.py
new file mode 100644
index 00000000..d7969d6d
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/tools/chaos_schedule.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python3
+"""Part E's schedule — night 2026-09-24. Deterministic: seed 20260924, 12 rounds of action x accident.
+
+Committed BEFORE round 1 and printed into DRILL-night-2026-09-24.md, so the schedule cannot be chosen after
+the fact. Constraints from the brief: every action at least once; controller killed at most 3 times, at
+least 20 minutes apart (rounds are ~6 min, so kills sit >= 4 rounds apart); a whole-box backup only on 9202.
+
+ python3 chaos_schedule.py # prints the table
+ python3 chaos_schedule.py --json # for the runner
+"""
+import json
+import random
+import sys
+
+SEED = 20260924
+ROUNDS = 12
+ROUND_MIN = 6 # planned length of one round, minutes
+
+ACTIONS = [
+ "ladder_step", # a proven ladder step (Update)
+ "failing_step", # a step whose probe fails -> undo
+ "cutoff_undo_file_app", # a file app's undo copy cut off -> the second-drive whole restore
+ "crash_loop", # a drill image that exits -> the box stops it
+ "oom_app", # a tiny mem_limit -> out-of-memory kills
+ "install",
+ "remove_held", # Remove on a held app
+ "start_after_unhealthy",# Start after the box stopped an app
+]
+ACCIDENTS = ["none", "power_cut", "controller_killed", "docker_restart", "disk_to_floor_plus_1g",
+ "whole_box_backup"]
+MAX_KILLS, KILL_GAP_MIN = 3, 20
+
+
+def schedule(seed=SEED):
+ rng = random.Random(seed)
+ for _ in range(10000):
+ acts = ACTIONS + [rng.choice(ACTIONS) for _ in range(ROUNDS - len(ACTIONS))]
+ rng.shuffle(acts)
+ accs = [rng.choice(ACCIDENTS) for _ in range(ROUNDS)]
+ kills = [i for i, a in enumerate(accs) if a == "controller_killed"]
+ if len(kills) > MAX_KILLS:
+ continue
+ if any((b - a) * ROUND_MIN < KILL_GAP_MIN for a, b in zip(kills, kills[1:])):
+ continue
+ # Start-after-unhealthy needs an app the box stopped: never before the first crash_loop/oom_app.
+ first_stop = min(i for i, a in enumerate(acts) if a in ("crash_loop", "oom_app"))
+ if acts.index("start_after_unhealthy") < first_stop:
+ continue
+ # Remove-on-held needs a held app: after a failing_step or cutoff.
+ first_hold = min(i for i, a in enumerate(acts) if a in ("failing_step", "cutoff_undo_file_app"))
+ if acts.index("remove_held") < first_hold:
+ continue
+ return [{"round": i + 1, "action": acts[i], "accident": accs[i]} for i in range(ROUNDS)]
+ raise SystemExit("no schedule satisfies the constraints")
+
+
+if __name__ == "__main__":
+ s = schedule()
+ if "--json" in sys.argv:
+ print(json.dumps(s, indent=1))
+ else:
+ print(f"seed {SEED}, {ROUNDS} rounds")
+ print("| round | action | accident |\n|---|---|---|")
+ for r in s:
+ print(f"| {r['round']} | {r['action']} | {r['accident']} |")
diff --git a/documentation/audits/night-2026-09-24/tools/liveA1.py b/documentation/audits/night-2026-09-24/tools/liveA1.py
new file mode 100644
index 00000000..7a5f98f0
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/tools/liveA1.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python3
+"""A1 live on 9202 (v0.269.0) — decision 26: nextcloud (file app) with a fresh second copy, files changed through
+the front door, a failing update whose undo copy is cut off (→ HOLD), then the second copy's „Teljes
+visszaállítás" = the WHOLE restore. Read back: the account, every file, the counts."""
+import html as H, json, re, sys, time
+sys.path.insert(0, ".")
+import walk as w, fixtures as fx, ncfiles as nf
+F, SUB, APP = fx.Nextcloud(), "cloud-a1", "nextcloud"
+CAT = f"{w.DRILL}/templates/{APP}"
+S = json.load(open(f"{w.SC}/a1-secret-state.json"))
+A, files = S["A"], S["files"]
+out = {"controller": w.guest("cat /etc/felhom-controller-image").strip()}
+w.login()
+
+
+def drill(edit, msg):
+ import fcntl
+ with open(f"{w.SC}/drill.lock", "w") as lk:
+ fcntl.flock(lk, fcntl.LOCK_EX)
+ w.sh(["git", "-C", w.DRILL, "pull", "-q", "--rebase", "origin", "main"], timeout=120)
+ edit()
+ w.sh(["git", "-C", w.DRILL, "commit", "-qam", "NIGHT-A1 " + msg])
+ w.sh(["git", "-C", w.DRILL, "push", "-q", "origin", "main"], timeout=120)
+ w.say("drill: " + msg)
+
+
+HEAD = open(f"{CAT}/docker-compose.yml").read()
+FY = open(f"{CAT}/.felhom.yml").read()
+names = list(files)
+# (a) a fresh backup + second copy: an update whose tag does not exist
+drill(lambda: open(f"{CAT}/docker-compose.yml", "w").write(HEAD.replace("nextcloud:34.0.4-apache", "nextcloud:34.0.98-notag")), "notag (backing-up + Tier 2, pull fails)")
+w.sync_rescan(APP, "nextcloud:34.0.98-notag", tries=60)
+# make the copy fresh-older than backup_max_age? the existing unit is < 24 h old -> force the backup by nothing: the
+# precondition may lean on the existing copy. So record which copy it used.
+out["press_a"] = w.press_update(APP)
+out["log_a"] = w.guest("docker logs --since 5m felhom-controller 2>&1 | grep -E 'nextcloud' | grep -iE 'Tier 2|precondition|same-disk|backup' | tail -8")
+w.say("log (a):\n" + out["log_a"])
+drill(lambda: open(f"{CAT}/docker-compose.yml", "w").write(HEAD), "back to 34.0.4")
+out["mirror"] = w.guest("""for b in $(find /mnt/sys_drive /mnt/felhom-drives -maxdepth 7 -type d -path '*backups/secondary/nextcloud' 2>/dev/null); do echo "== $b"; ls $b; find $b -name 'felhom-seed-*' | sed 's|.*/||'; done""")
+w.say("mirrors:\n" + out["mirror"])
+out["record"] = w.guest("python3 -c \"import json;d=json.load(open('/var/lib/docker/volumes/felhom-controller-data/_data/data/settings.json'));print(json.dumps(d['app_backup']['nextcloud']['cross_drive']))\"")
+w.say("Tier-2 record: " + out["record"])
+json.dump(out, open("../A1/11-copy.json", "w"), indent=2, default=str)
+open("../A1/11-copy.log", "w").write("\n".join(w.LOG) + "\n")
diff --git a/documentation/audits/night-2026-09-24/tools/liveA1b.py b/documentation/audits/night-2026-09-24/tools/liveA1b.py
new file mode 100644
index 00000000..b6ee7e7a
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/tools/liveA1b.py
@@ -0,0 +1,117 @@
+#!/usr/bin/env python3
+"""A1 live, whole: (a) a fresh second copy via the update's own backup; (b) through WebDAV: delete file 0, edit
+file 1 (live newer than the copy); (c) a failing update (probe 8999) whose undo copy is cut off → HOLD, the
+page naming the second drive; (d) the second copy's „Teljes visszaállítás" = the WHOLE restore; (e) read back."""
+import html as H, json, re, sys, time
+sys.path.insert(0, ".")
+import walk as w, fixtures as fx, ncfiles as nf
+F, SUB, APP = fx.Nextcloud(), "cloud-a1", "nextcloud"
+CAT = f"{w.DRILL}/templates/{APP}"
+S = json.load(open(f"{w.SC}/a1-secret-state.json"))
+A, files = S["A"], S["files"]
+names = sorted(files)
+out = {"controller": w.guest("cat /etc/felhom-controller-image").strip()}
+w.login()
+
+
+def drill(edit, msg):
+ import fcntl
+ with open(f"{w.SC}/drill.lock", "w") as lk:
+ fcntl.flock(lk, fcntl.LOCK_EX)
+ w.sh(["git", "-C", w.DRILL, "pull", "-q", "--rebase", "origin", "main"], timeout=120)
+ edit()
+ w.sh(["git", "-C", w.DRILL, "commit", "-qam", "NIGHT-A1b " + msg])
+ w.sh(["git", "-C", w.DRILL, "push", "-q", "origin", "main"], timeout=120)
+ w.say("drill: " + msg)
+
+
+HEAD = open(f"{CAT}/docker-compose.yml").read()
+FY = open(f"{CAT}/.felhom.yml").read()
+# (a)
+drill(lambda: open(f"{CAT}/docker-compose.yml", "w").write(HEAD.replace("nextcloud:34.0.4-apache", "nextcloud:34.0.97-notag")), "notag")
+w.sync_rescan(APP, "nextcloud:34.0.97-notag", tries=60)
+out["press_a"] = w.press_update(APP)
+out["log_a"] = w.guest("docker logs --since 3m felhom-controller 2>&1 | grep -iE 'nextcloud' | grep -iE 'Tier 2|precondition|same disk|same-disk' | tail -6")
+w.say("log (a):\n" + out["log_a"])
+drill(lambda: open(f"{CAT}/docker-compose.yml", "w").write(HEAD), "back to 34.0.4")
+out["record"] = w.guest("python3 -c \"import json;d=json.load(open('/var/lib/docker/volumes/felhom-controller-data/_data/data/settings.json'));print(json.dumps(d['app_backup']['nextcloud']['cross_drive']))\"")
+w.say("Tier-2 record: " + out["record"])
+dest = json.loads(out["record"]).get("destination_path", "")
+out["mirror_seeds"] = w.guest(f"find {dest}/backups/secondary/nextcloud -name 'felhom-seed-*' | sed 's|.*/||'; stat -c '%n dev=%d' {dest} /mnt/felhom-drives/scratch_hdd/userdata/nextcloud")
+w.say("mirror seeds + devices:\n" + out["mirror_seeds"])
+# (b)
+time.sleep(2)
+rc, code, _ = w.app_curl(SUB, f"/remote.php/dav/files/{A['uid']}/{names[0]}", "-u", f"{A['uid']}:{A['pw']}", method="DELETE")
+w.say(f"(b) DELETE {names[0]} -> {code}")
+files1 = "edited by the household " + str(time.time())
+ok = nf.put(w, SUB, A, names[1], files1)
+w.say(f"(b) PUT {names[1]} (newer than the copy) -> {ok}")
+out["after_b"] = nf.verify_files(w, SUB, A, {names[0]: files[names[0]], names[1]: files1, names[2]: files[names[2]]}, w.say)
+# (c)
+def break_edge():
+ open(f"{CAT}/docker-compose.yml", "w").write(HEAD.replace("image: redis:7-alpine", "image: redis:7.2-alpine"))
+ f = re.sub(r"(healthcheck:\n\s+checks:\n\s+- type: api\n\s+port: )80\b", r"\g<1>8999", FY, count=1)
+ assert f != FY, "probe port not found"
+ open(f"{CAT}/.felhom.yml", "w").write(f)
+
+
+drill(break_edge, "redis 7-alpine -> 7.2-alpine + probe 8999 (the failing edge)")
+w.sync_rescan(APP, "redis:7.2-alpine", tries=60)
+cut = {}
+code, d = w.ctl("POST", f"/api/stacks/{APP}/update")
+w.say(f"(c) Update -> {code}")
+seen, tp = None, time.time()
+while time.time() - tp < 1200:
+ st = w.stack(APP)
+ ph = st.get("update_phase")
+ if ph != seen and ph:
+ seen = ph
+ w.say(f" +{time.time()-tp:6.1f}s phase={ph}")
+ if ph == "verifying" and not cut:
+ cut["out"] = w.guest(f"""c=$(docker volume ls -q --filter label=felhom.undo-copy-of={APP} | head -1); echo "copy: $c"
+docker run --rm -v $c:/c alpine sh -c 'rm -f /c/felhom-undo-complete; ls /c'""")
+ w.say(" >>> marker removed:\n" + cut["out"])
+ if not st.get("updating") and ph in ("done", "failed", "undone") and time.time() - tp > 3:
+ break
+ time.sleep(0.5)
+st = w.stack(APP)
+out["held"] = {k: st.get(k) for k in ("update_phase", "hold_reason", "hold_no_whole_copy", "hold_kind", "state")}
+w.say("(c) held: " + json.dumps(out["held"], ensure_ascii=False))
+for lang in ("hu", "en"):
+ page = H.unescape(w.page(f"/apps/{APP}?lang={lang}"))
+ m = re.search(r'data-held="true">(.*?)', page, re.S)
+ out[f"page_{lang}"] = m.group(1).strip() if m else None
+ w.say(f"(c) [{lang}] {out[f'page_{lang}']!r}")
+# (d) the WHOLE restore: the button the page offers on the Mentések page for the second copy
+drill(lambda: (open(f"{CAT}/docker-compose.yml", "w").write(HEAD), open(f"{CAT}/.felhom.yml", "w").write(FY)), "back to the head + the real probe")
+w.sync_rescan()
+sess = open(f"{w.SC}/sess{__import__('os').getpid()}.txt").read().strip()
+csrf = open(f"{w.SC}/csrf{__import__('os').getpid()}.txt").read().strip()
+t0 = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
+r = w.sh(["curl", "-sk", "-D", "-", "-o", "/dev/null", "-H", w.HOSTHDR, "-H", f"Cookie: {sess}", "-X", "POST",
+ "--data-urlencode", f"_csrf={csrf}", "--data-urlencode", f"stack_name={APP}", f"{w.BASE}/backup/tier2/unit-restore"], timeout=120)
+w.say("(d) POST /backup/tier2/unit-restore -> " + (r.stdout or "").split("\n")[0] + " " + str([l for l in (r.stdout or "").split("\n") if l.lower().startswith("location")]))
+last = None
+for _ in range(600):
+ c2, dd = w.ctl("GET", "/api/backup/restore-status")
+ d2 = (dd.get("data") or {}) if isinstance(dd, dict) else {}
+ cur = (d2.get("running"), d2.get("message"))
+ if cur != last:
+ w.say(f" restore-status {cur}")
+ last = cur
+ if not d2.get("running") and d2.get("message"):
+ break
+ time.sleep(2)
+out["restore_msg"] = last
+out["restore_log"] = w.guest(f"docker logs --since {t0} felhom-controller 2>&1 | grep -iE 'whole restore|WHOLE restore|Tier-2 whole|Restore-from-unit|hold .*CLEARED' | tail -8")
+w.say("(d) log:\n" + out["restore_log"])
+# (e)
+w.wait_app(SUB, "/status.php", want=("200",), tries=120, delay=5)
+out["account"] = bool(F.verify(w, SUB, A, w.say))
+out["files_after"] = nf.verify_files(w, SUB, A, {names[0]: files[names[0]], names[1]: files1, names[2]: files[names[2]]}, w.say)
+out["kept_beside"] = w.guest(f"find /mnt/felhom-drives/scratch_hdd/userdata/nextcloud -name '*.felhom-*' | sed 's|.*/||'")
+st = w.stack(APP)
+out["final"] = {k: st.get(k) for k in ("state", "hold_reason", "update_phase")}
+w.say("(e) final " + json.dumps(out["final"], ensure_ascii=False) + " kept-beside: " + out["kept_beside"])
+json.dump(out, open("../A1/20-whole-restore.json", "w"), indent=2, ensure_ascii=False, default=str)
+open("../A1/20-whole-restore.log", "w").write("\n".join(w.LOG) + "\n")
diff --git a/documentation/audits/night-2026-09-24/tools/liveA1c.py b/documentation/audits/night-2026-09-24/tools/liveA1c.py
new file mode 100644
index 00000000..ef21cf43
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/tools/liveA1c.py
@@ -0,0 +1,33 @@
+import json, sys, time
+sys.path.insert(0, ".")
+import walk as w, fixtures as fx, ncfiles as nf
+F, SUB, APP = fx.Nextcloud(), "cloud-a1", "nextcloud"
+S = json.load(open(f"{w.SC}/a1-secret-state.json"))
+A, files = S["A"], S["files"]
+names = sorted(files)
+w.login()
+out = {}
+c, d = w.ctl("GET", "/api/backup/restore-status")
+out["restore_status"] = d
+w.say("restore-status: " + json.dumps(d, ensure_ascii=False)[:600])
+out["log"] = w.guest("docker logs --since 2026-09-24T10:24:55Z felhom-controller 2>&1 | grep -iE 'whole restore|WHOLE restore|Tier-2 whole|Restore-from-unit completed|hold .*CLEARED|Restoring Docker volume' | cut -c1-330 | tail -10")
+w.say("log:\n" + out["log"])
+w.wait_app(SUB, "/status.php", want=("200",), tries=60, delay=5)
+out["account"] = bool(F.verify(w, SUB, A, w.say))
+# file 0 was DELETED after the copy (must come back); file 1 was EDITED after the copy (newer — must stay edited)
+c0, b0 = nf.get(w, SUB, A, names[0]); c1, b1 = nf.get(w, SUB, A, names[1]); c2, b2 = nf.get(w, SUB, A, names[2])
+out["file0_back"] = (c0 == "200" and b0 == files[names[0]])
+out["file1_kept_edit"] = (c1 == "200" and b1.startswith("edited by the household"))
+out["file2_unchanged"] = (c2 == "200" and b2 == files[names[2]])
+w.say(f"file0 (deleted after the copy) back={out['file0_back']} ({c0}); file1 (edited after the copy) kept the edit={out['file1_kept_edit']}; file2 unchanged={out['file2_unchanged']}")
+out["occ_scan"] = w.guest(f"docker exec -u www-data nextcloud php occ files:scan {A['uid']} 2>&1 | tail -4")
+w.say("occ files:scan (the app sees its files): " + out["occ_scan"])
+c0b, b0b = nf.get(w, SUB, A, names[0])
+out["file0_back_after_scan"] = (c0b == "200" and b0b == files[names[0]])
+w.say(f"file0 after occ scan: {c0b} {out['file0_back_after_scan']}")
+out["kept_beside"] = w.guest("find /mnt/felhom-drives/scratch_hdd/userdata/nextcloud -name '*.felhom-*' | sed 's|.*/||'")
+st = w.stack(APP)
+out["final"] = {k: st.get(k) for k in ("state", "hold_reason", "update_phase")}
+w.say("final " + json.dumps(out["final"], ensure_ascii=False) + " | kept beside: " + repr(out["kept_beside"]))
+json.dump(out, open("../A1/21-readback.json", "w"), indent=2, ensure_ascii=False, default=str)
+open("../A1/21-readback.log", "w").write("\n".join(w.LOG) + "\n")
diff --git a/documentation/audits/night-2026-09-24/tools/liveA1d.py b/documentation/audits/night-2026-09-24/tools/liveA1d.py
new file mode 100644
index 00000000..ec9fd739
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/tools/liveA1d.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python3
+"""A1 live, whole: (a) a fresh second copy via the update's own backup; (b) through WebDAV: delete file 0, edit
+file 1 (live newer than the copy); (c) a failing update (probe 8999) whose undo copy is cut off → HOLD, the
+page naming the second drive; (d) the second copy's „Teljes visszaállítás" = the WHOLE restore; (e) read back."""
+import html as H, json, re, sys, time
+sys.path.insert(0, ".")
+import walk as w, fixtures as fx, ncfiles as nf
+F, SUB, APP = fx.Nextcloud(), "cloud-a1", "nextcloud"
+CAT = f"{w.DRILL}/templates/{APP}"
+S = json.load(open(f"{w.SC}/a1-secret-state.json"))
+A, files = S["A"], S["files"]
+names = sorted(files)
+out = {"controller": w.guest("cat /etc/felhom-controller-image").strip()}
+w.login()
+
+
+def drill(edit, msg):
+ import fcntl
+ with open(f"{w.SC}/drill.lock", "w") as lk:
+ fcntl.flock(lk, fcntl.LOCK_EX)
+ w.sh(["git", "-C", w.DRILL, "pull", "-q", "--rebase", "origin", "main"], timeout=120)
+ edit()
+ w.sh(["git", "-C", w.DRILL, "commit", "-qam", "NIGHT-A1b " + msg])
+ w.sh(["git", "-C", w.DRILL, "push", "-q", "origin", "main"], timeout=120)
+ w.say("drill: " + msg)
+
+
+HEAD = open(f"{CAT}/docker-compose.yml").read()
+FY = open(f"{CAT}/.felhom.yml").read()
+names = sorted(files)
+files1 = None
+def break_edge():
+ open(f"{CAT}/docker-compose.yml", "w").write(HEAD.replace("image: redis:7-alpine", "image: redis:7.4-alpine"))
+ f = re.sub(r"(healthcheck:\n\s+checks:\n\s+- type: api\n\s+port: )80\b", r"\g<1>8999", FY, count=1)
+ assert f != FY, "probe port not found"
+ open(f"{CAT}/.felhom.yml", "w").write(f)
+
+
+drill(break_edge, "redis 7-alpine -> 7.4-alpine + probe 8999 (the failing edge)")
+w.sync_rescan(APP, "redis:7.4-alpine", tries=60)
+cut = {}
+code, d = w.ctl("POST", f"/api/stacks/{APP}/update")
+w.say(f"(c) Update -> {code}")
+seen, tp = None, time.time()
+while time.time() - tp < 1200:
+ st = w.stack(APP)
+ ph = st.get("update_phase")
+ if ph != seen and ph:
+ seen = ph
+ w.say(f" +{time.time()-tp:6.1f}s phase={ph}")
+ if ph == "verifying" and not cut:
+ cut["out"] = w.guest(f"""c=$(docker volume ls -q --filter label=felhom.undo-copy-of={APP} | head -1); echo "copy: $c"
+docker run --rm -v $c:/c alpine sh -c 'rm -f /c/felhom-undo-complete; ls /c'""")
+ w.say(" >>> marker removed:\n" + cut["out"])
+ if not st.get("updating") and ph in ("done", "failed", "undone") and time.time() - tp > 3:
+ break
+ time.sleep(0.5)
+st = w.stack(APP)
+out["held"] = {k: st.get(k) for k in ("update_phase", "hold_reason", "hold_no_whole_copy", "hold_kind", "state")}
+w.say("(c) held: " + json.dumps(out["held"], ensure_ascii=False))
+for lang in ("hu", "en"):
+ page = H.unescape(w.page(f"/apps/{APP}?lang={lang}"))
+ m = re.search(r'data-held="true">(.*?)', page, re.S)
+ out[f"page_{lang}"] = m.group(1).strip() if m else None
+ w.say(f"(c) [{lang}] {out[f'page_{lang}']!r}")
+# decision 27, positive control / refusal: what the Remove dialog reads, and the Remove itself
+code, hd = w.ctl("GET", f"/api/stacks/{APP}/hdd-data")
+out["hdd_data"] = {"code": code, "keep_data_only": ((hd or {}).get("data") or {}).get("keep_data_only")}
+w.say("(c2) hdd-data -> " + json.dumps(out["hdd_data"]))
+# (d) the WHOLE restore: the button the page offers on the Mentések page for the second copy
+drill(lambda: (open(f"{CAT}/docker-compose.yml", "w").write(HEAD), open(f"{CAT}/.felhom.yml", "w").write(FY)), "back to the head + the real probe")
+w.sync_rescan()
+sess = open(f"{w.SC}/sess{__import__('os').getpid()}.txt").read().strip()
+csrf = open(f"{w.SC}/csrf{__import__('os').getpid()}.txt").read().strip()
+t0 = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
+r = w.sh(["curl", "-sk", "-D", "-", "-o", "/dev/null", "-H", w.HOSTHDR, "-H", f"Cookie: {sess}", "-X", "POST",
+ "--data-urlencode", f"_csrf={csrf}", "--data-urlencode", f"stack_name={APP}", f"{w.BASE}/backup/tier2/unit-restore"], timeout=120)
+w.say("(d) POST /backup/tier2/unit-restore -> " + (r.stdout or "").split("\n")[0] + " " + str([l for l in (r.stdout or "").split("\n") if l.lower().startswith("location")]))
+last = None
+for _ in range(600):
+ c2, dd = w.ctl("GET", "/api/backup/restore-status")
+ d2 = (dd.get("data") or {}) if isinstance(dd, dict) else {}
+ cur = (d2.get("running"), d2.get("message"))
+ if cur != last:
+ w.say(f" restore-status {cur}")
+ last = cur
+ if not d2.get("running") and d2.get("last"):
+ break
+ time.sleep(2)
+out["restore_msg"] = last
+out["restore_log"] = w.guest(f"docker logs --since {t0} felhom-controller 2>&1 | grep -iE 'whole restore|WHOLE restore|Tier-2 whole|Restore-from-unit|hold .*CLEARED' | tail -8")
+w.say("(d) log:\n" + out["restore_log"])
+# (e)
+w.wait_app(SUB, "/status.php", want=("200",), tries=120, delay=5)
+out["account"] = bool(F.verify(w, SUB, A, w.say))
+out["files_after"] = nf.verify_files(w, SUB, A, {names[0]: files[names[0]], names[2]: files[names[2]]}, w.say)
+out["kept_beside"] = w.guest(f"find /mnt/felhom-drives/scratch_hdd/userdata/nextcloud -name '*.felhom-*' | sed 's|.*/||'")
+st = w.stack(APP)
+out["final"] = {k: st.get(k) for k in ("state", "hold_reason", "update_phase")}
+w.say("(e) final " + json.dumps(out["final"], ensure_ascii=False) + " kept-beside: " + out["kept_beside"])
+json.dump(out, open("../A1/30-held-then-whole.json", "w"), indent=2, ensure_ascii=False, default=str)
+open("../A1/30-held-then-whole.log", "w").write("\n".join(w.LOG) + "\n")
diff --git a/documentation/audits/night-2026-09-24/tools/liveA1e.py b/documentation/audits/night-2026-09-24/tools/liveA1e.py
new file mode 100644
index 00000000..17f76a1d
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/tools/liveA1e.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python3
+"""A1 live, whole: (a) a fresh second copy via the update's own backup; (b) through WebDAV: delete file 0, edit
+file 1 (live newer than the copy); (c) a failing update (probe 8999) whose undo copy is cut off → HOLD, the
+page naming the second drive; (d) the second copy's „Teljes visszaállítás" = the WHOLE restore; (e) read back."""
+import html as H, json, re, sys, time
+sys.path.insert(0, ".")
+import walk as w, fixtures as fx, ncfiles as nf
+F, SUB, APP = fx.Nextcloud(), "cloud-a1", "nextcloud"
+CAT = f"{w.DRILL}/templates/{APP}"
+MIR = "/mnt/sys_drive/felhom-data/backups/secondary/nextcloud"
+S = json.load(open(f"{w.SC}/a1-secret-state.json"))
+A, files = S["A"], S["files"]
+names = sorted(files)
+out = {"controller": w.guest("cat /etc/felhom-controller-image").strip()}
+w.login()
+
+
+def drill(edit, msg):
+ import fcntl
+ with open(f"{w.SC}/drill.lock", "w") as lk:
+ fcntl.flock(lk, fcntl.LOCK_EX)
+ w.sh(["git", "-C", w.DRILL, "pull", "-q", "--rebase", "origin", "main"], timeout=120)
+ edit()
+ w.sh(["git", "-C", w.DRILL, "commit", "-qam", "NIGHT-A2 " + msg])
+ w.sh(["git", "-C", w.DRILL, "push", "-q", "origin", "main"], timeout=120)
+ w.say("drill: " + msg)
+
+
+HEAD = open(f"{CAT}/docker-compose.yml").read()
+FY = open(f"{CAT}/.felhom.yml").read()
+names = sorted(files)
+files1 = None
+def break_edge():
+ open(f"{CAT}/docker-compose.yml", "w").write(HEAD.replace("image: redis:7-alpine", "image: redis:7.4.0-alpine"))
+ f = re.sub(r"(healthcheck:\n\s+checks:\n\s+- type: api\n\s+port: )80\b", r"\g<1>8999", FY, count=1)
+ assert f != FY, "probe port not found"
+ open(f"{CAT}/.felhom.yml", "w").write(f)
+
+
+drill(break_edge, "redis 7-alpine -> 7.4.0-alpine + probe 8999 (the failing edge)")
+w.sync_rescan(APP, "redis:7.4.0-alpine", tries=60)
+cut = {}
+code, d = w.ctl("POST", f"/api/stacks/{APP}/update")
+w.say(f"(c) Update -> {code}")
+seen, tp = None, time.time()
+while time.time() - tp < 1200:
+ st = w.stack(APP)
+ ph = st.get("update_phase")
+ if ph != seen and ph:
+ seen = ph
+ w.say(f" +{time.time()-tp:6.1f}s phase={ph}")
+ if ph == "verifying" and not cut:
+ cut["out"] = w.guest(f"""c=$(docker volume ls -q --filter label=felhom.undo-copy-of={APP} | head -1); echo "copy: $c"
+docker run --rm -v $c:/c alpine sh -c 'rm -f /c/felhom-undo-complete; ls /c'
+mv {MIR} {MIR}.night-aside && echo "second-drive mirror moved aside (a one-drive box)" """)
+ w.say(" >>> marker removed:\n" + cut["out"])
+ if not st.get("updating") and ph in ("done", "failed", "undone") and time.time() - tp > 3:
+ break
+ time.sleep(0.5)
+st = w.stack(APP)
+out["held"] = {k: st.get(k) for k in ("update_phase", "hold_reason", "hold_no_whole_copy", "hold_kind", "state")}
+w.say("(c) held: " + json.dumps(out["held"], ensure_ascii=False))
+for lang in ("hu", "en"):
+ page = H.unescape(w.page(f"/apps/{APP}?lang={lang}"))
+ m = re.search(r'data-held="true">(.*?)', page, re.S)
+ out[f"page_{lang}"] = m.group(1).strip() if m else None
+ w.say(f"(c) [{lang}] {out[f'page_{lang}']!r}")
+# decision 27, positive control / refusal: what the Remove dialog reads, and the Remove itself
+code, hd = w.ctl("GET", f"/api/stacks/{APP}/hdd-data")
+out["hdd_data"] = {"code": code, "keep_data_only": ((hd or {}).get("data") or {}).get("keep_data_only")}
+w.say("(c2) hdd-data -> " + json.dumps(out["hdd_data"]))
+for lang in ("hu", "en"):
+ code, rr = w.ctl("POST", f"/api/stacks/{APP}/remove?lang={lang}", {"remove_hdd_data": True, "remove_backups": False})
+ out[f"remove_{lang}"] = {"code": code, "error": (rr or {}).get("error")}
+ w.say(f"(c3) [{lang}] Remove with data -> {code} {out[f'remove_{lang}']['error']!r}")
+code, rr = w.ctl("POST", f"/api/stacks/{APP}/remove?lang=en", {"remove_hdd_data": False, "remove_backups": True})
+out["remove_backups_en"] = {"code": code, "error": (rr or {}).get("error")}
+w.say(f"(c3) [en] Remove with backups -> {code}")
+st = w.stack(APP)
+out["still_deployed"] = st.get("deployed")
+w.say(f"(c3) still deployed: {st.get('deployed')}")
+out["mirror_back"] = w.guest(f"mv {MIR}.night-aside {MIR} && ls {MIR}")
+w.say("(c4) mirror moved back: " + out["mirror_back"].replace(chr(10), " "))
+# (d) the WHOLE restore: the button the page offers on the Mentések page for the second copy
+drill(lambda: (open(f"{CAT}/docker-compose.yml", "w").write(HEAD), open(f"{CAT}/.felhom.yml", "w").write(FY)), "back to the head + the real probe")
+w.sync_rescan()
+sess = open(f"{w.SC}/sess{__import__('os').getpid()}.txt").read().strip()
+csrf = open(f"{w.SC}/csrf{__import__('os').getpid()}.txt").read().strip()
+t0 = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
+r = w.sh(["curl", "-sk", "-D", "-", "-o", "/dev/null", "-H", w.HOSTHDR, "-H", f"Cookie: {sess}", "-X", "POST",
+ "--data-urlencode", f"_csrf={csrf}", "--data-urlencode", f"stack_name={APP}", f"{w.BASE}/backup/tier2/unit-restore"], timeout=120)
+w.say("(d) POST /backup/tier2/unit-restore -> " + (r.stdout or "").split("\n")[0] + " " + str([l for l in (r.stdout or "").split("\n") if l.lower().startswith("location")]))
+last = None
+for _ in range(600):
+ c2, dd = w.ctl("GET", "/api/backup/restore-status")
+ d2 = (dd.get("data") or {}) if isinstance(dd, dict) else {}
+ cur = (d2.get("running"), d2.get("message"))
+ if cur != last:
+ w.say(f" restore-status {cur}")
+ last = cur
+ if not d2.get("running") and d2.get("last"):
+ break
+ time.sleep(2)
+out["restore_msg"] = last
+out["restore_log"] = w.guest(f"docker logs --since {t0} felhom-controller 2>&1 | grep -iE 'whole restore|WHOLE restore|Tier-2 whole|Restore-from-unit|hold .*CLEARED' | tail -8")
+w.say("(d) log:\n" + out["restore_log"])
+# (e)
+w.wait_app(SUB, "/status.php", want=("200",), tries=120, delay=5)
+out["account"] = bool(F.verify(w, SUB, A, w.say))
+out["files_after"] = nf.verify_files(w, SUB, A, {names[0]: files[names[0]], names[2]: files[names[2]]}, w.say)
+out["kept_beside"] = w.guest(f"find /mnt/felhom-drives/scratch_hdd/userdata/nextcloud -name '*.felhom-*' | sed 's|.*/||'")
+st = w.stack(APP)
+out["final"] = {k: st.get(k) for k in ("state", "hold_reason", "update_phase")}
+w.say("(e) final " + json.dumps(out["final"], ensure_ascii=False) + " kept-beside: " + out["kept_beside"])
+json.dump(out, open("../A2/10-held-no-copy-keep-data.json", "w"), indent=2, ensure_ascii=False, default=str)
+open("../A2/10-held-no-copy-keep-data.log", "w").write("\n".join(w.LOG) + "\n")
diff --git a/documentation/audits/night-2026-09-24/tools/liveA3b.py b/documentation/audits/night-2026-09-24/tools/liveA3b.py
new file mode 100644
index 00000000..98660079
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/tools/liveA3b.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+"""A3 live, part 2: Start on the box-stopped gokapi lifts the hold and gives ONE more try; the loop comes back,
+the box stops it again (trip 2 within 24 h) and the page says support is informed. hu + en. No app_start_failed."""
+import html as H, json, re, sys, time
+sys.path.insert(0, ".")
+import walk as w
+APP = "gokapi"
+out = {"controller": w.guest("cat /etc/felhom-controller-image").strip()}
+w.login()
+t0 = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
+st = w.stack(APP)
+out["before"] = {k: st.get(k) for k in ("state", "hold_kind", "hold_reason")}
+w.say("before " + json.dumps(out["before"], ensure_ascii=False))
+code, d = w.ctl("POST", f"/api/stacks/{APP}/start")
+w.say(f"Start -> {code} {json.dumps(d, ensure_ascii=False)[:200]}")
+time.sleep(5)
+st = w.stack(APP)
+out["after_start"] = {k: st.get(k) for k in ("state", "hold_kind", "hold_reason")}
+w.say("after start " + json.dumps(out["after_start"], ensure_ascii=False))
+tp, last = time.time(), None
+while time.time() - tp < 1800:
+ st = w.stack(APP)
+ cur = (st.get("state"), st.get("hold_kind"))
+ if cur != last:
+ rc = w.guest("docker inspect -f '{{.Name}} {{.RestartCount}}' $(docker ps -aq --filter label=com.docker.compose.project=gokapi)").strip()
+ w.say(f" +{time.time()-tp:6.0f}s state={cur[0]} hold={cur[1]} restarts: {rc}")
+ last = cur
+ if cur[1] == "unhealthy_stop":
+ break
+ time.sleep(15)
+out["tripped_after_s"] = round(time.time() - tp)
+for lang in ("hu", "en"):
+ page = H.unescape(w.page(f"/apps/{APP}?lang={lang}"))
+ m = re.search(r'data-unhealthy-stop[^>]*>(.*?)', page, re.S)
+ txt = re.sub(r"<[^>]+>", " ", m.group(1)).strip() if m else None
+ out[f"page_{lang}"] = re.sub(r"\s+", " ", txt) if txt else None
+ w.say(f"[{lang}] {out[f'page_{lang}']!r}")
+out["log"] = w.guest(f"docker logs --since {t0} felhom-controller 2>&1 | grep -E 'deadapp|STOPPED by the box|hold (SET|CLEARED|lifted)|unhealthy|app_start_failed|app_stopped' | tail -15")
+w.say("log:\n" + out["log"])
+out["start_failed_lines"] = w.guest(f"docker logs --since {t0} felhom-controller 2>&1 | grep -c app_start_failed || true").strip()
+w.say("app_start_failed lines: " + out["start_failed_lines"])
+json.dump(out, open("../A3/30-start-then-trip2.json", "w"), indent=2, ensure_ascii=False, default=str)
+open("../A3/30-start-then-trip2.log", "w").write("\n".join(w.LOG) + "\n")
diff --git a/documentation/audits/night-2026-09-24/tools/liveB.py b/documentation/audits/night-2026-09-24/tools/liveB.py
new file mode 100644
index 00000000..c63b4da1
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/tools/liveB.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python3
+"""Part B live: a FLOATING tag (redis:7-alpine) whose tested digest moves in the catalog. (1) control: the box
+runs the ladder's digest, badge current; (2) the drill ladder's head entry gets a different tested digest for the
+same tag + a newer tested_at → badge must say behind (hu+en); (3) Update → the box pulls THAT digest, the compose
+it runs names it, installed record's digest = it, badge current. Also: the pin and record stay digest-free."""
+import json, re, sys, time, datetime
+sys.path.insert(0, ".")
+import walk as w
+APP, SVC = "nextcloud", "nextcloud-redis"
+NEW = "sha256:c35af3bbcef51a62c8bae5a9a563c6f1b60d7ebaea4cb5a3ccbcc157580ae098" # redis:7.4.0-alpine, stands in for a repush of 7-alpine
+CAT = f"{w.DRILL}/templates/{APP}"
+out = {"controller": w.guest("cat /etc/felhom-controller-image").strip()}
+w.login()
+
+
+def boxview(tag):
+ st = w.stack(APP)
+ ac = st.get("app_config") or {}
+ v = {"state": st.get("state"), "installed": ac.get("installed_images"), "pinned": ac.get("pinned_images"),
+ "catalog_images": st.get("catalog_images"), "catalog_digests": st.get("catalog_digests"),
+ "catalog_tested_at": st.get("catalog_tested_at"), "badges": w.badges(APP),
+ "compose_redis": w.guest(f"grep -n 'image:' /opt/docker/stacks/{APP}/docker-compose.yml | grep redis").strip(),
+ "running_redis": w.guest(f"docker inspect -f '{{{{.Config.Image}}}} {{{{.Image}}}}' {SVC}").strip()}
+ out[tag] = v
+ w.say(f"[{tag}] " + json.dumps({k: v[k] for k in ("state", "badges", "compose_redis", "running_redis")}, ensure_ascii=False))
+ w.say(f"[{tag}] installed={json.dumps(v['installed'])} pinned={json.dumps(v['pinned'])}")
+ return v
+
+
+w.sync_rescan()
+boxview("1-control")
+fy = open(f"{CAT}/.felhom.yml").read()
+lines = fy.split("\n")
+idx = max(i for i, l in enumerate(lines) if l.startswith(" - {") and '"to"' in l)
+e = json.loads(lines[idx][4:])
+old = e["digest"][SVC]
+e["digest"][SVC] = NEW
+e["tested_at"] = datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
+e["note"] = "NIGHT-B drill: tested digest of redis:7-alpine moved (stand-in digest of redis:7.4.0-alpine)"
+lines[idx] = " - " + json.dumps(e, ensure_ascii=False)
+out["ladder_edit"] = {"old": old, "new": NEW, "tested_at": e["tested_at"]}
+
+
+def edit():
+ open(f"{CAT}/.felhom.yml", "w").write("\n".join(lines))
+
+
+import fcntl
+with open(f"{w.SC}/drill.lock", "w") as lk:
+ fcntl.flock(lk, fcntl.LOCK_EX)
+ w.sh(["git", "-C", w.DRILL, "pull", "-q", "--rebase", "origin", "main"], timeout=120)
+ edit()
+ w.sh(["git", "-C", w.DRILL, "commit", "-qam", "NIGHT-B nextcloud: tested digest of redis:7-alpine moved"])
+ w.sh(["git", "-C", w.DRILL, "push", "-q", "origin", "main"], timeout=120)
+w.say("drill: ladder head entry digest " + old[:19] + " -> " + NEW[:19])
+w.sync_rescan()
+for _ in range(20):
+ v = boxview("2-behind")
+ if (v.get("catalog_digests") or {}).get(SVC) == NEW:
+ break
+ time.sleep(10)
+ w.sync_rescan()
+out["update"] = w.press_update(APP)
+w.sync_rescan()
+boxview("3-after")
+out["pull_log"] = w.guest("docker logs --since 10m felhom-controller 2>&1 | grep nextcloud | grep -E 'pin advanced|digest|pull|UPDATED|done|installed' | grep -v DEBUG | tail -10")
+w.say("log:\n" + out["pull_log"])
+json.dump(out, open("../B/10-floating-tag.json", "w"), indent=2, ensure_ascii=False, default=str)
+open("../B/10-floating-tag.log", "w").write("\n".join(w.LOG) + "\n")
diff --git a/documentation/audits/night-2026-09-24/tools/liveB2.py b/documentation/audits/night-2026-09-24/tools/liveB2.py
new file mode 100644
index 00000000..694b7c51
--- /dev/null
+++ b/documentation/audits/night-2026-09-24/tools/liveB2.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+"""Part B live: a FLOATING tag (redis:7-alpine) whose tested digest moves in the catalog. (1) control: the box
+runs the ladder's digest, badge current; (2) the drill ladder's head entry gets a different tested digest for the
+same tag + a newer tested_at → badge must say behind (hu+en); (3) Update → the box pulls THAT digest, the compose
+it runs names it, installed record's digest = it, badge current. Also: the pin and record stay digest-free."""
+import json, re, sys, time, datetime
+sys.path.insert(0, ".")
+import walk as w
+APP, SVC = "nextcloud", "nextcloud-redis"
+NEW = "sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499" # back to the registry's real 7-alpine digest
+CAT = f"{w.DRILL}/templates/{APP}"
+out = {"controller": w.guest("cat /etc/felhom-controller-image").strip()}
+w.login()
+
+
+def boxview(tag):
+ st = w.stack(APP)
+ ac = st.get("app_config") or {}
+ v = {"state": st.get("state"), "installed": ac.get("installed_images"), "pinned": ac.get("pinned_images"),
+ "catalog_images": st.get("catalog_images"), "catalog_digests": st.get("catalog_digests"),
+ "catalog_tested_at": st.get("catalog_tested_at"), "badges": w.badges(APP),
+ "compose_redis": w.guest(f"grep -n 'image:' /opt/docker/stacks/{APP}/docker-compose.yml | grep redis").strip(),
+ "running_redis": w.guest(f"docker inspect -f '{{{{.Config.Image}}}} {{{{.Image}}}}' {SVC}").strip()}
+ out[tag] = v
+ w.say(f"[{tag}] " + json.dumps({k: v[k] for k in ("state", "badges", "compose_redis", "running_redis")}, ensure_ascii=False))
+ w.say(f"[{tag}] installed={json.dumps(v['installed'])} pinned={json.dumps(v['pinned'])}")
+ return v
+
+
+w.sync_rescan()
+boxview("1-control")
+fy = open(f"{CAT}/.felhom.yml").read()
+lines = fy.split("\n")
+idx = max(i for i, l in enumerate(lines) if l.startswith(" - {") and '"to"' in l)
+e = json.loads(lines[idx][4:])
+old = e["digest"][SVC]
+e["digest"][SVC] = NEW
+e["tested_at"] = datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
+e["note"] = "NIGHT-B2 drill: re-tested at the registry digest"
+lines[idx] = " - " + json.dumps(e, ensure_ascii=False)
+out["ladder_edit"] = {"old": old, "new": NEW, "tested_at": e["tested_at"]}
+
+
+def edit():
+ open(f"{CAT}/.felhom.yml", "w").write("\n".join(lines))
+
+
+import fcntl
+with open(f"{w.SC}/drill.lock", "w") as lk:
+ fcntl.flock(lk, fcntl.LOCK_EX)
+ w.sh(["git", "-C", w.DRILL, "pull", "-q", "--rebase", "origin", "main"], timeout=120)
+ edit()
+ w.sh(["git", "-C", w.DRILL, "commit", "-qam", "NIGHT-B2 nextcloud: re-tested redis:7-alpine (v0.269.1 proof)"])
+ w.sh(["git", "-C", w.DRILL, "push", "-q", "origin", "main"], timeout=120)
+w.say("drill: ladder head entry digest " + old[:19] + " -> " + NEW[:19])
+w.sync_rescan()
+for _ in range(20):
+ v = boxview("2-behind")
+ if (v.get("catalog_digests") or {}).get(SVC) == NEW:
+ break
+ time.sleep(10)
+ w.sync_rescan()
+v2 = out["2-behind"]
+out["sync_kept_running_digest"] = ("c35af3bb" in v2["compose_redis"]) and (NEW not in v2["compose_redis"])
+w.say(f"CHECK the sync kept the running digest in the compose before Update: {out['sync_kept_running_digest']}")
+out["update"] = w.press_update(APP)
+w.sync_rescan()
+boxview("3-after")
+out["pull_log"] = w.guest("docker logs --since 10m felhom-controller 2>&1 | grep nextcloud | grep -E 'pin advanced|digest|pull|UPDATED|done|installed' | grep -v DEBUG | tail -10")
+w.say("log:\n" + out["pull_log"])
+json.dump(out, open("../B/21-floating-tag-0.269.1.json", "w"), indent=2, ensure_ascii=False, default=str)
+open("../B/21-floating-tag-0.269.1.log", "w").write("\n".join(w.LOG) + "\n")