Files
app-catalog-felhom.eu/NEW-APP-CHECKLIST.md
T
admin dc0ab8b2a8
gates / gates (push) Successful in 2s
New-app checklist: reviewed, a gate (onboarding), the wger pilot record, the existing apps' gap page
NEW-APP-CHECKLIST.md: the reviewer's draft reviewed - 60 rows in 10 groups, each with how/why and a since date;
7 rows added, 16 sharpened, 9 wrong claims fixed. onboarding/_TEMPLATE.md (one line per id), onboarding/wger.md
(the pilot, exempt app, 11 open rows each a register row), onboarding/EXISTING-APPS-GAPS.md (read only, from
scripts/onboarding_gaps.py). Gate onboarding (scripts/check-onboarding.py) in --fast: a template directory not
among the 53 published before 2026-10-01 needs a complete record; decoys in test_gate_decoys.py (16 cases, 5 gate
mutants seen red). CLAUDE.md, REUSE.md 5, README point to it. No template changed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
2026-10-01 15:20:52 +02:00

18 KiB
Raw Blame History

NEW-APP CHECKLIST — what a new catalog app must have measured before it reaches the live catalog

Operator request 2026-10-01: before new apps are added, a checklist every new app passes. Reviewer's draft the same day, reviewed and piloted on wger by CC (felhom.eu/documentation/audits/new-app-checklist-2026-10-01/). The gate: scripts/check-onboarding.py (in catalog_gates.py --fast, so the pre-push hook and CI run it).

What this is. Every check below exists because something went wrong on a real app — the why column names it. The filled-in copy per app is the app's onboarding record, onboarding/<app>.md, started by copying onboarding/_TEMPLATE.md. A new template directory without a complete record is refused by the gate.

How an item is answered. One line per id in the record: <id> | done | <evidence path>, <id> | n/a | <reason> or <id> | open | <what is missing>.

  • done names evidence that exists: a non-empty file, or a directory holding one. Paths are written from the workspace root — app-catalog-felhom.eu/… or felhom.eu/documentation/audits/…. Several paths: separate them with ;. A note may follow the path(s) after —.
  • n/a carries a reason of at least four words.
  • open blocks publishing.

Measured, not read. "Measured" means on the bench (LXC 9401 on demo-hp) or on scratch guest 9202 (drill catalog only, 09 §6.5) through the product. Upstream reading may PLAN a test; it never CLOSES an item. Where the how says "read", reading is the test (a licence, a tag list).

The since column. The date an id joined the list. A record carries every id whose since is on or before the record's opened: date; an id added later binds only apps opened after it. A record may answer a newer id anyway.

The 53 apps in the catalog on 2026-10-01 are exempt (operator default, may be reversed); what the catalog already shows for them is onboarding/EXISTING-APPS-GAPS.md. An exempt app's record, if one exists, must still be well-formed, and may say open.


0. Fit — should we offer it at all?

id since Check How Why (what went wrong before)
0.1 2026-10-01 Open-source licence; we pull the upstream image, never redistribute it read the repo's licence file the business sells installation, not software
0.2 2026-10-01 Upstream is alive: a release in the last 6 months, issues answered read the repo plant-it is lifecycle: abandoned (templates/plant-it/.felhom.yml)
0.3 2026-10-01 An official image with version tags (not latest only), amd64 (+ arm64 if pi_compatible) docker manifest inspect <image>:<tag> — the tag list can be stale (REUSE.md §2 "Image pinning") pins, digests and the ladder need real tags
0.4 2026-10-01 Telemetry / phone-home — on by default? a switch to turn it off? Start-time downloads (exercise sync, model fetch)? read the entrypoint (1.7) for start-time network jobs; on 9202, the app's log of its first start data sovereignty is the pitch
0.5 2026-10-01 Needs the internet at runtime (claim tokens, first-start downloads)? docs + the first start on 9202 plex (PLEX_CLAIM), adventurelog's world data (09 decision 41), immich's geodata import (R-732)
0.6 2026-10-01 Needs ports other than HTTP(S)? The tunnel carries HTTP only — say what the household loses compose + docs gitea SSH, DLNA, game servers
0.7 2026-10-01 Phone / desktop apps: which login route do they call, and does it work through the tunnel and the setup gate? docs for the route; then that route with curl through traefik on 9202 (3.9) — a real client only if one is at hand wger's phone-app login route answered 500 while the web login worked (R-737); a gated app is unreachable for a phone app (09 decision 46)
0.8 2026-10-01 What a household gets from it, in one sentence (Hungarian) — the catalog card and use_cases
0.9 2026-10-01 The app does not call ITSELF at its public name (server-side), or the bench override is recorded the env/config the server reads for its own URL; then the bench start wanderer cannot run on the bench at all (R-739) — no bench, no update proof

