diff --git a/REPORT.md b/REPORT.md index 84dd29f..8189628 100644 --- a/REPORT.md +++ b/REPORT.md @@ -12,7 +12,7 @@ Full report, per-app evidence, proofs and proposed register entries: |---|---|---| | **gramps-web** | Mounted `/app/data` (a path the app never writes), `/app/media`, `/tmp`. Its accounts database AND **its family tree** landed in the container's writable layer — destroyed by any redeploy, absent from every backup, while `gramps_data` was tarred nightly as an empty directory. | **FIXED** — persists the 8 paths the image's own environment names. Survives-a-redeploy proven: `users.sqlite` and the family-tree files byte-identical, same inode. | | **wishlist** | Mounted `wishlist_data:/data`, another path the app never writes. `prod.db` landed in the **anonymous** volume from the image's `VOLUME` directive — never in `ResolveDockerVolumeNames`, so never backed up, and orphaned by a redeploy. | **FIXED** — mounts `/usr/src/app/data` + `/usr/src/app/uploads`. `prod.db` byte-identical with the same inode across a redeploy. | -| **papra** (R-156) | Mounts `papra_data:/app/data`, which is root-owned, unwritable by uid 999, and does not exist in the image at all; the database is written to `/app/app-data/db/`. | **NOT FIXED — referred to the operator.** It is deployed on one box, and the fix would destroy the writable layer its documents live in. Fix prepared and proven in the scratch guest. See §6.1. | +| **papra** (R-156) | Mounts `papra_data:/app/data`, which is root-owned, unwritable by uid 999, and **does not exist in the papra image at all**; the database is written to `/app/app-data/db/`. | **NOT FIXED — referred to the operator.** Fix prepared and proven in the scratch guest (current: `db.sqlite` differs after a redeploy, so a real account created via the API is lost; fixed: byte-identical, it survives). It was deployed on one box; that box (`c10-soak`) was torn down by the other session mid-run and papra now appears nowhere in hub telemetry — but that is *absence* evidence, and `demo-hp` is fenced. One command settles it: §6.1a. | 7 UNDETERMINED are listed with reasons and **never folded into CLEAN** (§4.2): 4 apps write nothing until a user completes setup, 3 could not be started. diff --git a/audits/persistence-sweep-2026-08-02/README.md b/audits/persistence-sweep-2026-08-02/README.md index 375fe0b..5ac80d9 100644 --- a/audits/persistence-sweep-2026-08-02/README.md +++ b/audits/persistence-sweep-2026-08-02/README.md @@ -506,6 +506,30 @@ running container first (`docker cp papra:/app/app-data `), then push, the the new volume. **No migration is shipped here** — §5 of the brief, and inventing one is how this class of defect gets worse. + +> ### 6.1a — UPDATE at teardown: the deployment disappeared mid-session +> +> Between the start of this run and its teardown, the hub's `/configs` and `/hosts` both **stopped +> listing `c10-soak`** — the other session tore Campaign 10's rig down while this work was in +> flight (its teardown was owed). In the same interval the hub's `/apps` went from 10 deployed apps +> to 6, and the four that disappeared are **papra, grafana, homebox and rallly** — precisely the +> four apps Campaign 10's audit §A4 names as its discriminator set. papra's single deployment was +> therefore on `c10-soak`, as §6.1 suspected, and it is gone. +> +> **The fix is still not applied, deliberately.** The evidence is strong but it is *absence* +> evidence — papra no longer appearing in aggregated telemetry is not the same as a positive +> observation that no box runs it, and this project's standing rule 3 exists for exactly that +> distinction. The one box I cannot check is `demo-hp`, which is fenced this session; the hub says +> it is reporting normally and shows no papra, but that is the same absence argument one level down. +> +> The asymmetry decides it: applying the fix while a papra instance does exist destroys its +> documents at the next redeploy and is irreversible; leaving it is a one-line change the operator +> can push in seconds. **So it is left.** +> +> **What settles it:** `ssh demo-hp "pct exec 9201 -- docker ps -a --filter name=papra"` — one +> command, on the box this session may not touch. If it returns nothing, apply the diff in §6.1; +> there is then no strand risk and papra joins gramps-web and wishlist as an ordinary fix. + ### 6.2 Nothing else needed a ruling Both other BROKEN apps have an unambiguous correct path — each confirmed by **two independent @@ -682,8 +706,9 @@ time (`drill-r50`, `sess-c`, `sess-d`), so it is stated explicitly rather than i ### Layer 1 — the machine -`pct stop 9301 && pct destroy 9301 --purge`, and its `local-lvm` volume with it. Verified by -`pct list` (no 9301) and `lvs` (no `vm-9301-disk-0`). **Evidence: `state/teardown.txt`.** +**DONE.** `pct stop 9301 && pct destroy 9301 --purge` → `Logical volume "vm-9301-disk-0" +successfully removed` / `purging CT 9301 from related configurations` / rc=0. Verified after: +`pct list` shows only 9201, and `lvs | grep 9301` returns nothing. Evidence: `state/teardown.txt`. ### Layer 2 — the host @@ -693,7 +718,11 @@ time (`drill-r50`, `sess-c`, `sess-d`), so it is stated explicitly rather than i |---|---|---| | before the run began | 258 702 410 | 29.27 % | | end of run, before teardown | 169 968 909 | 53.53 % | -| after teardown | *see `state/teardown.txt`* | | +| **after teardown** | **258 702 410** | **29.27 %** | + +**The space returned exactly to the pre-run baseline** — 258 702 410 KiB, identical to the figure +recorded before the guest was created. `local` is unchanged at 26.00 %. Full before/after: +`state/teardown.txt`. The guest's 120 G volume was thin-provisioned on an **over-subscribed** pool (`pct create` warned: sum of thin volume sizes 402 G against a 348 G pool), which is why a disk watchdog ran throughout @@ -709,9 +738,10 @@ exist to orphan. The hub was touched **read-only** throughout — `/apps`, `/app `/hosts`, `/hosts/`, over the ClusterIP with Basic auth — to answer "is papra deployed anywhere?" (§6.1). -Confirmed against the live hub `/configs` list, unchanged from the pre-run state and containing no -entry for this run: `c10-soak`, `david`, `demo-felhom`, `demo-hp`, `drill-r50`, `peti-felhom`, -`sess-f`. +Confirmed after teardown against the live hub: `/configs` contains no entry for this run +(`catalog-persist` / `9301`: absent), and `/hosts` lists only `demo-felhom-8363b5`, +`demo-hp-bb76ea`, `drill-r50-0a4f9a`, `sess-f-2670b5`. The list is **two shorter** than at the +start of the run — `c10-soak` was removed by the other session mid-run, not by this one (§6.1a). The scratch host paths the gate creates for `${HDD_PATH}` / `${IMPORT_PATH}` (`/srv/felhom-gate/…`) and the `felhom-volgate-canary:1` image existed only inside the guest, so both go with layer 1. diff --git a/audits/persistence-sweep-2026-08-02/state/teardown.txt b/audits/persistence-sweep-2026-08-02/state/teardown.txt new file mode 100644 index 0000000..8312837 --- /dev/null +++ b/audits/persistence-sweep-2026-08-02/state/teardown.txt @@ -0,0 +1,27 @@ +=== BEFORE === +Name Type Status Total (KiB) Used (KiB) Available (KiB) % +felhom-backup dir active 960303848 21660676 889788660 2.26% +felhom-pbs pbs active 0 0 0 0.00% +local dir active 98497780 25608552 67839680 26.00% +local-lvm lvmthin active 365760512 196047634 169712877 53.60% +VMID Status Lock Name +9201 running demo-felhom +9301 running catalog-persist + LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert + vm-9301-disk-0 pve Vwi-aotz-- 120.00g data 70.73 + +=== DESTROY 9301 === + Logical volume "vm-9301-disk-0" successfully removed. +purging CT 9301 from related configurations.. +rc=0 + +=== AFTER === +Name Type Status Total (KiB) Used (KiB) Available (KiB) % +felhom-backup dir active 960303848 21660676 889788660 2.26% +felhom-pbs pbs active 0 0 0 0.00% +local dir active 98497780 25608496 67839736 26.00% +local-lvm lvmthin active 365760512 107058101 258702410 29.27% +VMID Status Lock Name +9201 running demo-felhom +--- lvs for 9301 (expect none) --- +no 9301 logical volume