9.1 KiB
NEW-APP CHECKLIST — draft (reviewer, 2026-10-01)
What this is. The list of things every new catalog app must have measured and recorded BEFORE it reaches the live catalog. Each item 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; a gate refuses a new app without a complete one.
How to read an item. Each answer is done (with an evidence path), n/a (with a one-line reason), or open (blocks publishing). "Measured" means on the bench or on scratch guest 9202 through the product — never read from upstream alone. Upstream reading is allowed to PLAN a test, never to CLOSE an item.
Where it lives (proposed — CC confirms): the checklist in app-catalog-felhom.eu/NEW-APP-CHECKLIST.md; one record per
app in app-catalog-felhom.eu/onboarding/<app>.md (outside templates/, so the box never syncs it — the controller copies
only docker-compose.yml and .felhom.yml, sync.go:364). The new app is tested from the drill catalog on 9202 and
reaches the live catalog only with a complete record.
0. Fit — should we offer it at all?
| # | Check | How | Why (what went wrong before) |
|---|---|---|---|
| 0.1 | Open-source licence; we pull the upstream image, never redistribute | read the repo | the business sells installation, not software |
| 0.2 | Upstream is alive: a release in the last 6 months, issues answered | read the repo | plant-it went abandoned, its image is gone |
| 0.3 | An official image with version tags (not latest-only), amd64 (+arm64 if Pi) |
registry | pins and digests need real tags |
| 0.4 | Telemetry / phone-home — on by default? a switch to turn it off? | docs + one packet capture or log read on 9202 | data sovereignty is the pitch |
| 0.5 | Needs the internet at runtime (claim tokens, first-start downloads)? | docs + a first start | plex (plex.tv claim), adventurelog (world data), immich (geodata) |
| 0.6 | Needs ports other than HTTP(S)? The tunnel carries HTTP only | compose + docs | gitea SSH, media DLNA, game servers — say what the household loses |
| 0.7 | Phone / desktop apps: do they work through the tunnel and the setup gate? | docs + one real client if one exists | immich's phone app vs the gate (decision 46) |
| 0.8 | What a household gets from it, in one sentence (Hungarian) | — | the catalog card and use_cases |
1. Images and the database
| # | Check | How | Why |
|---|---|---|---|
| 1.1 | Every image pinned to a concrete tag; resolvable | catalog_gates.py <app> (gates 1, 2) |
:latest breaks restore fidelity |
| 1.2 | Database engine and major = what the app's own upstream compose runs | upstream compose at the pinned tag | decision 42's rule; avoids an early conversion |
| 1.3 | MariaDB sidecar: MARIADB_AUTO_UPGRADE=1; PostgreSQL 18: mount at /var/lib/postgresql |
compose | R-459; PG 18 refuses /data |
| 1.4 | The app migrates its own database at start, or the switch that makes it do so is set | an update on 9202, login read back | wger: no migration without DJANGO_* switch, update said "done", login 500 (R-738) |
| 1.5 | The app runs its production server, not a dev server | process list in the container | wger runserver (R-755) |
| 1.6 | Every secret the app needs is generated (no image default, no empty key) | compose + a login on 9202 | wger JWT key missing → login 500 (R-737) |
2. Storage and backup
| # | Check | How | Why |
|---|---|---|---|
| 2.1 | Every path the app writes is mounted — measured | gate 3, check-volume-persistence.py <app> |
papra backed up an empty folder (R-156) |
| 2.2 | Where each path lives: named volume (NVMe) / ${HDD_PATH}/appdata / ${USERDATA_PATH} |
compose, REUSE.md skeleton | the household can browse userdata, not appdata |
| 2.3 | Backup class for every HDD path (mandatory / excluded …) and data_paths labels |
.felhom.yml backup: |
DB rows that point at files need those files (07) |
| 2.4 | File owner / uid fits the box (PUID/PGID where the image wants them) | first start on 9202 | linuxserver images chown their tree |
| 2.5 | 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 | "Remove with data" and "remove keep data" both work | 9202 | R-756 (refused with a folder present) |
| 2.7 | Off-site size estimate for a typical household | measured size after seed + a sentence | decision 50's size warning |
3. Accounts and strangers
| # | Check | How | Why |
|---|---|---|---|
| 3.1 | First-admin class (FIRST-ADMIN.md 1–6), measured | fresh install on 9202 | decision 45 |
| 3.2 | Known default login → after_install with a generated password (and name, if the name is public and lockable) |
9202: default fails, generated works, wrong fails | bookstack, calibre-web (decisions 45, 61) |
| 3.3 | Open first-run screen → setup_gate (+ probe that flips, measured before and after) |
9202 as a stranger | 32 apps, decision 46 |
| 3.4 | Open sign-up after the setup → signup_block / after_setup switch; case-insensitive |
9202 as a stranger | decisions 47–49 |
| 3.5 | The install window: a stranger reaches nothing before the password is replaced | poll once a second during install | R-741 |
| 3.6 | Lock-out: N wrong passwords by a stranger — who is locked (name / address / everyone), for how long | 9202 through traefik | mealie 24 h, wger everyone (R-747, R-752, R-753) |
| 3.7 | The household can change its password and add family members; the page says how | 9202 | add_people copy |
| 3.8 | Secrets pass to commands as arguments, never inside program code | review after_install |
security review 2026-09-29 |
4. Health
| # | Check | How | Why |
|---|---|---|---|
| 4.1 | Compose healthcheck of the right family, 127.0.0.1 not localhost |
REUSE.md §2 | vaultwarden IPv6 trap |
| 4.2 | The controller probe dials what the compose healthcheck dials; a real health path where one exists | gate 7 | a wrong probe stops a working app after an update (R-618) |
| 4.3 | Healthy within start_period on a cold first start (incl. one-time imports) |
9202, timed | immich geodata import |
| 4.4 | Negative control: a broken app reads unhealthy | stop the DB, read the status | a probe that is always green proves nothing |
| 4.5 | The probe-named container is the stack name; sidecars <app>-db … |
compose | findProbeContainer fallback picks the DB |
5. Resources
| # | Check | How | Why |
|---|---|---|---|
| 5.1 | First start from birth, swap OFF: peak anon, oom_kill = 0 |
bench, sampled every 2 s | immich DB killed at 512 MiB, hidden by swap on 9202 (R-732, R-733) |
| 5.2 | 10-minute light-load soak: peak < 80 % of the limit, 0 kills, 0 restarts | harness memory watch | romm OOM at +76 s (decision 22) |
| 5.3 | mem_limit in .felhom.yml = the sum of the compose limits |
gate / review | immich's header was 128 MB off |
| 5.4 | Node/Java apps: does the heap size itself from the limit? | two watches at two limits | R-693 (docmost) |
| 5.5 | Pi-compatible (yes/no), disk it pulls (image size) | registry | old images filled 9202 (R-736) |
6. Updates
| # | Check | How | Why |
|---|---|---|---|
| 6.1 | An upgrade fixture: seed + read-back through the app's own front door, negative control | upgrade_fixtures*.py |
without it the app never updates itself |
| 6.2 | A first ladder step proven on bench + 9202 (--write-ladder), or "manual only" with the reason |
harness | 35 of 53 update at night; the rest need a person |
| 6.3 | The undo works on a real failure (one forced-fail case) | 9202 | zipline's real failed jump, undone in 20 s |
| 6.4 | files_may_change mark understood (which files change at start) |
bench | immich's six marker files (R-734) |
| 6.5 | Tag shape is stable upstream (no v dropped, no flavour prefix) |
tag list | gramps-web, jellyfin, kimai (R-731) |
7. Mail
| # | Check | How | Why |
|---|---|---|---|
| 7.1 | Sends mail? → smtp_mapping + ${VAR:-} compose lines; a fresh install with mail OFF boots |
9202 | vaultwarden empty-vars trap |
8. Household-facing text and listing
| # | Check | How | Why |
|---|---|---|---|
| 8.1 | Hungarian + English, informal „te", no „kérjük"; parity green; freeze updated | check-copy-i18n.py |
operator rule |
| 8.2 | app_info: tagline, use_cases, first_steps, default_creds (if any), add_people |
read on 9202's page | the page is the manual |
| 8.3 | Logo + screenshots on felhom.eu by slug |
the asset URL answers 200 | the card is blank otherwise |
| 8.4 | README tables, FIRST-ADMIN row, category, catalog_since |
review | REUSE.md §5 |
9. Sign-off
| # | Check | How |
|---|---|---|
| 9.1 | python3 scripts/catalog_gates.py <app> — all green (exit 0) |
bench / scratch |
| 9.2 | A fresh install on 9202 from the drill catalog, as a household and as a stranger, start to finish | 9202 |
| 9.3 | Every item above done or n/a-with-reason; every finding that is not fixed is a register row | the record |
| 9.4 | Published to the live catalog in ONE commit with its record | the gate checks |