1. Images, the start command and the database

id since Check How Why
1.1 2026-10-01 Every image pinned to a concrete tag that resolves gates image-pins + image-resolvable (catalog_gates.py <app>) :latest breaks restore fidelity
1.2 2026-10-01 Database engine and major = what the app's own upstream compose runs upstream compose at the pinned tag 09 decisions 37/42 (one conversion, not two)
1.3 2026-10-01 MariaDB sidecar: MARIADB_AUTO_UPGRADE=1; PostgreSQL 18: mount at /var/lib/postgresql compose (no gate checks either today) R-459; PG 18 refuses /var/lib/postgresql/data (CLAUDE.md engine rule)
1.4 2026-10-01 The app migrates its own database at start, or the switch that makes it do so is set 1.7's read names the switch; then prove it: install the PREVIOUS upstream release on the bench, seed, move to the pin (upgrade-test.py --move), read back through the login wger ran no migration without DJANGO_PERFORM_MIGRATIONS; the update said done, the login answered 500 (R-738). A new app at its newest tag has no "next" update to try — the step INTO the pin is the test
1.5 2026-10-01 The app runs its production server, not a development server process list in the running container (ps / /proc/*/cmdline) wger ran manage.py runserver (R-755)
1.6 2026-10-01 Every key and secret the app READS is set or generated — none left at an image default or empty list the env names the app's settings read (grep its settings source for env reads of *KEY*, *SECRET*, *TOKEN*, *PEM*); each one set in the compose; then 3.9's logins on every route wger read JWT_PRIVATE_KEY, the template set none: the API login answered 500 on the right password while the web login worked (R-737); grafana falls back to admin on an empty field (R-708)
1.7 2026-10-01 Every start-time switch in the image's entrypoint is read and decided (migrations, production server, debug, start-time downloads, static files) read the entrypoint inside the image (docker run --rm --entrypoint cat <image> <entrypoint>); list each if $VAR and the value the template sets one read of wger's entrypoint.sh shows DJANGO_PERFORM_MIGRATIONS AND WGER_USE_GUNICORN — R-738 and R-755 in one file (felhom.eu/documentation/audits/more-night-apps-2026-09-30/box/wger/entrypoint-read.txt)
1.8 2026-10-01 Debug / development mode is OFF on the public origin 1.7's read + the running container's env; an error page through traefik shows no stack trace adventurelog ran Django with DEBUG=True on the public origin (R-482)
1.9 2026-10-01 A secret whose loss destroys data or locks people out (it encrypts stored data, or signs 2FA secrets / long-lived tokens) carries data_key: true and a comment saying why it must never be regenerated; a key that only signs sessions does not what each generated secret is used for (the app's settings source) a restore must RECOVER that key; regenerating it destroys data or locks out 2FA (felhom-app-catalog skill §4)

2. Storage and backup

id since Check How Why
2.1 2026-10-01 Every path the app writes is mounted — measured gate volume-persistence (check-volume-persistence.py <app>, scratch host) papra backed up an empty folder (R-156)
2.2 2026-10-01 Where each path lives: named volume (NVMe) / ${HDD_PATH}/appdata / ${USERDATA_PATH} compose; REUSE.md §2 "Compose file skeleton" the household can browse userdata, not appdata
2.3 2026-10-01 Backup class for every HDD path (backup: in .felhom.yml) and what the tier-1 unit holds .felhom.yml backup:; the app's backup page on 9202 DB rows point at files (07); the tier-1 unit holds NO drive-side data (R-537, R-538)
2.4 2026-10-01 File owner / uid fits the box (PUID/PGID where the image wants them) first start on 9202 linuxserver images chown their tree
2.5 2026-10-01 A backup and a restore through the product, data read back 9202: seed → backup → remove → restore → read back the promise is the restore, not the backup
2.6 2026-10-01 "Remove with data" and "remove, keep data" both do what they say 9202, then list what is left on the drive "remove with data" was inert (R-442); refused with the folder present (R-756, cause not yet known)
2.7 2026-10-01 Off-site size for a typical household measured size after the seed + one sentence 09 decision 50 (the page names the largest apps)
2.8 2026-10-01 A file the household uploads opens again through the front door 9202: upload through traefik, open it the way the page does adventurelog's photos uploaded and rendered broken (R-483)

3. Accounts and strangers

id since Check How Why
3.1 2026-10-01 First-admin class (FIRST-ADMIN.md 1–6), measured, and the household can make its first account on a fresh install fresh install on 9202, through traefik 09 decision 45; wishlist could not be signed up to while the deploy said success (R-612)
3.2 2026-10-01 Known default login → after_install with a generated password (and a generated name, if the name is public and a lock targets it) 9202: default fails, generated works, wrong fails bookstack, claper (R-702), calibre-web (09 decisions 45, 61)
3.3 2026-10-01 Open first-run screen → setup_gate (+ a probe that flips, measured before and after) 9202 as a stranger; gate probe-measured refuses a probe with no measurement above it 33 apps gated today (09 decision 46); a probe that never flips blocks the household (R-715)
3.4 2026-10-01 Open sign-up after the setup → signup_block / after_setup; the block case-insensitive, the API sign-up blocked too 9202 as a stranger, after the setup R-711, R-512; 09 decisions 47–49
3.5 2026-10-01 The install window: a stranger reaches nothing before the password is replaced poll the default login once a second from the install press R-741 (fixed box-wide in controller v0.284.x — a new after_install app still proves it)
3.6 2026-10-01 Lock-out: N wrong passwords by a stranger for the public name — who is locked (name / address / everyone), for how long 9202 through traefik; then the household's right password, and a SECOND member's mealie 24 h (R-747); behind the tunnel every visitor has ONE address, so a per-address lock locks everyone (R-753) — wger (R-752)
3.7 2026-10-01 The household can change its password and add family members; the page says how 9202 add_people copy
3.8 2026-10-01 Secrets pass to commands as arguments, never inside program code review after_install claper pasted the password into Elixir code (R-713); security review 2026-09-29
3.9 2026-10-01 Sign in the way each client does, through traefik over https: the browser form (with its https Origin and CSRF cookie) AND every API login route a phone/desktop app uses — right password works, wrong refused 9202, curl with the headers a browser sends; the API route from 0.7 wger refused every browser sign-in behind traefik (CSRF, R-712); wger's API login 500 (R-737)

4. Health

id since Check How Why
4.1 2026-10-01 Compose healthcheck of the family the IMAGE has (inspected, one tool per run), dialling 127.0.0.1 not localhost the inspection loop in the felhom-app-catalog skill §2; REUSE.md §2 (no gate checks localhost; 0 templates use it today) rallly's guessed wget (ENOENT); vaultwarden's IPv6 trap
4.2 2026-10-01 The controller probe dials what the compose healthcheck dials; a real health path where one exists gate probe-matches-compose a wrong probe stops a working app after an update (R-618)
4.3 2026-10-01 Healthy within start_period on a cold first start (incl. one-time imports), with no restarts 9202, timed, RestartCount read glance crash-looped on every fresh install (R-473); immich's first start restarted 12× (R-676) — 09 decision 28 stops ≥ 6 in 10 min
4.4 2026-10-01 Negative control: a broken app reads unhealthy stop the DB (or break the app's data dir), read the status a probe that is always green proves nothing; uptime-kuma parked on its wizard read healthy (R-613)
4.5 2026-10-01 The probe-named container is the stack name; sidecars <app>-db … compose (REUSE.md §2 "Probe-container naming") findProbeContainer falls back to the DB; paperless's probe never ran (R-630)

5. Resources

id since Check How Why
5.1 2026-10-01 First start from birth, swap OFF: peak anon, oom_kill = 0 bench, the container's own cgroup sampled every 2 s from creation (memory.stat anon, memory.events oom_kill — never Docker's OOMKilled, R-528) immich's DB killed at 512 MiB, hidden by swap on 9202 (R-732, R-733); calcom could not start at its limit (R-703)
5.2 2026-10-01 10-minute soak under the household's heaviest ordinary act: peak anon < 80 % of the limit, 0 kills, 0 restarts harness memory watch (upgrade-test.py) + one burst of that act romm OOM-looped for six hours after an update called success (R-635, 09 decision 22); paperless lost 11 of 20 uploads at once (R-514)
5.3 2026-10-01 mem_limit in .felhom.yml = the sum of the compose limits, and the header comment says the same arithmetic by hand — no gate checks it; 8 templates differ today (R-758) immich's mem_limit was 128 MB under its sum, its header named a 256M database running at 512M (fixed 56c4888)
5.4 2026-10-01 Node/Java apps: does the heap size itself from the limit? two watches at two limits R-693 (docmost)
5.5 2026-10-01 Pi-compatible (yes/no) and the disk the images pull registry (image size) pi_compatible is a card field; a box's Docker disk refuses installs when full (R-736)

6. Updates

id since Check How Why
6.1 2026-10-01 An upgrade fixture: seed + read-back through the app's own front door, negative control — or the reason none can exist upgrade_fixtures*.py the box's health check sees only the front page; only the read-back saw wger broken (R-738); three apps cannot be seeded headless (R-624)
6.2 2026-10-01 A first ladder step proven on bench + 9202 (--write-ladder), or "manual only" with the reason harness; gates test-record + test-record-move check the entry once written 38 of 53 apps carry a ladder today; zipline needed two steps where one failed (R-742)
6.3 2026-10-01 The undo works on a real failure (one forced-fail case) 9202, the drill catalog's image store (09 §6.5) zipline's real failed jump, undone in 20 s (R-742)
6.4 2026-10-01 files_may_change understood (which files change at start) bench (files_changed_detail) immich's six marker files (R-734)
6.5 2026-10-01 Tag shape is stable upstream (no v dropped, no flavour prefix) and whether the publisher re-pushes tags tag list over a year; linuxserver rebuilds weekly gramps-web, jellyfin, kimai (R-731); same-tag re-pushes (R-743, 09 decisions 52/55)

7. Mail

id since Check How Why
7.1 2026-10-01 Sends mail? → smtp_mapping + ${VAR:-} compose lines; a fresh install with mail OFF boots 9202 vaultwarden's empty-vars trap (REUSE.md §2 "App-email")

8. Household-facing text and listing

id since Check How Why
8.1 2026-10-01 Hungarian + English, informal „te", no „kérjük"; parity green; freeze updated gate copy-i18n (check-copy-i18n.py --capture-freeze after a copy change) operator rule; R-560
8.2 2026-10-01 app_info: tagline, use_cases, first_steps, default_creds (if any), add_people — and each one TRUE on 9202 read on 9202's app page and follow the first steps paperless's page named a login that did not exist (R-515); first steps named a literal wiki.DOMAIN (R-498)
8.3 2026-10-01 Logo + screenshots on felhom.eu by slug https://felhom.eu/assets/<slug>-logo.svg (or .png, the controller's fallback) and <slug>-screenshot-<n>.webp answer 200 — NOT -logo.webp, which the canonical template's comment still names (R-761) the card is blank otherwise
8.4 2026-10-01 README tables, FIRST-ADMIN row, category, catalog_since review REUSE.md §5
8.5 2026-10-01 The website's app count still holds `ls templates wc -lagainstfelhom.eu/website` claims; record a drift

9. Sign-off

id since Check How Why
9.1 2026-10-01 python3 scripts/catalog_gates.py <app> — all green (exit 0) bench / scratch host (it runs the runtime gate) the one entry point (R-161)
9.2 2026-10-01 A fresh install on 9202 from the drill catalog, as a household and as a stranger, start to finish 9202 the walk finds what single checks miss (R-482..R-488 in one evening)
9.3 2026-10-01 Every item above done or n/a-with-reason; every finding not fixed is a register row the record prose is not a record
9.4 2026-10-01 Published to the live catalog in ONE commit with its record gate onboarding a template and its proof travel together

Row count

group rows
0 Fit 9
1 Images, start command, database 9
2 Storage and backup 8
3 Accounts and strangers 9
4 Health 5
5 Resources 5
6 Updates 5
7 Mail 1
8 Text and listing 5
9 Sign-off 4
total 60