From 796c373a3a6971c09588abe47694858d017c6866 Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Sat, 17 Oct 2026 04:50:09 +0200 Subject: [PATCH] 0.324.0 is the floor (02:46 UTC) after the night's off-site runs on all three test boxes; R-1105, R-1107 closed --- REPORT-night-2026-10-17.md | 10 ++++++--- STATUS.md | 4 ++-- .../release/global-floor-readback.txt | 5 +++++ .../release/global-floor.txt | 4 ++++ .../release/night-offsite-check.txt | 4 ++++ .../release/night-offsite-logs.txt | 21 +++++++++++++++++++ documentation/backlog/CLOSED-ITEMS.md | 2 ++ documentation/backlog/OPEN-ITEMS.md | 4 +--- 8 files changed, 46 insertions(+), 8 deletions(-) create mode 100644 documentation/audits/night-restore-2026-10-17/release/global-floor-readback.txt create mode 100644 documentation/audits/night-restore-2026-10-17/release/global-floor.txt create mode 100644 documentation/audits/night-restore-2026-10-17/release/night-offsite-check.txt create mode 100644 documentation/audits/night-restore-2026-10-17/release/night-offsite-logs.txt diff --git a/REPORT-night-2026-10-17.md b/REPORT-night-2026-10-17.md index a8bf1304..54395e0c 100644 --- a/REPORT-night-2026-10-17.md +++ b/REPORT-night-2026-10-17.md @@ -77,12 +77,16 @@ declared value — not lowered). Per-box floors at 21:51:39 UTC (`303` → `floo demo-felhom on 0.324.0 at 21:52 UTC**, each through the normal update path. Box walk: no container unhealthy, restarting, exited or created (28 / 9 / 17); restic 0.19.1 in each; dashboards 200; https-only 37 names OK (`release/`). -NIGHT-PLACEHOLDER +**Tonight's off-site runs on 0.324.0:** demo-hp `backup OK` 02:18:07 UTC (14 apps, 296 snapshots, 2m59s), demo-felhom +02:15:36 (5 apps, 25), Tester 1 02:17:16 (10 apps, 60) — read at 02:45 UTC on each box and in each log +(`release/night-offsite-*.txt`). **Global floor 0.324.0 / MinAgent 0.158.1 at 02:46:24 UTC** (`303` → `flash=floor_set`; +the brief allowed it tonight); per-box floors cleared. Read back on two channels: one `tar` of `hub.db` + `-wal` + `-shm` +(integrity ok: `0.324.0`, `0.324.0=0.158.1`, no per-box floor) and the hub's own `/configuration` page. ## 7. Rows **Before 177. Opened 7** (R-1109 … R-1115). **Closed 8** (R-1106, R-127, R-926, R-1108, R-1065, R-1017, R-983, R-976). -FLOOR-ROWS-PLACEHOLDER R-1096 corrected and READY. `unproven.py`: NOT WALKED 35 of 55, unchanged. +**Closed 2 more with the floor** (R-1105, R-1107). **After: 174** (177 + 7 − 10). R-1096 corrected and READY. `unproven.py`: NOT WALKED 35 of 55, unchanged. ## 8. Observations @@ -105,4 +109,4 @@ FLOOR-ROWS-PLACEHOLDER R-1096 corrected and READY. `unproven.py`: NOT WALKED 35 left); my marker files and the cover file removed; sharing off, the test share and its folders removed. 9201 runs 0.324.0. The old Kimai hold is not mine and stays. - **Host:** nothing. -- **Hub:** per-box floors set (§6). +- **Hub:** per-box floors set and cleared; the global floor raised to 0.324.0 / MinAgent 0.158.1 (the release, §6). diff --git a/STATUS.md b/STATUS.md index 5834a3a2..a66f46e5 100644 --- a/STATUS.md +++ b/STATUS.md @@ -2,7 +2,7 @@ **Ready for the first real tester (Tester-2): yes. Tester 2 (a laptop) is off; nothing was sent to it.** -**Updated 2026-10-17 (night).** Controller 0.324.0 runs on the three test boxes: a restore now brings the database password back with the data (your 1A), restic 0.19.1 (your 2A), dashboard hardening — `REPORT-night-2026-10-17.md`. The update for every box: see „What we work on". **Earlier (2026-10-16 evening):** 0.323.0, Debian 13 — `REPORT-debian13-restic-2026-10-16.md`. **Earlier (midday):** Controller 0.319.0 (the app page in panels, the login in one panel) is the floor, MinAgent 0.158.1 — `REPORT-app-page-tuning-2026-10-16.md`. **Earlier (morning):** Storage identity build B shipped: agent 0.158.1 and golden 0.318.0 vouched by you, controller 0.318.0 is the floor (MinAgent 0.158.1), installer 1.33.0 published — new one-disk boxes get their own data area. Open items: see `documentation/backlog/OPEN-ITEMS.md`. Older notes, day by day: +**Updated 2026-10-17 (night).** Controller 0.324.0 is the floor for every box since 02:46 UTC (all three test boxes made tonight's off-site backup on it first): a restore now brings the database password back with the data (your 1A), restic 0.19.1 (your 2A), dashboard hardening — `REPORT-night-2026-10-17.md`. **Earlier (2026-10-16 evening):** 0.323.0, Debian 13 — `REPORT-debian13-restic-2026-10-16.md`. **Earlier (midday):** Controller 0.319.0 (the app page in panels, the login in one panel) is the floor, MinAgent 0.158.1 — `REPORT-app-page-tuning-2026-10-16.md`. **Earlier (morning):** Storage identity build B shipped: agent 0.158.1 and golden 0.318.0 vouched by you, controller 0.318.0 is the floor (MinAgent 0.158.1), installer 1.33.0 published — new one-disk boxes get their own data area. Open items: see `documentation/backlog/OPEN-ITEMS.md`. Older notes, day by day: `documentation/backlog/STATUS-HISTORY.md`. Reports: `REPORT-night-2026-10-14.md`, `REPORT-immich-rollout-2026-10-14.md`, `REPORT-seo-felho-tarhely-2026-10-14.md`, `REPORT-immich-repair-2026-10-14.md`, `REPORT-power-cut-fixes-2026-10-14.md`, `REPORT-night-power-2026-10-13.md`, `REPORT-poweroff.md`. ## What works @@ -38,7 +38,7 @@ - Tester 2 has never made an off-site check (R-862). The scratch box cannot read the drill catalog (R-992). ## What we work on -- **The update for every box (R-1105, R-1107) waits on tonight's off-site backups on 0.324.0.** The night session checks from 02:45 UTC and raises the floor if all three are `ok`. If it could not, the morning check is: `ssh "pct exec 9201 -- python3 -c \"import json;o=json.load(open('/var/lib/docker/volumes/felhom-controller-data/_data/data/settings.json'))['offbox'];print(o['last_status'],o['last_success'])\""` for `demo-hp`, `felhom-pve`, `root@192.168.0.154`; all `ok` after 2026-10-17T00:00Z on 0.324.0 → global floor `min_controller_version=0.324.0`, `min_agent=0.158.1` (`felhom-build-deploy` step 5), read back with the WAL, clear the three per-box floors. After 2026-10-18 14:16 UTC: the old restic still reads demo-hp's store after its first 0.19.1 prune (`audits/night-restore-2026-10-17/tools/rx.sh`). +- **After 2026-10-18 14:16 UTC** (demo-hp's next clean-up window): check that the old restic (image 0.322.0) still lists and restores demo-hp's store after its first real 0.19.1 prune (`audits/night-restore-2026-10-17/tools/rx.sh`). Everything else of R-1105/R-1107 is done. - The power-cut findings still open (R-1004, R-1005, R-1010, R-1012, R-1013). - Media apps on every drive (R-1023): live with the 0.313.0 floor — every drive in every media app, a new drive made ready by the box, a pulled drive loses nothing, a safe removal lets go first and the drive comes back by itself. diff --git a/documentation/audits/night-restore-2026-10-17/release/global-floor-readback.txt b/documentation/audits/night-restore-2026-10-17/release/global-floor-readback.txt new file mode 100644 index 00000000..5ff08645 --- /dev/null +++ b/documentation/audits/night-restore-2026-10-17/release/global-floor-readback.txt @@ -0,0 +1,5 @@ +integrity: ok +('min_controller_version', '0.324.0') +('min_controller_version_declared_min_agent', '0.324.0=0.158.1') +per-box floors still set: [] +hub /configuration page: 5 0.158.1 3 0.324.0 diff --git a/documentation/audits/night-restore-2026-10-17/release/global-floor.txt b/documentation/audits/night-restore-2026-10-17/release/global-floor.txt new file mode 100644 index 00000000..b4984fed --- /dev/null +++ b/documentation/audits/night-restore-2026-10-17/release/global-floor.txt @@ -0,0 +1,4 @@ +global floor 2026-10-17T02:46:24Z: HTTP/1.1 303 See Other Location: /configuration?flash=floor_set +clear per-box tester-1: HTTP/1.1 303 See Other Location: /customers/tester-1?flash=floor_set +clear per-box demo-hp: HTTP/1.1 303 See Other Location: /customers/demo-hp?flash=floor_set +clear per-box demo-felhom: HTTP/1.1 303 See Other Location: /customers/demo-felhom?flash=floor_set diff --git a/documentation/audits/night-restore-2026-10-17/release/night-offsite-check.txt b/documentation/audits/night-restore-2026-10-17/release/night-offsite-check.txt new file mode 100644 index 00000000..1b5b4e02 --- /dev/null +++ b/documentation/audits/night-restore-2026-10-17/release/night-offsite-check.txt @@ -0,0 +1,4 @@ +02:45Z | demo-hp: gitea.dooplex.hu/admin/felhom-controller:0.324.0 ok 2026-10-17T02:18:07Z 2026-10-17T02:18:07Z 296 | felhom-pve: gitea.dooplex.hu/admin/felhom-controller:0.324.0 ok 2026-10-17T02:15:36Z 2026-10-17T02:15:36Z 25 | root@192.168.0.154: gitea.dooplex.hu/admin/felhom-controller:0.324.0 ok 2026-10-17T02:17:16Z 2026-10-17T02:17:16Z 60 +ALL THREE OK + +[exited with code 0] diff --git a/documentation/audits/night-restore-2026-10-17/release/night-offsite-logs.txt b/documentation/audits/night-restore-2026-10-17/release/night-offsite-logs.txt new file mode 100644 index 00000000..2a909a7b --- /dev/null +++ b/documentation/audits/night-restore-2026-10-17/release/night-offsite-logs.txt @@ -0,0 +1,21 @@ +=== demo-hp +2026/10/17 02:17:50 offbox.go:1504: [INFO] [offbox] backed up bookstack (/mnt/sys_drive/felhom-data/backups/primary/bookstack, 0 mandatory path(s)) +2026/10/17 02:17:52 offbox.go:1504: [INFO] [offbox] backed up docmost (/mnt/sys_drive/felhom-data/backups/primary/docmost, 0 mandatory path(s)) +2026/10/17 02:17:54 offbox.go:1504: [INFO] [offbox] backed up opengist (/mnt/sys_drive/felhom-data/backups/primary/opengist, 0 mandatory path(s)) +2026/10/17 02:17:56 offbox.go:1504: [INFO] [offbox] backed up privatebin (/mnt/sys_drive/felhom-data/backups/primary/privatebin, 0 mandatory path(s)) +2026/10/17 02:17:58 offbox_window.go:234: [INFO] [offbox] retention skipped (after-run): no clean-up window now (not due (last window 2026-10-12T02:16:53Z)) — nothing deleted (decision 68) +2026/10/17 02:18:07 offbox.go:1266: [INFO] [offbox] backup OK: 14 app(s) backed up, 1 skipped (no unit), 296 snapshot(s), 2m59s +=== felhom-pve +2026/10/17 02:15:24 [INFO] [offbox] backed up prowlarr (/mnt/sys_drive/felhom-data/backups/primary/prowlarr, 0 mandatory path(s)) +2026/10/17 02:15:27 [INFO] [offbox] backed up qbittorrent (/mnt/felhom-drives/hdd_1/backups/primary/qbittorrent, 0 mandatory path(s)) +2026/10/17 02:15:29 [INFO] [offbox] backed up radarr (/mnt/felhom-drives/hdd_1/backups/primary/radarr, 0 mandatory path(s)) +2026/10/17 02:15:31 [INFO] [offbox] backed up sonarr (/mnt/felhom-drives/hdd_1/backups/primary/sonarr, 0 mandatory path(s)) +2026/10/17 02:15:33 [INFO] [offbox] retention skipped (after-run): no clean-up window now (not due (last window 2026-10-12T02:15:09Z)) — nothing deleted (decision 68) +2026/10/17 02:15:36 [INFO] [offbox] backup OK: 5 app(s) backed up, 25 snapshot(s), 33s +=== root@192.168.0.154 +2026/10/17 02:17:03 [INFO] [offbox] backed up privatebin (/mnt/sys_drive/felhom-data/backups/primary/privatebin, 0 mandatory path(s)) +2026/10/17 02:17:05 [INFO] [offbox] backed up prowlarr (/mnt/sys_drive/felhom-data/backups/primary/prowlarr, 0 mandatory path(s)) +2026/10/17 02:17:08 [INFO] [offbox] backed up qbittorrent (/mnt/felhom-drives/media_a/backups/primary/qbittorrent, 0 mandatory path(s)) +2026/10/17 02:17:10 [INFO] [offbox] backed up sonarr (/mnt/felhom-drives/media_a/backups/primary/sonarr, 0 mandatory path(s)) +2026/10/17 02:17:12 [INFO] [offbox] retention skipped (after-run): no clean-up window now (not due (last window 2026-10-12T02:15:49Z)) — nothing deleted (decision 68) +2026/10/17 02:17:16 [INFO] [offbox] backup OK: 10 app(s) backed up, 60 snapshot(s), 2m13s diff --git a/documentation/backlog/CLOSED-ITEMS.md b/documentation/backlog/CLOSED-ITEMS.md index 1a02967d..f307e9f9 100644 --- a/documentation/backlog/CLOSED-ITEMS.md +++ b/documentation/backlog/CLOSED-ITEMS.md @@ -30,6 +30,8 @@ | Row | What | Closed | Evidence | |---|---|---|---| +| **R-1105** | **The controller runtime is still Debian 12, because Debian 13 moves the off-site engine from restic 0.14.0 to 0.18.0.** (P3) | CLOSED 2026-10-17 — the controller runs Debian 13 with restic pinned outside the OS (v0.323.0: 0.18.1; **v0.324.0: 0.19.1**, operator 2A). Every off-site assumption re-proven with REAL outputs of 0.14.0, 0.18.1, 0.19.1 (`TestR1105_*`); the one change (the forget plan keeps each group's oldest snapshot while monthly slots are unused) only keeps more. Both directions byte-identical on demo-hp's real store; prune proven on a scratch store. Night 2026-10-17: all three test boxes `backup OK` on 0.324.0 (02:15–02:18 UTC); **global floor 0.324.0 / MinAgent 0.158.1 at 02:46 UTC**, read back from the hub DB (with its WAL) and the hub's page; per-box floors cleared. One check left in STATUS: the old restic reads demo-hp's store after its first real 0.19.1 prune (window after 2026-10-18 14:16 UTC). `07` §6.8. | `audits/night-restore-2026-10-17/`, `audits/debian13-restic-2026-10-16/` | +| **R-1107** | **The pinned restic 0.18.1 official release binary carries 50 fixable security findings (2 critical, 48 high): it is built with Go 1.25.1 (25 standard-library findings) and old modules (x/crypto v0.41.0, x/net v0.43.0, grpc v1.74.2, otel v1.36.0 — 25).** (P3) | CLOSED 2026-10-17 — operator decision 2A: **restic 0.19.1** in controller v0.324.0 (official binary, signed sums, both per-arch sums pinned; a wrong sum fails the build — proved). Its notes read: one change (a missing top-level path exits 3) — reaches only a race, now loud. Scanner: restic's part 50 fixable → 19; image critical 3 (2) → 1 (0), high 122 (52) → 93 (23). On every box with the global floor 0.324.0 (02:46 UTC). | `audits/night-restore-2026-10-17/`, `audits/debian13-restic-2026-10-16/` | | **R-1065** | **After the Plex claim the box does not set Plex's libraries until the next wiring run.** (P3) | CLOSED 2026-10-17 — **controller v0.324.0**: a successful Plex claim schedules the media wiring (`RunMediaWiringSoon`, both claim routes). `TestPlexClaim_TriggersMediaWiring` (claimed / refused / never claimed), red-proved. Not seen live: no claim code tonight; the N100's next claim is the live observation. | `audits/night-restore-2026-10-17/` | | **R-926** | **After a household removes an `after_install` app keeping its backups and then restores it, nobody has checked whether the app page still shows a password that works.** (P3) | CLOSED 2026-10-17 — answered from the code and fixed where it was wrong. After a unit restore that had to GENERATE a `type: password` login, the page never shows it and says to use the old password (R-694's `RestoredLogins`) — the household's own password works (measured for grocy 2026-10-10). The path that was wrong: an off-site restore over a reinstall kept the reinstall's admin login as „the login”; since **controller v0.324.0** `SetStackSecretValues` records those as restored-login fields too (`TestR1106_SetStackSecretValues`, red-proved). | `audits/night-restore-2026-10-17/` | | **R-1106** | **An app with a database that is removed, reinstalled and then fully restored from the off-site copy comes back unusable: the restore puts back the OLD database volume, which carries the old install's database password, while the reinstall generated a new one.** (P2) | CLOSED 2026-10-17 — **controller v0.324.0** (operator decision 1A): the database password comes back with the data. The off-site full restore over a reinstall writes the snapshot's password before the database starts (only when the snapshot replays the DB volume); the shared unit-restore env refuses, before any change, when neither the unit nor an install OLDER than the data holds it; O4 never generates a DB password. **Live on demo-hp 9201** (rc.1, the dashboard's posts): Rallly (Postgres) and Grimmory (MariaDB) seeded, removed, reinstalled, restored — off-site path: marker row + books file back, both apps healthy, the app's and the dump user's DB logins work (last night: crash loop and held); on-box path (Rallly) the same; the refusal (Rallly unit without its password) refused with the app untouched; „use my kept data” (Grimmory) the same. Tests `TestR1106_*` red-proved; catalog gate `db-credentials`. `07` §7.4. | `audits/night-restore-2026-10-17/` | diff --git a/documentation/backlog/OPEN-ITEMS.md b/documentation/backlog/OPEN-ITEMS.md index 792b051c..419b8a95 100644 --- a/documentation/backlog/OPEN-ITEMS.md +++ b/documentation/backlog/OPEN-ITEMS.md @@ -237,12 +237,10 @@ stopping line that lies. | **R-1050** | Security & access | P4 | **Raise HSTS to one year on or after 2026-11-15, if no https trouble was reported.** `09` §3 decision 249 (D2 = A, operator 2026-10-15): HSTS stays `max-age=86400` now; after a month with no https trouble it becomes one year (`31536000`); never preload, never `includeSubDomains`. The value is `HSTSMaxAgeSeconds` in `felhom-controller/controller/internal/infra/infra.go`, pinned by `TestRenderForwardedHeaders_HTTPSOnly`. „Trouble” = any report of a name that will not open, or a box answering http after the floor. | **WATCHING** — filed 2026-10-15 | the date | On or after 2026-11-15: read the register and the operator's mail for https trouble since 2026-10-15; none → a controller release with one year (test updated), bake → vouch → floor | CC | | **R-1054** | Security & access | P3 | **The hub's operator/global API key is still one bearer that can do everything on `/api/v1`, including reading every box's break-glass root password (`GET /admin/hosts/{id}/recovery-credential`).** Hub v0.152.0 gave the web surface scoped tokens (`05` §17) but tokens are refused on `/api/v1` by design. Who uses the global key today is not listed in one place. Fix: list its consumers; move each operator use to a scoped token on the web side (or a token class on the API), and keep the global key only where a box path needs it — or retire it. | OPEN — filed 2026-10-15 by the token build | — | List the global key's consumers (pod env, scripts, runbooks) | CC | -## Box system & updates — 14 rows (P2 1, P3 9, P4 4) +## Box system & updates — 12 rows (P2 1, P3 7, P4 4) | ID | Category | Sev | What | State | Blocked on | Next action | Owner | |---|---|---|---|---|---|---|---| -| **R-1105** | Box system & updates | P3 | **The controller runtime is still Debian 12, because Debian 13 moves the off-site engine from restic 0.14.0 to 0.18.0.** The off-site code's measured behaviour is restic 0.14.0's: a missing source path is only a warning (`offbox_capture.go`), the `--json` progress shape (`offbox_progress.go`), the forget day-buckets (`offbox_window.go`), restore without a lock (`offbox_proof.go`). Debian 12's restic binary is built with go1.19.8. Measured 2026-10-16 on demo-hp: one off-site backup with 0.18.0 (backup OK, 172 snapshots), then 0.14.0 on the same store (backup OK, 186) — so the store stays readable both ways; the rest is NOT measured. | **NARROWED** 2026-10-16 (evening): **re-proven and released to the three test boxes** — controller v0.323.0: Debian 13.7, restic 0.18.1 pinned outside the OS (official binary, signature + two sums checked at build). Every assumption re-proven with REAL outputs of both versions (`testdata/restic/`, `TestR1105_*`); the one that moved is the forget plan (0.17+ keeps the oldest snapshot while monthly slots are unused — only ever more kept). Both restore directions byte-identical on demo-hp's real store; the drill's row and file came back. Tester 1, demo-hp, demo-felhom on 0.323.0 at 19:25 UTC by per-box floors, box walk green. `07` §6.8. Evidence `audits/debian13-restic-2026-10-16/` | each test box's first real night on 0.323.0 (≈02:15 UTC 2026-10-17); demo-hp's next clean-up window (after 2026-10-18 14:16 UTC) | Read each box's `offbox.last_success` after 2026-10-17T00:00Z with `last_status` ok (the command is in `STATUS.md`); then the global floor 0.323.0 / MinAgent 0.158.1 and clear the three per-box floors; close when 0.14.0 (image 0.322.0) still lists and restores demo-hp's store after the first 0.18.1 prune there (`tools/rx.sh`) | CC | -| **R-1107** | Box system & updates | P3 | **The pinned restic 0.18.1 official release binary carries 50 fixable security findings (2 critical, 48 high): it is built with Go 1.25.1 (25 standard-library findings) and old modules (x/crypto v0.41.0, x/net v0.43.0, grpc v1.74.2, otel v1.36.0 — 25).** Measured 2026-10-16, trivy 0.74.0 on controller 0.323.0-rc.1: they are most of the image's fixable highs (52 of 122). Debian 12's restic 0.14.0 was not flagged by the scanner (a Debian package, judged by Debian's tracker). Options measured the same day: restic **0.19.1** official (Go 1.26.4) — 0 critical / 19 high; 0.18.1 built from its signed source on our Go 1.27.2 — about 25 (the module half stays). The re-proof is now cheap: capture the new version's outputs into `testdata/restic//` (`audits/debian13-restic-2026-10-16/tools/fixtures.sh`) and run `TestR1105_*`. | WAITING-ON-OPERATOR — filed 2026-10-16 | the operator: stay on 0.18.1 (the brief's choice), or move to 0.19.x (a second version step, with the same re-proof) | On the operator's word: pin 0.19.x, capture its fixtures, re-run the R-1105 suite and the demo-hp drill | CC | | **R-984** | Box system & updates | P4 | **Host and agent hardening, low risk each: the backup-target route has no role check; the unsigned guest spec has no size bounds; signed ops have no maximum validity; the guarded mkfs does not refuse LVs, zvols or RAID devices; the local API is reachable from the LAN by a static route; a sudoers inspect pattern is wider than needed; install passes secrets on the command line; guest settings files are world-readable; the host offers rpcbind and DNS on the LAN.** Fix each in its own small change. Security review 2026-10-12 (`audits/security-review-2026-10-12/`); detail held off-git for the operator (`/mnt/5_hdd/felhom.eu/evidence/security-review-2026-10-12-PRIVATE/`). | OPEN | — | Batch into hardening work | CC | | **R-982** | Box system & updates | P3 | **The Proxmox host accepts root password login over SSH from the LAN (measured on demo-hp), so anyone on the household network can try the root password.** Design-shaped: the break-glass password exists for the console. Fix: key-only SSH for root; the password stays for the console. Evidence: `audits/security-review-2026-10-12/evidence/demo-hp-host-reads.txt`. | OPEN — design-proposal | — | Decide; then set it in the installer and the config bundle | CC | | **R-979** | Box system & updates | P3 | **Controller updates are trusted by registry tag alone (no digest, no signature), and the Update button offers any newer tag, even an unvouched one; the built-in traefik, cloudflared and FileBrowser images are also pinned by tag only.** Whoever can push to the registry decides what runs. Fix: pin by digest from the hub's vouched list; offer only vouched versions. Security review 2026-10-12 (`audits/security-review-2026-10-12/`); detail held off-git for the operator (`/mnt/5_hdd/felhom.eu/evidence/security-review-2026-10-12-PRIVATE/`). | OPEN — design-proposal | — | Design note, then build | CC |