papra: mount the volume where the app actually writes (R-156, last leg)
gates / gates (push) Successful in 1s
gates / gates (push) Successful in 1s
papra mounted papra_data:/app/data while the application writes to /app/app-data, so its database sat in the container's writable layer: lost on redeploy, and tarred nightly as an empty directory while the healthcheck stayed green. Last of the three apps R-156 convicted. Decided from the IMAGE, not the README. docker inspect of ghcr.io/papra-hq/papra:26.6.1-rootless gives WORKDIR=/app and all three data paths under ./app-data (DATABASE_URL, DOCUMENT_STORAGE_FILESYSTEM_ROOT, PAPRA_CONFIG_DIR) — and /app/data does not exist in the image at all. Reconfiguring the app to write to /app/data was available and deliberately not taken: it enumerates data paths, so a fourth added upstream would escape to the writable layer again, silently — this defect re-armed. Mounting the app's own data ROOT captures every current and future path by construction. Precondition checked rather than inherited: docker ps -a (including stopped) on BOTH demo guests, plus the hub fleet view (two enrolled hosts, zero papra) — both boxes were wiped and rebuilt today, so the 2 August evidence was re-measured. Proven by the runtime gate in both directions: CLEAN with the self-test passing, and BROKEN when the mount is reverted, with the exact R-156 evidence. Full catalog_gates.py papra: all three gates OK.
This commit is contained in:
@@ -1,5 +1,35 @@
|
||||
# Changelog
|
||||
|
||||
### papra — the volume is mounted where the app actually writes (2026-08-03, R-156, last leg)
|
||||
|
||||
**The third and last of the three apps that kept their data where backups never looked.** papra
|
||||
mounted `papra_data:/app/data` while the application writes to `/app/app-data`, so its database sat
|
||||
in the container's **writable layer**: lost on redeploy, and tarred nightly as an empty directory
|
||||
while the healthcheck stayed green.
|
||||
|
||||
**Decided from the IMAGE, not the README.** `docker inspect ghcr.io/papra-hq/papra:26.6.1-rootless`
|
||||
gives `WORKDIR=/app` and all three data paths under `./app-data` — `DATABASE_URL=file:./app-data/db/db.sqlite`,
|
||||
`DOCUMENT_STORAGE_FILESYSTEM_ROOT=./app-data/documents`, `PAPRA_CONFIG_DIR=./app-data` — and
|
||||
**`/app/data` does not exist in the image at all**.
|
||||
|
||||
**Why the mount moved rather than the app being reconfigured.** Pointing all three env vars at
|
||||
`/app/data` would have worked, but it enumerates data paths: a fourth one added upstream escapes to
|
||||
the writable layer again, silently, which is this defect re-armed. Mounting the app's own data ROOT
|
||||
captures every current and future path by construction.
|
||||
|
||||
**Precondition checked, not inherited:** papra is deployed nowhere — `docker ps -a` (including
|
||||
stopped) on both demo guests, plus the hub fleet view showing two enrolled hosts and zero papra
|
||||
references. Both boxes were wiped and rebuilt on 3 August, so the 2 August evidence was re-measured.
|
||||
|
||||
**Proven by the runtime gate, in both directions.** `check-volume-persistence.py papra` → **CLEAN**,
|
||||
with its self-test passing on the same run. Red-proof: reverting the mount to `/app/data` → **BROKEN**
|
||||
with the exact R-156 evidence (`DATA in the writable layer at /app/app-data/db`,
|
||||
`declared volume /app/data is EMPTY`). Full `catalog_gates.py papra`: all three gates OK.
|
||||
|
||||
**Note for the next run of that gate:** it needs **root** (it reads `/var/lib/docker/volumes`, mode
|
||||
`drwx--x---`; as a normal user its own canary fails UNDETERMINED and it correctly refuses a verdict),
|
||||
and it should be scoped to the app touched — unscoped it deploys all 53 templates.
|
||||
|
||||
## CI — the static catalog gate runs on every push (2026-08-02, R-168)
|
||||
|
||||
**No version bump, no build, no deploy** — this adds a workflow file only. Stated explicitly so the
|
||||
|
||||
Reference in New Issue
Block a user