Files
2026-10-05 16:55:06 +02:00

19 KiB
Raw Permalink 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)
3.10 2026-10-02 The visitor's address: how the app reads it (from the RIGHT with a trusted-proxy list / a fixed count / the LEFTMOST X-Forwarded-For / not at all) and what it decides with it (log, lock, "local network" rights). A leftmost reader whose address decides anything carries the router reset <router>-xff; a right-walking reader with a setting gets 172.16.0.0/12 trusted read the app's client-address function at the pinned tag; then on 9202 a forged leftmost X-Forwarded-For through the SIMULATED tunnel (a container at 172.16.253.2, Part A's method) must not become the address the app logs or locks since controller v0.286.0 traefik keeps the tunnel's chain, whose leftmost entry a stranger writes (R-753); 19 catalog apps read the leftmost (felhom.eu/documentation/audits/visitors-2026-10-01/A/sweep/)

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 (the canonical template's comment said so until 2026-10-05, 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 10
4 Health 5
5 Resources 5
6 Updates 5
7 Mail 1
8 Text and listing 5
9 Sign-off 4
total 61