# REPORT — the controller on Debian 13 with a pinned restic, re-proven; the Samba image rebuilt (2026-10-16) Written in ASD-STE100 Simplified Technical English. Operator brief „the controller on Debian 13 with a pinned restic, re-proven; the Samba image rebuilt". Design read first: `architecture/07-backup-architecture.md` (Tier 3, §6.3, §6.7 the failure classifier, §3 the restore table; now §6.8), `06-offsite-connectivity.md`, `03-host-agent.md` (escrow), `_recovery-inventory-2026-07-28.md`. Evidence: `documentation/audits/debian13-restic-2026-10-16/`. ## 0. Decisions I took (inside the brief; reversible) 1. **I removed seven packages, not two.** The brief named the two database clients. I read every program call in the controller: it never runs `sqlite3`, `fdisk`, `e2fsprogs`, `parted` (disk work is the agent's since v0.37.0) or `sshpass` (its key install was replaced by the hub's registrar in v0.289.0) either. On demo-hp every kept tool works, every removed one is gone, and the log has no „executable not found". To undo: put the names back in the apt list. 2. **I kept restic 0.18.1, as the brief says, although it adds scanner findings** (§4). The choice of 0.19.x is yours (R-1107). ## 1. The assumption table (Part 1) and each row on restic 0.18.1 Measured on scratch stores with both official binaries side by side (`part1/`), then pinned as test fixtures of REAL outputs (`felhom-controller` `internal/backup/testdata/restic/`, `TestR1105_*`, red-proved by six mutations). | restic call · where | What the code assumes | 0.18.1 | |---|---|---| | `backup` (+ missing path) · `offbox.go`, `offbox_capture.go` | a missing source path: a warning, exit 0; an unreadable file: exit 3 | same | | `backup --json` · `offbox_progress.go` | status lines: `percent_done`, totals, `current_files` | same names (live bar moved on demo-hp) | | `snapshots --json`, `snapshots latest --tag` · `offbox.go`, `offbox_inventory.go`, `offbox_restore.go`, `offbox_window.go` | one JSON array, nothing else in the combined output (also with an empty cache) | same (new fields only) | | `stats `, `stats --mode raw-data` · `offbox_restore.go`, `offbox.go` | `total_size` | same | | `forget … --dry-run --json` · `offbox_window.go` | the plan of the 7/4/6 policy, `--group-by host,tags` | **CHANGED, safe direction:** 0.17+ also keeps each group's oldest snapshot while monthly slots are unused — fewer removals, never more (41-day set 60 vs 62; 230-day set identical, both time zones). The guard's verdict holds. Pinned. No code change needed. | | `restore … --include --no-lock` · `offbox_proof.go` | writes nothing to the store | same (restores from a read-only store) | | `restore` (plain) · `offbox_restore.go`, `offbox.go`, `shares_restore.go` | the layout under the target | same. The old comment „restore takes no lock" was wrong for both versions; corrected | | `cat config`, `init` · `offbox.go` probe | „wrong password…" = orphaned; „unable to open config file" = no repo; „already exists" = init race is success | same | | `unlock`, `unlock --remove-all` · `resticStep` | lock text „repository is already locked"; stale-only unlock keeps a foreign lock | same (both lock forms) | | `check` · `offbox_integrity.go` | a damaged pack reads as damage, a healthy run does not | same verdict (other wording) | | the §6.7 classifier | refused port, refused key, unreachable host, dead rclone ssh → `transport` | same | | agent: escrow, recovery, DR recipe | — | **the agent runs no restic**; it only stores and returns the password | Exit codes changed (10, 11, 12); the controller reads none. ## 2. The version and the pin - **restic 0.18.1** (newest 0.18 patch). The hosts run **no restic** (demo-hp, demo-felhom, Tester 1: not installed); every guest ran Debian's 0.14.0. So „one version for guest and host" means the guest only. - **The pin:** a `restic` build stage downloads the official release file; restic's `SHA256SUMS` signature is checked with the vendored release key (fingerprint CF8F 18F2 8445 7597 3F79 D4E1 91A6 868B D3F7 A907, cross-checked against restic's own install doc); the file must match the published sum AND the sum pinned in the Dockerfile. A wrong sum fails the build (proved). `TestR1105_ResticIsPinnedOutsideTheOS`, red-proved by four mutations. ## 3. Both directions and the drill (demo-hp 9201, rc.1, the dashboard's own posts) - **0.18.1 restores a 0.14.0 snapshot:** the product's off-site restore of bookstack → a tree byte-identical to a 0.14.0 restore of the same snapshot. **0.14.0 restores a 0.18.1 snapshot:** byte-identical too. Both on demo-hp's REAL store. - **0.14.0 after a 0.18.1 prune:** proven on a scratch store (502 snapshots; `check --read-data` clean, both restores). On the real store it waits for demo-hp's next weekly clean-up window (due after 2026-10-18 14:16 UTC). In STATUS. - **The drill:** Rallly (database) and Grimmory (books folder): a marker row and a marker file; the off-site run (dumps + push, 3m33s, 186 → 202 snapshots); removed with data and backups; reinstalled (nothing there — the negative control); full restore from the off-site copy. **The row and the file came back** (file sha256 identical). **But both apps were then unusable — not because of restic:** the restore puts back the OLD database volume with the old password, and the reinstall made a new one. Rallly crash-looped on auth while the restore said done; Grimmory was held stopped. Filed R-1106 (R-127's class, on the reinstall path). ## 4. Debian 13 and the scanner Kept: git, rsync, util-linux, openssh-client, curl, ca-certificates, gnupg, docker CLI + compose plugin. Removed: the seven in §0. trivy 0.74.0 (critical / high, fixable in brackets): | image | critical | high | |---|---|---| | controller 0.322.0 (Debian 12) | 4 (0) | 140 (4) | | controller 0.323.0 (Debian 13) | **3 (2)** | **122 (52)** | | — of which the OS | 1 (0) | 70 (0) | | — of which restic 0.18.1 (Go 1.25.1, old modules) | 2 (2) | 48 (48) | | felhom-samba 1.1.0 | 0 | 48 (48) | | felhom-samba 1.2.0 | **0** | **0** | The OS part fell by half. The official restic file is built with an old Go, so the fixable count rose. restic 0.19.1's official file: 0 / 19. R-1107. ## 5. Samba (R-972) felhom-samba 1.2.0: Alpine 3.24.2 (digest-pinned), same packages, Samba 4.23.8. `build-samba-image.sh` now refuses a tag already in the registry, a registry it cannot ask, and a dirty or unpushed tree (6 cases; 4 fail on the old script). Checked on demo-hp first on 1.1.0, then after the switch to 1.2.0, with the SAME household password (the passdb volume survived): smbclient from DooPlex wrote, listed and read back a file; a wrong password was refused; nmbd answered the name; mDNS answered `FELHOMHP.local`; WS-Discovery answered; smbd, nmbd, wsdd, avahi, dbus under tini; `testparm` clean. Found on the way: a share added before the first password is not exported (R-1108, pre-existing, narrow). ## 6. Release Built **0.323.0** from `8587198` (code identical to rc.1 `fbcc3cf`; only CHANGELOG and README differ), MinAgent 0.158.1. Per-box floors at 19:23:46 UTC (`303` → `floor_set`, all three); **Tester 1, demo-hp and demo-felhom on 0.323.0 at 19:25 UTC**, each through the normal update path (`Post-update startup: update successful`). Box walk: no container unhealthy, restarting, exited or created (24 / 9 / 17); restic 0.18.1 and Debian 13 in each controller; the three dashboards 200; https-only 35 names OK with both controls (`release/`). **The global floor is NOT raised tonight** (the brief). It waits until each test box has made one real night's off-site backup on 0.323.0 (they run at about 02:15 UTC). The exact check and the floor command are in `STATUS.md` („What we work on"). The real-store check „0.14.0 reads after a 0.18.1 prune" waits for demo-hp's clean-up window (after 2026-10-18 14:16 UTC). **CI:** green by job for every commit of this task — controller `fbcc3cf` (1963), `8587198` (1965), `00a0d91` (1967); felhom.eu `e713f6ae` (1964), `babf596c` (1966). ## 7. Rows **Before 174, after 177. Opened 3** (R-1106, R-1107, R-1108). **Closed 0.** R-1105 and R-972 NARROWED (R-1105 waits on the night check; R-972's Samba part is done and rides the floor). `unproven.py`: NOT WALKED 35 of 55, unchanged. ## 8. Observations 1. The forget plan of restic 0.17+ keeps each group's oldest snapshot while monthly slots are unused. NOT-A-FINDING: it only ever keeps more, the guard's verdict holds; pinned by `TestR1105_TheForgetPlanOnlyEverKeepsMore` and written into `07` §6.8. 2. A reinstalled database app restored from the off-site copy is unusable (old volume, new password). FILED: R-1106 3. The official restic 0.18.1 binary adds 50 fixable scanner findings. FILED: R-1107 4. A share added before the first household password is not exported. FILED: R-1108 5. A remove „with drive data" keeps `userdata/media/grimmory`. NOT-A-FINDING: by design (`07` §6.5, decision 67 — the household's folders stay). 6. A restore hold outlives the remove and a fresh install. NOT-A-FINDING: by design (R-379 — only the operator clears it; `ClearUpdateHold`'s comment). The fresh install over it runs; the hold sentence stays on its page until cleared — part of R-1106's chain. 7. The `-clear-restore-hold` tool's change is overwritten if the controller is not restarted at once. NOT-A-FINDING: the tool prints „NOW RESTART THE CONTROLLER"; done that way it worked. 8. A plain `restic restore` writes a lock (both versions); an old code comment said it did not. NOT-A-FINDING: the comment is corrected; the nightly proof uses `--no-lock` and was never affected. 9. `build.sh --push` also pushes `:latest` for an rc tag. NOT-A-FINDING: nothing pulls `:latest` (the update button reads X.Y.Z tags; boxes run a named tag). ## 9. Teardown (three layers) - **Machine (demo-hp 9201):** Rallly and Grimmory removed through the product (no container, volume, unit or folder left; the Grimmory restore hold cleared with the operator tool and a restart). The check copies are gone. Sharing turned off, both test shares deleted, their empty folders removed; the passdb volume and `user_set` stay, as the product leaves them. The helper scripts and scratch restores in the guest removed. 9201 runs 0.323.0. - **Host:** nothing. - **Hub:** per-box floors (see §6).