New apps (in progress): fit table, Radicale + Karakeep evidence, R-765..R-774; wger hidden recorded
gates / gates (push) Successful in 27s

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-10-01 18:01:40 +02:00
parent 0d4600741a
commit ce2a10e995
137 changed files with 15156 additions and 2 deletions
+12 -2
View File
@@ -873,9 +873,19 @@ class (an image `VOLUME` at an unmounted path) is still live — `immich-server`
| **R-759** | **[P3-LOW] wger's onboarding record (the checklist pilot) keeps rows open that no other row owns.** 2026-10-01, `app-catalog-felhom.eu/onboarding/wger.md`: **2.5** no backup → remove → restore → read back of wger exists — the box has no per-app backup press outside an Update (R-648) and wger has no newer step to carry one; **3.7** changing the password and adding a family member not measured, and the template has no `add_people` text; **6.3** no forced-fail undo for wger; **8.2** the app page not read on 9202 this session; **9.1** the runtime volume-persistence gate not re-run (last CLEAN 2026-08-02). The other open rows have their own: 1.5 (R-755), 1.7/2.8 (R-762), 3.4 (R-763), 7.1 (R-764). wger is exempt from the onboarding gate (published before the checklist), so nothing blocks; this row is what keeps the record honest. **Needs:** the five measured on 9202 — 2.5 and 6.3 ride wger's next ladder step (the update's backing-up phase is the per-app backup). `audits/new-app-checklist-2026-10-01/` | **READY — rank P3-LOW; owner: CC (catalog)** |
| **R-760** | **[P3-LOW] vikunja's compose has no healthcheck, and nothing says why.** FOUND 2026-10-01 by `scripts/onboarding_gaps.py` (row 4.1): two services in the catalog have no compose `healthcheck:` — `adventurelog-frontend` (deliberate, a comment cites R-655: the image brings its own) and `vikunja` (no comment). Not measured: whether the vikunja image declares its own `HEALTHCHECK`. The controller's probe still runs (`healthcheck.checks` in `.felhom.yml`), so the badge is not blind; Docker's own health state is. **Needs:** read the image's config; either a compose healthcheck of the family the image has (REUSE.md §2), or a comment saying why none. `app-catalog-felhom.eu/onboarding/EXISTING-APPS-GAPS.md` | **READY — rank P3-LOW; owner: CC (catalog)** |
| **R-761** | **[P3-LOW] The canonical example template tells a new app's author the logo is `<slug>-logo.webp`; the controller loads `<slug>-logo.svg`, then `.png`.** READ 2026-10-01 (checklist row 8.3): `templates/paperless-ngx/.felhom.yml` lines 20–24 (the comment block REUSE.md §2 says to copy) name `{assets.base_url}/assets/{slug}-logo.webp`; `felhom-controller` `internal/config/config.go` `AppLogoURL`/`AppLogoPNGURL` ask for `-logo.svg` and `-logo.png`; `https://felhom.eu/assets/wger-logo.webp` answers 404, `wger-logo.png` 200 (negative control `nosuchapp-logo.png` 404). A new app's author following the comment publishes a logo the box never loads. **Needs:** the comment corrected (a comment-only template change, in a session allowed to touch templates). The checklist row 8.3 already names the right files. `audits/new-app-checklist-2026-10-01/B/B1-upstream-reads.txt` | **READY — rank P3-LOW; owner: CC (catalog)** |
| **R-762** | **[P2-MEDIUM] wger serves no CSS or JavaScript and no uploaded photo: every static file and every `/media/` file answers 404.** MEASURED 2026-10-01 on 9202 (drill catalog, the live template `82fff32`, wger 2.7), found by checklist rows 1.7 and 2.8: the login page links `/static/css/workout-manager.css`, `/static/bootstrap-compiled.css` — both 404 through traefik; the static root inside the container is empty (4 KB). A progress photo posted to `/api/v2/gallery/` answered 201 and the file is on the media volume, but `GET /media/gallery/…png` answers 404 signed in, without a session, and straight at the app inside the container. **Cause, read in the image:** the entrypoint runs `collectstatic` only when `DJANGO_DEBUG == "False"` and the template sets no `DJANGO_DEBUG`; and wger serves `/media/` only in development (`urls.py:393` „served like this during development only”) — upstream's production setup puts nginx in front for `/static` and `/media`. So the household gets an unstyled app and photos that never show. The same lines stand since the template was written (the 2026-09-29 template too). Not checked: whether any box runs wger (on 2026-09-30 none reported to the hub). **Needs:** `DJANGO_DEBUG=False` (collectstatic) and something that serves `/static` + `/media` (upstream's nginx sidecar, or the gunicorn switch of R-755 plus a static server), proven on the bench and on 9202 with a page that loads its CSS and a photo read back. Owner decides together with R-755 (same server question). `audits/new-app-checklist-2026-10-01/C/C8-signup-guest-media-static.txt`, `C/C4-seed-photo-size.txt` | **READY — rank P2-MEDIUM; owner: CC (catalog)** |
| **R-763** | **[P2-MEDIUM] On wger a stranger can make an account after the household's setup, and every anonymous visit to the dashboard creates a guest account.** MEASURED 2026-10-01 on 9202 (live template `82fff32`), found by checklist row 3.4: after the admin existed, a stranger with no dashboard session `POST /en/user/registration` → 302, signed in with it → 302, read the API → 200; two anonymous `GET /en/dashboard` raised the user count from 2 to 4 (wger's middleware `create_temporary_user`, `utils/middleware.py:69`). Settings read inside the app: `ALLOW_REGISTRATION True`, `ALLOW_GUEST_USERS True` (the image defaults; the template sets neither). `GET /en/user/demo-entries` as a stranger answered 500. wger is FIRST-ADMIN class 3 (a known default login, fixed by `after_install`), so it never got decision 47's sign-up lock — it was not in R-711's list. Every crawler visit adds a user row to the household's database. **Needs:** per decision 47, close it after the first admin: `ALLOW_REGISTRATION=False` and `ALLOW_GUEST_USERS=False` (env switches the settings read — measure that the admin can still add family members, row 3.7), proven on 9202 as a stranger. `audits/new-app-checklist-2026-10-01/C/C8-signup-guest-media-static.txt` | **READY — rank P2-MEDIUM; owner: CC (catalog)** |
| **R-762** | **[P2-MEDIUM] wger serves no CSS or JavaScript and no uploaded photo: every static file and every `/media/` file answers 404.** MEASURED 2026-10-01 on 9202 (drill catalog, the live template `82fff32`, wger 2.7), found by checklist rows 1.7 and 2.8: the login page links `/static/css/workout-manager.css`, `/static/bootstrap-compiled.css` — both 404 through traefik; the static root inside the container is empty (4 KB). A progress photo posted to `/api/v2/gallery/` answered 201 and the file is on the media volume, but `GET /media/gallery/…png` answers 404 signed in, without a session, and straight at the app inside the container. **Cause, read in the image:** the entrypoint runs `collectstatic` only when `DJANGO_DEBUG == "False"` and the template sets no `DJANGO_DEBUG`; and wger serves `/media/` only in development (`urls.py:393` „served like this during development only”) — upstream's production setup puts nginx in front for `/static` and `/media`. So the household gets an unstyled app and photos that never show. The same lines stand since the template was written (the 2026-09-29 template too). Not checked: whether any box runs wger (on 2026-09-30 none reported to the hub). **Needs:** `DJANGO_DEBUG=False` (collectstatic) and something that serves `/static` + `/media` (upstream's nginx sidecar, or the gunicorn switch of R-755 plus a static server), proven on the bench and on 9202 with a page that loads its CSS and a photo read back. Owner decides together with R-755 (same server question). `audits/new-app-checklist-2026-10-01/C/C8-signup-guest-media-static.txt`, `C/C4-seed-photo-size.txt` **-- 2026-10-01 (operator):** wger is `lifecycle: hidden` until this and its twin are fixed (catalog `55b8c8a`; read back on 9202: not on the app list, mealie control present). | **READY — rank P2-MEDIUM; owner: CC (catalog)** |
| **R-763** | **[P2-MEDIUM] On wger a stranger can make an account after the household's setup, and every anonymous visit to the dashboard creates a guest account.** MEASURED 2026-10-01 on 9202 (live template `82fff32`), found by checklist row 3.4: after the admin existed, a stranger with no dashboard session `POST /en/user/registration` → 302, signed in with it → 302, read the API → 200; two anonymous `GET /en/dashboard` raised the user count from 2 to 4 (wger's middleware `create_temporary_user`, `utils/middleware.py:69`). Settings read inside the app: `ALLOW_REGISTRATION True`, `ALLOW_GUEST_USERS True` (the image defaults; the template sets neither). `GET /en/user/demo-entries` as a stranger answered 500. wger is FIRST-ADMIN class 3 (a known default login, fixed by `after_install`), so it never got decision 47's sign-up lock — it was not in R-711's list. Every crawler visit adds a user row to the household's database. **Needs:** per decision 47, close it after the first admin: `ALLOW_REGISTRATION=False` and `ALLOW_GUEST_USERS=False` (env switches the settings read — measure that the admin can still add family members, row 3.7), proven on 9202 as a stranger. `audits/new-app-checklist-2026-10-01/C/C8-signup-guest-media-static.txt` **-- 2026-10-01 (operator):** wger is `lifecycle: hidden` until this and its twin are fixed (catalog `55b8c8a`; read back on 9202: not on the app list, mealie control present). | **READY — rank P2-MEDIUM; owner: CC (catalog)** |
| **R-764** | **[P3-LOW] wger sends no mail: its mail backend is the console, so a password-reset mail never leaves the box, and the template maps no SMTP.** READ 2026-10-01 inside the app on 9202 (checklist row 7.1): `EMAIL_BACKEND django.core.mail.backends.console.EmailBackend`; the settings read `ENABLE_EMAIL`, `EMAIL_HOST`, `EMAIL_PORT`, `FROM_EMAIL` …; the template carries no `smtp_mapping`. The household's admin can reset another member's password in the app; a member who forgets theirs and asks wger by e-mail gets nothing, and the page does not say so. Not measured: what wger shows after a reset request. **Needs:** an `smtp_mapping` (vaultwarden's shape, a fresh install with mail OFF booting — REUSE.md §2), or the page saying mail is not available. `audits/new-app-checklist-2026-10-01/C/C2-static-reads.txt` | **READY — rank P3-LOW; owner: CC (catalog)** |
| **R-765** | **[P2-MEDIUM] A new app that builds its login from a `type: password` value at EVERY start loses the household's login on a restore after a remove — found on Radicale's first template, fixed before publishing.** MEASURED 2026-10-01 on 9202 (drill catalog): Radicale's start command rewrote its login file from `RADICALE_PASSWORD` at every start. Remove (keeping backups) + the household's restore → the right password answered 401. Cause, read in the controller: a restore carries only `type: secret` values (`stacks.PortableSecretEnvVars`, the D5 ruling — a `type: password` is an internet-reachable login and stays out of the drive's backup) and makes a NEW password when the guest's own copy is gone (`restore_unit.go:538`, „generated replacement … the page will not show the new value as the password”), expecting the app's login to come back WITH ITS DATA. Fix (catalog, same session): the login file is written on the FIRST start only and lives on the data volume; re-measured: remove + restore → the original login reads the event back. The checklist row that caught it is 2.5 (restore round trip); 1.9 names the shape. `audits/new-apps-2026-10-01/box/radicale-attempt1/restore.txt`, `box/radicale/restore.txt` | **CLOSED 2026-10-01 — Radicale's template, before publishing** |
| **R-766** | **[P3-LOW] A new app's logo and screenshots reach the boxes only with the next HUB release — the website alone is not enough.** READ 2026-10-01: the box's asset syncer reads the hub's `/api/v1/assets/manifest` (`felhom-controller internal/assets/syncer.go`); the hub serves its PVC, seeded at start from `/usr/share/felhom/assets-seed/` baked into the hub IMAGE (`hub/Dockerfile:27`; the hub build copies `website/assets/*-logo.{svg,png}` and `*-screenshot-*.webp`, `hub/README.md`). So Radicale's assets (pushed `40f0742`) answer 200 on felhom.eu, and a box shows an app card without a logo until a hub build carries them. Today's fence kept the hub untouched. **Needs:** the next hub release (any) carries them — say so in its report; or a ruling that the hub reseeds from the website without a release. `audits/new-apps-2026-10-01/S/S3-assets.txt` | **READY — rank P3-LOW; owner: CC (next hub release)** |
| **R-767** | **[P3-LOW] MeTube has no login at all, by design, and the box has no permanent household-only door — so it is not built.** READ 2026-10-01 (fit table): upstream „MeTube deliberately has no login system … use HTTP basic auth, Authelia, or your proxy” (wiki, Reverse-proxy-configurations). Published on the household's subdomain, anyone who finds it can make the box download through the household's IP and use uploaded YouTube cookies. The box offers `setup_gate` (until setup) and `signup_block` only. **Two routes:** (a) a login-proxy sidecar inside the template (basic auth from a generated password, catalog-only — measure the phone/browser experience); (b) a controller feature: a permanent household-only door. Also owed before any build: the operator's word on the YouTube sentence (STATUS). `audits/new-apps-2026-10-01/FIT.md` | **WAITING-ON-OPERATOR — rank P3-LOW; owner: operator (route), CC builds** |
| **R-768** | **[P3-LOW] Grimoire is not built: upstream rules out public exposure and publishes no image for its current line.** READ 2026-10-01: SECURITY.md „Public-network exposure is not a supported Grimoire mode”; docs/06-remote-access.md „General REST routes remain loopback-only and tokenless”; v1.x has no published image (the compose builds from source; Docker Hub is the old 0.x). Karakeep covers the bookmark need. **Reopens if** upstream publishes an image and supports authenticated remote use. `audits/new-apps-2026-10-01/FIT.md` | **WATCHING — rank P3-LOW; owner: CC (re-read at the next catalog campaign)** |
| **R-769** | **[P3-LOW] Pinchflat is not built: upstream is paused (no release in 2026, last push 2025-12-16) and its last release has no image tag.** READ 2026-10-01: ghcr's newest version tag `v2025.6.6`, `latest` amd64 only; runs as root by default; an unanswered 30 GB yt-dlp memory report (#866); third parties call it unmaintained (community-scripts #15968). Forks with images exist (Pinchflat-NGX, MorganKryze). **Needs:** the operator's word on a fork (a new upstream), after the YouTube sentence (R-767). `audits/new-apps-2026-10-01/FIT.md` | **WAITING-ON-OPERATOR — rank P3-LOW; owner: operator** |
| **R-770** | **[P3-LOW] Invidious — fit check only; the recommendation is not to build it.** READ 2026-10-01: playback needs `invidious-companion` (rolling `latest`, no version tags); PostgreSQL 14 (EOL 2026-11); `registration_enabled: true` by default; upstream: a bot check means „your IP is blocked from YouTube”, a 429 can last 24 h, triggered by „someone on your network” — on our boxes that IP is the household's. One bad period in 2026 (March, ~2 weeks). No report found of a family's other devices being bot-checked (inference). **Needs:** the operator's go / no-go. `audits/new-apps-2026-10-01/FIT.md` | **WAITING-ON-OPERATOR — rank P3-LOW; owner: operator** |
| **R-771** | **[P3-LOW] moonlight-web — fit check only; not buildable through an HTTP-only tunnel at usable latency.** READ 2026-10-01: two unrelated projects (MrCreativ3001/moonlight-web-stream, the original; linckosz/moonlight-web); both need Sunshine/Apollo/Wolf on a gaming PC on the LAN and WebRTC over UDP (40000-40100/udp; linckosz recommends host networking and sends telemetry by default); both have a WebSocket fallback (high latency, all video through the tunnel); a logged-in user controls the PC's desktop. **Needs:** the operator's go / no-go (LAN-only use would need a different publishing model). `audits/new-apps-2026-10-01/FIT.md` | **WAITING-ON-OPERATOR — rank P3-LOW; owner: operator** |
| **R-772** | **[P3-LOW] A health probe that finds NO container to probe records `healthy: true` — and the next check is 5 minutes away.** MEASURED 2026-10-01 on 9202 (controller 0.285.0, Karakeep): with the app container stopped, the probe's record read `{type: none, target: karakeep, healthy: true, message_key: health.no_probe_container}` and stayed so 4+ minutes (`RunHealthProbes: skipping karakeep — last check …, effective interval 5m0s, healthy=true`). The app's STATE did read `degraded` within 10 s, so the household saw it; the probe record alone says healthy about a check that did not run (the presence-is-not-success shape). Not checked: which readers use `health_probe.healthy` (the guarded Update's `verifying`? the hub report?). **Needs:** a not-run probe recorded as not-healthy (or `unknown`), with a test that pins it. `audits/new-apps-2026-10-01/box/karakeep-768M/neg-and-crawl.txt` | **READY — rank P3-LOW; owner: CC (controller)** |
| **R-773** | **[P3-LOW] After a remove + restore, an app's sign-up route block (decision 47) is gone; only the app's own switch still refuses.** MEASURED 2026-10-01 on 9202 (Karakeep): before — `/signup` 403 and tRPC `users.create` 403; after the household removed the app (keeping backups) and pressed restore — `/signup` answers 200, `users.create` still 403 (the restored env keeps `DISABLE_SIGNUPS=true`). For an app with NO own switch (opengist, wishlist: block only) a remove + restore would reopen sign-up entirely — not measured. **Needs:** the restore re-applies the lock record's block (or the gate) for an app whose template has `signup_block`, with a test; then measured on a block-only app. `audits/new-apps-2026-10-01/box/karakeep/restore.txt` | **READY — rank P3-LOW; owner: CC (controller)** |
| **R-774** | **[P3-LOW] Two things the new apps' pages do not show yet: Karakeep's mail-ON path is unproven, and its official phone app reports crashes to its makers.** READ/MEASURED 2026-10-01: Karakeep's `smtp_mapping` (plaintext :2526, as Cal.com) was proven only with mail OFF (a fresh install boots) — 9202 has no hub, so the relay cannot be exercised there; the official mobile app ships Sentry crash reporting with a hard-coded DSN (`apps/mobile/app/_layout.tsx`, FIT.md). **Needs:** one password-reset mail from Karakeep on a hub-enabled box (demo-hp 9201, a throwaway install); and a sentence on the page about the phone app (copy, freeze). `audits/new-apps-2026-10-01/bench/karakeep-mail-off-boot.txt` | **READY — rank P3-LOW; owner: CC (catalog)** |
<!-- DUE-CHECKS-BEGIN — machine-readable. Parsed by scripts/due_checks_gate.py.
One row per dated check. The R-number must have a row above. Dates are UTC.