catalog: re-pin wanderer to the current upstream shape, retire plant-it, add the resolvability gate
wanderer: ghcr.io/flomp/wanderer:0.16.0 is a ghost - upstream split the app into web+db images, moved registry and renamed the org. Restructured to upstream's own v0.20.0 compose (3 services, new /data/plugins volume, second public hostname for PocketBase, meilisearch pinned DOWN to upstream's v1.36.0 per the R-42 ruling). plant-it: retired. The repo name was wrong (plant-it-server) but upstream has DELETED self-hosting; last server image is 2024-12-10 and it needs MySQL+Redis the template never had. Moved to retired/ rather than deleted - reversible. R-41 slice 1: check-image-resolvable.py. Encodes two traps - manifest inspect exits 0 while printing toomanyrequests, and the inverse, where the first sweep called 24 of 65 pins dead because Hub throttled it. Ambiguity is INCONCLUSIVE, never an accusation.
This commit is contained in:
@@ -1,68 +1,87 @@
|
||||
# REPORT — CAMPAIGN 7: full app-catalog sweep (2026-07-18 → 2026-07-19)
|
||||
# REPORT — catalog honesty pass (TASK-E Part 3), 2026-07-21
|
||||
|
||||
**Scope:** every app under `templates/` — bump the pin, deploy it through the real pipeline on the
|
||||
demo box, prove it works, audit its healthcheck, record, remove.
|
||||
**Box:** demo guest 9201 (`demo-felhom` on `felhom-pve`), controller **0.146.0**.
|
||||
**Full matrix + ranked findings:**
|
||||
`felhom.eu/documentation/audits/CAMPAIGN-7-catalog-sweep-2026-07-19.md`
|
||||
## What ran
|
||||
|
||||
## Outcome
|
||||
Diagnose-and-re-pin for the two Campaign 7 apps whose images "do not resolve at all", plus R-41
|
||||
slice 1 (a standing resolvability gate). Baseline `34d50a3`, clean tree.
|
||||
|
||||
| | count |
|
||||
## wanderer — RE-PINNED (upstream is alive; the template pointed at a ghost)
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| apps attempted | **53 / 53** (full coverage, no remainder) |
|
||||
| passed end-to-end | **45** |
|
||||
| still failing | **4** (glance, gokapi, plant-it, wanderer) |
|
||||
| not automatable | **1** (plex — needs a real `PLEX_CLAIM` token) |
|
||||
| template fixes committed + live-re-validated | **13** |
|
||||
| broken healthchecks found and fixed | **7** |
|
||||
| apps that had NEVER been deployable, now fixed | **5** |
|
||||
| Old pin | `ghcr.io/flomp/wanderer:0.16.0` — **does not resolve** (verified `docker manifest inspect`) |
|
||||
| Failure class | **repo split + registry move + org rename**, all at once |
|
||||
| New pins | `flomp/wanderer-web:v0.20.0`, `flomp/wanderer-db:v0.20.0`, `getmeili/meilisearch:v1.36.0` |
|
||||
| Evidence | upstream `docker-compose.yml` at tag `v0.20.0` (github.com/open-wanderer/wanderer); latest release 2026-07-07; all three tags confirmed present |
|
||||
|
||||
## Pre-flight gate — PASSED
|
||||
Compose restructured from 1 service to 3 (web + PocketBase + Meilisearch), per upstream. Notable
|
||||
consequences, none of them cosmetic:
|
||||
|
||||
`artifact_golden_version` = 0.146.0 (sha `4834c703…e955`), `min_controller_version` = 0.146.0,
|
||||
guest running `felhom-controller:0.146.0` healthy and reporting. The sweep validated on the version
|
||||
customers will actually run.
|
||||
- **A second public hostname was unavoidable.** `PUBLIC_POCKETBASE_URL` is browser-side, so
|
||||
PocketBase must be publicly reachable; added `SUBDOMAIN_DB` (default `hike-db`). Upstream's own
|
||||
proxy example does the same.
|
||||
- **New required volume** `/data/plugins` — v0.20.0 moved the Strava/Komoot/Hammerhead integrations
|
||||
into a WASM plugin sandbox.
|
||||
- **New generated secret** `POCKETBASE_ENCRYPTION_KEY` (`hex:16` → exactly 32 chars, as required).
|
||||
- **Meilisearch pinned DOWN v1.49 → v1.36.0**, applying the R-42 ruling (sidecar follows the app
|
||||
template's proposed pin). Campaign 7 had bumped it while the app image was dead.
|
||||
- `mem_limit` 384M → 1024M = the exact sum of the three services (REUSE.md §2 rule).
|
||||
- Probe container name kept equal to the stack dir (`wanderer`), or the health probe would hit the DB.
|
||||
|
||||
## The headline finding
|
||||
**Not deployed live.** This is a from-scratch 3-service restructure of an app that has never once
|
||||
deployed successfully; it wants a real deploy on the demo box before anyone is invited to it.
|
||||
|
||||
**A lying healthcheck does not merely mislead — it takes the app off-line.** Traefik refuses to
|
||||
route to a container in `unhealthy` state, so a probe that cannot execute (missing `wget`/`curl`,
|
||||
wrong port, wrong path, HEAD-vs-GET) produces a permanent `unhealthy`, and the customer gets a
|
||||
**404 while the app is serving 200 perfectly well on its own port**. Seven apps were in that state.
|
||||
## plant-it — RETIRED (operator ruling 2026-07-21)
|
||||
|
||||
## Second finding
|
||||
The pin was only slightly wrong — repository is `msdeluise/plant-it-server`, and `0.10.0` was the
|
||||
right version. Correcting the name would have been the wrong fix:
|
||||
|
||||
**5 of 53 apps had never been deployable at all** — papra, zipline, wishlist, homebox, glance —
|
||||
and none of it was caused by this sweep; the sweep is simply the first thing that ever tried to
|
||||
deploy them. Four are fixed. The catalog has no standing "does every template still deploy?" check,
|
||||
and that absence is the real gap.
|
||||
- Upstream **discontinued self-hosting**: `backend/` and `deployment/` are deleted from `main`; the
|
||||
project is now an Android app on F-Droid/Obtainium, maintainer notes development has slowed.
|
||||
- Last server image pushed **2024-12-10** — a security-frozen Spring Boot 3.4.0.
|
||||
- Requires **MySQL 8.0 + Redis**. The template claimed "Database: None (file-based)" — never true.
|
||||
|
||||
## End-state (verified against the dashboard's own state, not just `docker ps`)
|
||||
Moved to `retired/plant-it/` rather than deleted: the controller discovers apps by directory, so this
|
||||
un-offers it while keeping it revivable with one `git mv`. Catalog is now **52 apps**.
|
||||
|
||||
- Deployed customer apps: **bookstack, calibre-web, immich** — the trio, freshly redeployed on
|
||||
current versions, all containers healthy, offsite toggles restored to their recorded
|
||||
pre-uninstall state (`offbox: true` on all three).
|
||||
- Protected stacks untouched and running: traefik, cloudflared, filebrowser, samba, controller.
|
||||
- Orphaned stacks: **none**. Stuck `deploying` flags: **none**.
|
||||
- `app.yaml` present for the trio only; every other campaign app removed via the real delete flow.
|
||||
- Image prune run. Docker volume: **179 GB free** (pre-campaign 178 GB).
|
||||
- No campaign residue in `/opt/docker/stacks/` beyond synced templates.
|
||||
## R-41 slice 1 — `scripts/check-image-resolvable.py`
|
||||
|
||||
## Needs a decision or a follow-up task
|
||||
Complements the syntactic pin gate, which cannot see rot. Resolves each unique pin one image at a
|
||||
time; exit 0 / 1 (GONE) / 2 (inconclusive). 14 fixture tests, no network.
|
||||
|
||||
1. **Multi-hop major upgrades** — nextcloud states "You cannot skip major releases"; the template
|
||||
moved 31 → 34. Fresh deploys are fine, but an existing customer's update button cannot express a
|
||||
staged upgrade. Systemic; ROADMAP.
|
||||
2. **DB/cache sidecar majors** — deliberately not bumped (postgres 16→18, redis 7→8, mariadb→12).
|
||||
Rationale in the campaign doc §4. Wants an explicit ruling.
|
||||
3. **gokapi v2 config migration** — parked on v1.9.6 until the seeded `config.json` is regenerated.
|
||||
Security-relevant: it should not sit on a superseded line indefinitely.
|
||||
4. **plant-it / wanderer** — images unresolvable; need upstream research (finding, not a deletion).
|
||||
5. **glance** — needs a seeded default `glance.yml`; it has never started from this catalog.
|
||||
**Two traps encoded, both hit live during this change:**
|
||||
|
||||
## Secrets
|
||||
1. `docker manifest inspect` prints `toomanyrequests: …` and **still exits 0** — the same
|
||||
exits-0-on-failure shape as the ISO tooling's `validate-answer`. stderr is therefore checked
|
||||
even on rc=0.
|
||||
2. The inverse, and the more dangerous one: the **first full sweep called 24 of 65 pins dead**,
|
||||
including `postgres:16-alpine` and `redis:7-alpine`, purely because Docker Hub throttled it
|
||||
partway through. Ambiguity now resolves to INCONCLUSIVE, never to an accusation.
|
||||
|
||||
No secret value was logged, printed, or written to evidence at any point. Generated deploy secrets
|
||||
are recorded as `<generated-secret, not recorded>`; the trio's credentials live in their `app.yaml`
|
||||
on the box, out-of-band as usual.
|
||||
### Sweep result — INCOMPLETE, and that is the honest answer
|
||||
|
||||
The full 65-pin sweep **could not be completed**: DooPlex is not logged in to Docker Hub, and the
|
||||
anonymous limit was exhausted partway. The pre-throttle probes are trustworthy and are what the
|
||||
findings above rest on:
|
||||
|
||||
| Image | Result | When |
|
||||
|---|---|---|
|
||||
| `ghcr.io/flomp/wanderer:0.16.0` | **GONE** | pre-throttle |
|
||||
| `msdeluise/plant-it:0.10.0` | **GONE** | pre-throttle |
|
||||
| `getmeili/meilisearch:v1.49` | resolves | pre-throttle |
|
||||
| `flomp/wanderer-web:latest`, `flomp/wanderer-db:latest` | resolve | pre-throttle |
|
||||
| `msdeluise/plant-it-backend/-frontend:latest` | resolve | pre-throttle |
|
||||
| `flomp/wanderer-{web,db}:v0.20.0`, `getmeili/meilisearch:v1.36.0`, `msdeluise/plant-it-server:0.10.0` | exist (Hub tag API, HTTP 200) | post-throttle cross-check |
|
||||
|
||||
**Follow-up for the operator:** re-run `python3 scripts/check-image-resolvable.py` after
|
||||
`docker login` to get the complete 52-app table. The gate now reports the throttle as INCONCLUSIVE
|
||||
(exit 2) instead of inventing 24 dead images, so a repeat is safe to trust.
|
||||
|
||||
## Observations (recorded, not acted on)
|
||||
|
||||
- `REUSE.md` cited `felhom-controller/internal/stacks/healthprobe.go`; the real path has an extra
|
||||
`controller/` segment. Corrected, but `reuse_refs_check.py` still flags it — it resolves citations
|
||||
against the repo root and cannot follow a cross-repo pointer. Pre-existing checker limitation.
|
||||
- The `-app`-suffixed wanderer tags (`v0.20.0-app`) are undocumented upstream; deliberately not used.
|
||||
- 6 apps' `.felhom.yml` still carry `mem_limit` values that do not equal the sum of their services'
|
||||
limits. wanderer is now correct; the others were out of scope.
|
||||
|
||||
Reference in New Issue
Block a user