upstream image drift audit — app-catalog-felhom.eu — 2026-09-21 ====================================================================== Read-only measurement, run from DooPlex against public registries only (Docker Hub, ghcr.io, lscr.io, registry.gitlab.com). Nothing was queried from a customer/demo box. The catalog repo was not modified and nothing was committed. PRE-EXISTING REPO STATE NOTE: at the start of this session `git status --porcelain` in app-catalog-felhom.eu already showed 3 modified files (CLAUDE.md, scripts/check-engine-major.py, scripts/test_gate_decoys.py) — an in-progress, uncommitted R-469 change (MariaDB major-upgrade gate) that predates this session. This audit did not touch those files and made no edits/commits anywhere in the repo; flagging only because the task said 'do not modify' and the tree was already not clean. 1. INVENTORY ---------------------------------------------------------------------- Apps (templates/*/docker-compose.yml): 53 Total `image:` lines across all composes: 79 Unique image refs (pins): 66 floating (DB-engine family tag, e.g. postgres:16-alpine, mariadb:11.6, redis:7-alpine, postgis:16-3.5-alpine): 8 pinned-semver (full app release tag): 58 App classification (from compose + .felhom.yml, not memory): database = has a postgres/mariadb/mysql/mongo sidecar image file-leg = binds host media/library paths (HDD_PATH / USERDATA_PATH), no DB sidecar other = neither database: 14 apps -> bookstack, calcom, claper, docmost, immich, kimai, nextcloud, outline, paperless-ngx, rallly, romm, sparkyfitness, tandoor, zipline file-leg: 9 apps -> audiobookshelf, calibre-web, emby, jellyfin, komga, navidrome, plex, radarr, sonarr other: 30 apps -> actualbudget, adventurelog, bentopdf, code-server, crafty-controller, ghost, gitea, glance, gokapi, grafana, gramps-web, home-assistant, homebox, homepage, mealie, n8n, onlyoffice, opengist, papra, plant-it, privatebin, recipe-importer, seerr, termix, uptime-kuma, vaultwarden, vikunja, wanderer, wger, wishlist EDGE CASE flagged, not silently folded in: `adventurelog` runs a postgis/postgis (postgres-derivative) DB sidecar, but the string 'postgis' does not contain 'postgres'/'mariadb'/'mysql'/'mongo', so by the literal substring rule it lands in 'other', not 'database'. If you want postgis apps folded into 'database', adventurelog is the one app that moves (other: 30->29, database: 14->15). 2. DEAD-IMAGE CHECK (does the pin still resolve at all) ---------------------------------------------------------------------- Ran the repo's own gate, unmodified, exactly as documented in CLAUDE.md: $ python3 scripts/check-image-resolvable.py --all Result: 65 of 66 pins verified via `docker manifest inspect`, ALL resolve. NO dead images among those checked. The 1 unchecked: msdeluise/plant-it:0.10.0 (app `plant-it`, lifecycle: abandoned). The gate calls it INCONCLUSIVE (rc=0 but stderr contains 'denied'/'unauthorized', which its own trap-1 logic refuses to read as success). Independently: hub.docker.com's own catalog API returns {"message":"object not found"} for the whole msdeluise/plant-it repository (not just the tag), and the upstream GitHub repo msdeluise/plant-it now 404s too — two positive signals the image is gone, stronger than the registry gate alone captures, but I'm stating both rather than asserting 'dead' from a registry API that itself only said INCONCLUSIVE. This matches its already-documented `lifecycle: abandoned` state — expected, not a new incident. 3. PINNED-SEMVER: IS THERE A NEWER TAG UPSTREAM ---------------------------------------------------------------------- pinned-semver pins evaluated: 58 behind: 46 no-newer-or-unparsed: 10 error: 1 internal-not-upstream: 1 Of the 46 pins with a newer upstream tag: 7 cross a MAJOR version, 39 are MINOR/PATCH steps (same leading version number). NOTE ON THE MAJOR/MINOR RULE: 'major' = the first dot-separated numeric component changed. This is literal, not semantic — it correctly reads a real major bump for apps like nextcloud/claper/paperless-ngx, but for calendar-versioned images (home-assistant: YYYY.MM.PATCH, actualbudget: YY.MM.PATCH) the 'first number' is a year, so a same-year update is correctly minor/patch and would only misread as 'major' at a year rollover — noted, not a defect found in this sweep's data. 4. APPS BEHIND ON A PINNED-SEMVER IMAGE, sorted furthest-behind first ---------------------------------------------------------------------- STEP IMAGE REPO CURRENT NEWEST APP(S) PINNED SINCE MAJOR nextcloud 34.0.1-apache 35.0.0-apache nextcloud 2026-07-18 MAJOR f0rc3/gokapi v1.9.6 v2.2.4 gokapi 2026-07-18 MAJOR ghcr.io/gethomepage/homepage v1.13.2 v2.4.0 homepage 2026-07-18 MAJOR codewithcj/sparkyfitness_server v0.17.3 v1.7.2 sparkyfitness 2026-07-18 MAJOR codewithcj/sparkyfitness v0.17.3 v1.7.2 sparkyfitness 2026-07-18 MAJOR ghcr.io/paperless-ngx/paperless-ngx 2.20.15 3.2.1 paperless-ngx 2026-07-18 MAJOR ghcr.io/claperco/claper 2.5 3.0 claper 2026-07-18 minor/patch plexinc/pms-docker 1.41.4.9463-630c9f557 -> 1.43.4.10903-e5521bd8c plex 2026-02-15 minor/patch emby/embyserver 4.10.0.20 4.11.0.1 emby 2026-07-18 minor/patch getmeili/meilisearch v1.36.0 v1.54.0 wanderer 2026-07-21 minor/patch ghost 6.53.0-alpine 6.64.0-alpine ghost 2026-07-18 minor/patch n8nio/n8n 2.31.3 2.40.4 n8n 2026-07-18 minor/patch lscr.io/linuxserver/code-server 4.129.0 4.138.0 code-server 2026-07-18 minor/patch ghcr.io/mealie-recipes/mealie v3.20.1 v3.27.0 mealie 2026-07-18 minor/patch lukevella/rallly 4.11.1 4.15.1 rallly 2026-07-18 minor/patch ghcr.io/lukegus/termix 2.5.0 2.8.0 termix 2026-07-12 minor/patch vikunja/vikunja 2.3.0 2.6.0 vikunja 2026-07-18 minor/patch ghcr.io/home-assistant/home-assistant 2026.7.2 2026.9.3 home-assistant 2026-07-18 minor/patch actualbudget/actual-server 26.7.0 26.9.0 actualbudget 2026-07-18 minor/patch gotson/komga 1.25.0 1.27.0 komga 2026-07-18 minor/patch rommapp/romm 5.0.0 5.2.0 romm 2026-07-18 minor/patch ghcr.io/immich-app/immich-server v3.0.3 v3.2.2 immich 2026-07-18 minor/patch ghcr.io/immich-app/immich-machine-learning v3.0.3 v3.2.2 immich 2026-07-18 minor/patch louislam/uptime-kuma 2.4.0 2.5.5 uptime-kuma 2026-07-18 minor/patch lscr.io/linuxserver/radarr 6.3.0 6.4.4 radarr 2026-07-18 minor/patch vaultwarden/server 1.36.0-alpine 1.37.3-alpine vaultwarden 2026-07-18 minor/patch grafana/grafana 13.1.0 13.2.2 grafana 2026-07-18 minor/patch ghcr.io/advplyr/audiobookshelf 2.35.1 2.36.1 audiobookshelf 2026-07-18 minor/patch docmost/docmost 0.95.0 0.96.0 docmost 2026-07-18 minor/patch outlinewiki/outline 1.9.1 1.10.1 outline 2026-07-18 minor/patch ghcr.io/cmintey/wishlist v0.66.0 v0.67.0 wishlist 2026-07-19 minor/patch ghcr.io/seanmorley15/adventurelog-backend v0.12.1 v0.13.0 adventurelog 2026-07-18 minor/patch ghcr.io/seanmorley15/adventurelog-frontend v0.12.1 v0.13.0 adventurelog 2026-07-18 minor/patch ghcr.io/diced/zipline 4.6.1 4.7.0 zipline 2026-07-18 minor/patch deluan/navidrome 0.63.2 0.64.0 navidrome 2026-07-18 minor/patch registry.gitlab.com/crafty-controller/crafty-4 4.10.7 4.11.0 crafty-controller 2026-06-26 minor/patch lscr.io/linuxserver/bookstack 26.05.2 26.05.5 bookstack 2026-07-18 minor/patch gitea/gitea 1.27.0 1.27.3 gitea 2026-07-18 minor/patch ghcr.io/alam00000/bentopdf v2.8.6 v2.8.8 bentopdf 2026-07-12 minor/patch ghcr.io/thomiceli/opengist 1.13 1.15 opengist 2026-07-18 minor/patch ghcr.io/tandoorrecipes/recipes 2.6.13 2.6.15 tandoor 2026-07-18 minor/patch glanceapp/glance v0.8.5 v0.8.6 glance 2026-07-18 minor/patch ghcr.io/papra-hq/papra 26.6.1-rootless 26.6.2-rootless papra 2026-07-12 minor/patch privatebin/pdo 2.0.5 2.0.6 privatebin 2026-09-14 minor/patch lscr.io/linuxserver/sonarr 4.0.19 4.0.20 sonarr 2026-07-18 minor/patch wger/server 2.6 2.7 wger 2026-07-19 5. BY APP CLASS ---------------------------------------------------------------------- database (14 apps, 23 unique pins): floating: 7 pinned-semver: 16 of pinned-semver -> behind: 14 (major: 5, minor/patch: 9), up to date: 2, other: 0 file-leg (9 apps, 9 unique pins): floating: 0 pinned-semver: 9 of pinned-semver -> behind: 7 (major: 0, minor/patch: 7), up to date: 2, other: 0 other (30 apps, 34 unique pins): floating: 1 pinned-semver: 33 of pinned-semver -> behind: 25 (major: 2, minor/patch: 23), up to date: 6, other: 2 6. FLOATING PINS — has the tag's digest moved since the catalog set it ---------------------------------------------------------------------- Method: compare each floating tag's registry-reported last-push date (Docker Hub API tag metadata; ghcr.io has no equivalent public timestamp API reachable anonymously) against the app's own `catalog_since` (the date THIS repo last moved that pin — CLAUDE.md's own definition of 'when we set it'). mariadb:11.4 apps=romm catalog_since=2026-07-18 registry last push=2026-09-18T02:36:29.445608Z mariadb:11.6 apps=kimai, nextcloud catalog_since=2026-07-18 registry last push=2025-02-04T13:35:57.325706Z mariadb:12.3 apps=bookstack catalog_since=2026-07-18 registry last push=2026-09-18T02:37:26.402424Z postgres:15-alpine apps=sparkyfitness catalog_since=2026-07-18 registry last push=2026-09-21T07:07:40.622579Z postgres:16-alpine apps=8 apps (see below) catalog_since=2026-07-18 registry last push=2026-09-21T04:07:46.010722Z redis:7-alpine apps=6 apps (see below) catalog_since=2026-07-18 registry last push=2026-09-18T03:04:58.155237Z postgis/postgis:16-3.5-alpine apps=adventurelog catalog_since=2026-07-18 registry last push=2026-08-31T11:37:24.0266Z Verdict (comparing dates above): 6 of 7 Docker-Hub-hosted floating pins have had their tag repushed (new digest) at least once since the catalog set them (mariadb:11.4, mariadb:12.3, postgres:15-alpine, postgres:16-alpine, redis:7-alpine, postgis:16-3.5-alpine — pushes on or after 2026-08-31, vs. catalog_since 2026-07-18 for all of them). Only mariadb:11.6 (kimai, nextcloud) has NOT been repushed since pinning — its last Docker Hub push was 2025-02-04, well before catalog_since. ghcr.io/immich-app/postgres:16-vectorchord0.4.3-pgvectors0.2.0 (immich): COULD NOT TELL. ghcr.io's anonymous v2 API exposes no last-modified timestamp, and I did not have an authenticated GitHub Packages token to query the versions API. Digest resolved today: sha256:1a078b237c1d9b420b0ee59147386b4aa60d3a07a8e6a402fc84a57e41b043a4 but I have no earlier digest recorded anywhere to compare it to, so 'has it moved since catalog_since' is unmeasured for this one pin — stated as unmeasured rather than guessed. 7. REPRODUCIBLE COMMANDS ---------------------------------------------------------------------- Dead-image check (the repo's own gate, unmodified): cd app-catalog-felhom.eu && python3 scripts/check-image-resolvable.py --all Newer-tag-upstream check (this audit's script, not part of the repo, kept in the scratchpad — reuses the registry-v2 anonymous-bearer-token auth flow from scripts/check-image-resolvable.py, but lists the full upstream tag set per repo instead of testing one ref): python3 /tmp/claude-1000/-mnt-5-hdd-felhom-eu-git/5f83c0fb-b1bf-404e-b99e-2e3b28a31615/scratchpad/drift.py (writes results.json next to it; apps.json/pins.json are the inventory extracted from the catalog's compose/.felhom.yml files) Floating-tag last-push check (Docker Hub API only, no docker CLI): curl -s https://hub.docker.com/v2/repositories///tags/