Files
app-catalog-felhom.eu/NEW-APP-CHECKLIST.md
T
2026-10-01 14:48:25 +02:00

119 lines
9.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 |