REPORT: the catalog_since backfill — 53 apps, six hand-checks, one known gate gap
gates / gates (push) Successful in 1s
gates / gates (push) Successful in 1s
This commit is contained in:
@@ -1,70 +1,56 @@
|
||||
# REPORT — papra volume persistence (R-156, last leg)
|
||||
# REPORT — `catalog_since` backfill (2026-09-02)
|
||||
|
||||
**Date:** 2026-08-03 · **Repo:** `app-catalog-felhom.eu` · **No version** (catalog templates are unversioned)
|
||||
*Overwritten each run. This records the most recent implementation only.*
|
||||
|
||||
## What changed
|
||||
## What was done
|
||||
|
||||
`templates/papra/docker-compose.yml` — the volume mount moved from `/app/data` to `/app/app-data`.
|
||||
One line, plus a Hungarian comment recording why, so the next reader does not "fix" it back.
|
||||
One new optional key, `catalog_since`, in all **53** `.felhom.yml` files, plus the rule that keeps it
|
||||
true in `CLAUDE.md`. **No `docker-compose.yml` was touched and no image pin moved.**
|
||||
|
||||
## Why
|
||||
This is slice 2 of the update arc opened by
|
||||
`felhom.eu/documentation/audits/SPIKE-app-update-2026-09-01.md`. The consumer is felhom-controller
|
||||
**v0.233.0**, which renders one Hungarian badge from it — *"Naprakész"* or
|
||||
*"Frissítés elérhető — N napja"*. **No version number reaches the customer**, by operator ruling.
|
||||
|
||||
papra mounted `papra_data:/app/data` while the application writes to `/app/app-data`. Its database
|
||||
therefore lived 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.
|
||||
## Baseline
|
||||
|
||||
## Precondition — checked, not inherited
|
||||
|
||||
The register's "deployed nowhere" evidence was from 2026-08-02 and covered one guest; both demo boxes
|
||||
were wiped and rebuilt on 2026-08-03, so it was re-measured three ways:
|
||||
|
||||
- `docker ps -a` (**including stopped containers**) on demo-hp guest 9201 → no papra
|
||||
- `docker ps -a` on demo-felhom guest 9201 → no papra
|
||||
- hub `/hosts` fleet view → exactly two enrolled hosts (`demo-felhom-8363b5`, `demo-hp-bb76ea`), **zero** papra references
|
||||
|
||||
Deployed nowhere ⇒ the template fix strands no live data.
|
||||
|
||||
## How the fix was chosen
|
||||
|
||||
From the **image**, not the README:
|
||||
|
||||
```
|
||||
WORKDIR=/app
|
||||
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 — the old mount pointed at a path nothing could
|
||||
ever write.
|
||||
|
||||
**Reconfiguring the app to write to `/app/data` was available and was deliberately not taken.** All
|
||||
three paths are env-settable, so option (1) of the task's preference order was open. It enumerates
|
||||
data paths: a fourth added upstream would escape to the writable layer again, silently — this exact
|
||||
defect, re-armed and invisible. Mounting the app's own data ROOT captures every current and future
|
||||
path by construction, in one line rather than three env vars coupled to upstream.
|
||||
|
||||
## Proof — the runtime gate, in both directions
|
||||
|
||||
| Run | Verdict |
|
||||
| | |
|
||||
|---|---|
|
||||
| `check-volume-persistence.py papra` (fixed) | **CLEAN**, self-test passed: *"prober flags the R-156 signature and clears a correct template — trustworthy"* |
|
||||
| same gate, mount reverted to `/app/data` (red-proof) | **BROKEN** — `mount /app/data is NOT writable by the app's own uid=999`; `DATA in the writable layer at /app/app-data/db (db_signature=True, e.g. ['db.sqlite'])`; `declared volume /app/data is EMPTY` |
|
||||
| `catalog_gates.py papra` (full, not `--fast`) | **rc=0** — image-pins OK (53 templates, 0 unpinned) · image-resolvable OK · volume-persistence OK |
|
||||
| repo at start | `5d8f25f61189` — matched the task's stated baseline, had not moved |
|
||||
| commit pushed | `69761cf91bfc` |
|
||||
|
||||
The red-proof was run against the **real template**, not only the built-in canary, so the gate is
|
||||
shown to discriminate on the artifact actually being shipped.
|
||||
## How the dates were derived
|
||||
|
||||
## Two operational notes for the next run of that gate
|
||||
A throwaway script walked each app's `templates/<app>/docker-compose.yml` history newest→oldest and
|
||||
took the newest commit whose **set of `image:` values differs from its parent's**. A YAML parse, not a
|
||||
line scan.
|
||||
|
||||
- **It needs root.** It reads volume contents under `/var/lib/docker/volumes` (mode `drwx--x---`). As
|
||||
a normal user its own canary self-test fails UNDETERMINED and it correctly refuses to report — the
|
||||
fail-closed behaviour worked exactly as designed.
|
||||
- **Scope it to the app you touched.** Unscoped it deploys all 53 templates; that run exceeded ten
|
||||
minutes and was aborted. Its scratch containers were cleaned up afterwards (`volgate-*` projects).
|
||||
- It hardcodes a scratch path `/srv/felhom-gate`, which had to be created on DooPlex.
|
||||
**Two commits excluded by hash:** `214d448` and `30bd892` — the bentopdf pin move and its same-hour
|
||||
revert. This repo's own CHANGELOG records them as a spike measurement, not a release; counting them
|
||||
would date bentopdf 2026-09-02 for a pin that has not moved since `71828a8` (2026-07-12).
|
||||
|
||||
## Teardown
|
||||
**Verification.** The four multi-major anchors from the spike all reproduced exactly — nextcloud
|
||||
`5e2c1ae`, grafana `b789acc`, calcom `147cee7`, vikunja `3fa63cd`, all **2026-07-18**. Six further
|
||||
apps were re-checked against `git log` **by hand**: bentopdf, plex, crafty-controller, homebox,
|
||||
bookstack, wanderer. All six correct.
|
||||
|
||||
`volgate-*` scratch compose projects removed with their volumes. The pre-existing `jarr-*` containers
|
||||
(9 days old, unrelated) were left untouched.
|
||||
Range: **2026-02-15** (plex, still on the pin it was added with) to **2026-07-21**. Thirty-eight of 53
|
||||
sit on 2026-07-18, the catalog-wide bump.
|
||||
|
||||
Every file was re-parsed with `yaml.safe_load` after the edit and the key read back — 53/53 clean.
|
||||
|
||||
## Gates
|
||||
|
||||
| gate | result |
|
||||
|---|---|
|
||||
| `image-pins` | **OK** (exit 0) |
|
||||
| `image-resolvable` | INCONCLUSIVE — Docker Hub throttled 6 of 65 unauthenticated manifest lookups. 59 verified, none dead. **Not caused by this change.** |
|
||||
| `volume-persistence` | INCONCLUSIVE — the prober refused to report because its own canary came back UNDETERMINED on this host. **Not caused by this change** (it needs a scratch Docker host). |
|
||||
|
||||
Entry point: `python3 scripts/catalog_gates.py`, overall exit **0**.
|
||||
|
||||
## Known gap, filed rather than left implicit
|
||||
|
||||
There is **no gate** asserting that a commit which changes an `image:` line also moves that app's
|
||||
`catalog_since`. The gates runner fetches at `--depth 1` and has no parent commit to diff against, so
|
||||
the gate needs a deeper fetch. Filed as a row in `felhom.eu/documentation/backlog/OPEN-ITEMS.md`.
|
||||
|
||||
Reference in New Issue
Block a user