Files
app-catalog-felhom.eu/FIRST-ADMIN.md
T
admin b7f0f7cef5
gates / gates (push) Failing after 14m4s
Grocy and LubeLogger: two new apps, each with its complete record
60 template directories, 58 offered. Records: onboarding/grocy.md and
onboarding/lubelogger.md, all 61 checks answered, none open. Evidence:
felhom.eu/documentation/audits/new-apps-2026-10-10/.

Grocy 4.7.1 from lscr.io/linuxserver/grocy (grocy publishes no image of its
own). SQLite in one volume, no HDD, ~30 MiB idle, amd64 + arm64, Hungarian UI
91.6 %. First admin class 3: it starts with its documented admin/admin and
after_install replaces that password with a generated one.

LubeLogger v1.7.3 from ghcr.io/hargata/lubelogger. LiteDB in one volume, no
HDD, ~60 MiB idle, amd64 + arm64. First admin class 1 — and it has to be: the
image ships EnableAuth=false, and with that the middleware mints a ticket with
the IsRootUser role for every visitor. Measured on a default start: a stranger
got 200 on /, on /api/vehicles and on /Home/Settings and CREATED A VEHICLE.
The compose entrypoint exports EnableAuth=true and the SHA-256 of a generated
name and password, so the app's own login is on before its first byte (at t+1 s
nothing listening, at t+2 s /api/vehicles already 401).

Two defects found by the walk and fixed before publishing:

1. An after_install command may not contain `$`. The controller runs every
   element through os.Expand and refuses one naming anything outside env:, so
   PHP cannot be inlined. On 9202 the first attempt came back
   `[pw argv dsn db i t e s n q h] not declared in env or has no value — not
   run` and the app sat behind its install hold with admin/admin in place. The
   code now lives in a file the compose entrypoint writes. Written into
   REUSE.md's after_install row as a trap.

2. Grocy's persisted config.php does not follow the image. The image copies
   config-dist.php only when that file is absent, so a volume written by 4.6.0
   and started under 4.7.1 answered HTTP 500 on every page — AUTH_CLASS names a
   class 4.7 moved — while its log said migrations done. The entrypoint now
   deletes the file at every start. The 4.6.0 -> 4.7.1 edge failed before this
   and is proven after it, on both venues.

Ladders, written by upgrade-test.py --write-ladder from both verdicts:
  grocy       4.6.0 -> 4.7.1   bench proven, box proven (guarded Update, 41 s)
  lubelogger  v1.7.2 -> v1.7.3 bench proven, box proven (25.6 s)
Checklist 6.3 came from a real failure, not a forced one: before the config.php
fix the product undid the same step in 346 s with the data intact.

REUSE.md: a fifth healthcheck family (bash /dev/tcp) for an image with no HTTP
client at all, measured in both directions.

Tool fixes made on the way: check-onboarding.py crashed on a Windows console
while printing which ids were open; upgrade-test.py's read_text/write_text used
the platform encoding and wrote a cp1250 em dash into a template full of
Hungarian.
2026-10-10 12:31:34 +02:00

125 lines
29 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.
# FIRST-ADMIN — how each app gets its first admin account (the audit behind `09` §3 decision 45)
**The rule (operator, 2026-09-28): an app is never published with a login a stranger knows.** The box publishes every
app on the household's domain (`*.domain` through the tunnel). Where the box can set the first admin password, it
generates one at install and shows it on the app page. Where it cannot, the app stays in the catalog, and the install
dialog and the app page say what the default login is and to change it at once.
**Mechanisms (controller ≥ 0.279.0; the gate ≥ 0.280.0):**
- `after_install:` in `.felhom.yml` — one command in the app's own container after a FRESH install, with generated
deploy values filled in; `success:` marker required. Never after a restore or a kept-data load. (`internal/stacks/after_install.go`)
- The page's default-login rule — `app_info.default_creds` is shown, with the sentence „Ez az alkalmazás egy ismert,
közös jelszóval indul: %s. Telepítés után azonnal változtasd meg." / "This app starts with a known, shared password:
%s. Change it right after the install.", while that login is in effect; hidden once `after_install` replaced it.
(`internal/web/known_login.go`)
- The setup gate (`09` §3 decision 46, controller ≥ 0.280.0) — `setup_gate: true` (+ optional `setup_done_probe:`): a
fresh install of an open-first-run app is closed to everyone but the household until the app's own status says an
admin exists, or the household presses „Kész, beállítottam". Spike and verdict:
`felhom.eu/documentation/audits/login-gate-2026-09-29/B/B-VERDICT.md` (14 of 34 class-4 apps have a probe — 2
measured, 12 upstream; 20 need the button). **It does not close OPEN SIGN-UP after the setup** (R-711).
- „Megváltoztattam" / "I changed it" (controller ≥ 0.280.0, R-710) — the household's word under a known default login.
**Classes:** 1 generated by us at install · 2 set by the household in our dialog · 3 a hard-coded default · 4 an open
first-run screen (the first visitor creates the admin) · 5 no login by design · 6 unknown.
**Sources:** **M** = measured on a box (named) · **R** = read in this catalog · **U** = upstream, read or remembered,
NOT measured. Every U must be measured before a fix is built on it.
**Status 2026-09-29 (afternoon):** every class-3 app fixed (5); **32 of 34 class-4 apps gated** (controller ≥ 0.281.0) —
9 open by the app's own status (probe measured before and after on 9202), the rest by the household's press, which
asks the probe first where there is one. Open sign-up is closed by the box after the setup on 11 apps (decision 47);
11 more refuse a stranger by themselves. **Not gated:** wanderer (R-714, needs a design); plant-it is not installable.
**Not provable on 9202:** the opening of seerr (media server), outline and rallly (e-mail).
| app | class | how the first admin exists | fix route | status | source |
|---|---|---|---|---|---|
| **actualbudget** | 4 | first visitor sets the server password | **setup gate**, opened by probe `/account/needs-bootstrap` → `data.bootstrapped`; the app itself refuses a stranger's second first-admin / sign-up call | **GATED** — catalog, 2026-09-29 (decisions 46–47) | **M 9202**: stranger → gate page / 401; household reached the first-setup screen; gate opened after the setup; the app answered after (`felhom.eu/documentation/audits/gate-rollout-2026-09-29/`) |
| **adventurelog** | 4 | open sign-up | **setup gate**, opened by the household's press („Kész, beállítottam", confirm first); sign-up closed by the box after the setup: `/signup` + allauth signup | **GATED** — catalog, 2026-09-29 (decisions 46–47) | **M 9202**: stranger → gate page / 401; household reached the first-setup screen; gate opened after the setup; the app answered after (`felhom.eu/documentation/audits/gate-rollout-2026-09-29/`) |
| **audiobookshelf** | 4 | first visitor creates root | **setup gate**, probe `GET /status` → `isInit` | **GATED** — catalog, 2026-09-29 | **M 9202**: as immich (the probe, read from upstream, measured: opened ~20 s after `POST /init`); its app's login 200 after |
| bentopdf | 5 | browser-only PDF tool, no accounts | – | fine | R |
| **bookstack** | 3 | `admin@admin.com / password` | (b) `artisan bookstack:create-admin --initial` | **FIXED** — catalog, decision 45 | **M 9202**: default fails, generated works; **M demo-hp**: default still works on the installed app (unchanged, page warns) |
| **calcom** | 4 | first visitor becomes admin (`/api/auth/setup`) | **setup gate**, opened by the household's press („Kész, beállítottam", confirm first); sign-up closed by the box after the setup: `/signup`, `/auth/signup`, `/api/auth/signup` | **GATED** — catalog, 2026-09-29 (decisions 46–47) | **M 9202**: stranger → gate page / 401; household reached the first-setup screen; gate opened after the setup; the app answered after (`felhom.eu/documentation/audits/gate-rollout-2026-09-29/`) |
| **calibre-web** | 3 | `admin / admin123` | (b) as `abc`: rename `admin` in `app.db` to the generated `ADMIN_USER` (`secret`, `hex:5`), then `cps.py -p /config/app.db -s <name>:<pw>`, then prove the hash and that no `admin` is left; `generate: password:24:special` (controller ≥ 0.280.0) | **FIXED** — catalog, 2026-09-29; **the name generated too** 2026-10-01 (`09` §3 decision 61, R-752: Calibre-Web locks a NAME for a day after 40 wrong tries) | **M 9202** (2026-10-01): fresh install — the generated name + password sign in (form and OPDS), `admin` refused, 40 wrong tries on `admin` and the household still in at once; **M demo-hp**: the installed app renamed by hand the same way, the name in the operator's credentials file |
| **claper** | 3 (+ open sign-up) | seeds `admin@claper.co / claper` | (b) `bin/claper rpc … update_user_password` | **FIXED** — catalog, decision 45 | **M 9202**: default fails, generated works, a restore keeps it |
| code-server | 1 | `PASSWORD` generated, applied every start | – | fine | R |
| crafty-controller | 1 | `CRAFTY_PASSWORD` → default.json | – | fine | R |
| **dawarich** | 3 | `rails db:seed` creates `demo@dawarich.app` / `safepassword` | (b) `bin/rails runner` sets the generated password (ARGV); the install hold keeps strangers out until it succeeds | **NEW 2026-10-01** — catalog, `onboarding/dawarich.md` | **M 9202** (drill catalog): the first public answer 2 s after the password was replaced; default refused, generated signs in (303), wrong refused; registration off (302/422); 10 wrong tries lock the public name for 1 h (Devise; per-minute throttles first) — the mealie precedent (decision 57), the first step tells the household to set its own e-mail; the phone API key keeps working (`felhom.eu/documentation/audits/new-apps-2026-10-01/box/dawarich/`) |
| **docmost** | 4 | first registered user is admin | **setup gate**, opened by the household's press („Kész, beállítottam", confirm first); the app itself refuses a stranger's second first-admin / sign-up call; no JSON status before/after → button | **GATED** — catalog, 2026-09-29 (decisions 46–47) | **M 9202**: stranger → gate page / 401; household reached the first-setup screen; gate opened after the setup; the app answered after (`felhom.eu/documentation/audits/gate-rollout-2026-09-29/`) |
| **emby** | 4 | setup wizard | **setup gate**, opened by the household's press („Kész, beállítottam", confirm first); the app itself refuses a stranger's second first-admin / sign-up call | **GATED** — catalog, 2026-09-29 (decisions 46–47) | **M 9202**: stranger → gate page / 401; household reached the first-setup screen; gate opened after the setup; the app answered after (`felhom.eu/documentation/audits/gate-rollout-2026-09-29/`) |
| **ghost** | 4 | `/ghost/` setup | **setup gate**, opened by the household's press („Kész, beállítottam", confirm first); the app itself refuses a stranger's second first-admin / sign-up call; its status exists but is a list (`setup[0].status`) — the box reads only objects → button | **GATED** — catalog, 2026-09-29 (decisions 46–47) | **M 9202**: stranger → gate page / 401; household reached the first-setup screen; gate opened after the setup; the app answered after (`felhom.eu/documentation/audits/gate-rollout-2026-09-29/`) |
| **gitea** | 4 | web installer open (no `INSTALL_LOCK`) | **setup gate**, opened by the household's press („Kész, beállítottam", confirm first); sign-up closed by the box after the setup: `/user/sign_up`; `/api/v1/version` is not JSON before the install → button | **GATED** — catalog, 2026-09-29 (decisions 46–47) | **M 9202**: stranger → gate page / 401; household reached the first-setup screen; gate opened after the setup; the app answered after (`felhom.eu/documentation/audits/gate-rollout-2026-09-29/`) |
| glance | 5 | config-file dashboard, no users | – | fine | R |
| gokapi | 1 | `GOKAPI_PASSWORD` before first serve | – | fine | R |
| grafana | 1 | `GF_SECURITY_ADMIN_PASSWORD` — **falls back to `admin` if empty** (R-708) | – | fine while the field is set | R |
| **gramps-web** | 4 | first-run onboarding | **setup gate**, opened by the household's press („Kész, beállítottam", confirm first); sign-up closed by the box after the setup: `/api/users/<name>/register/`; its status answers HTTP 405 after the setup — the box reads only 200 answers (measured: the press was then refused, fail closed) → button; self-registration answered 500, blocked anyway | **GATED** — catalog, 2026-09-29 (decisions 46–47) | **M 9202**: stranger → gate page / 401; household reached the first-setup screen; gate opened after the setup; the app answered after (`felhom.eu/documentation/audits/gate-rollout-2026-09-29/`) |
| **grimmory** | 4 | first visitor creates the admin (`POST /api/v1/setup`) | **setup gate**, probe `GET /api/v1/setup/status` → `data` (`false` → `true`), **and the family gate** for good (decision 64): strangers reach nothing but the e-reader exceptions, which keep Grimmory's own login | **NEW 2026-10-02** — catalog, `onboarding/grimmory.md` | **M 9202**: the household made the admin through both gates; a second setup 403; R-775's lock cannot be aimed from outside (`audits/family-gate-2026-10-02/A/items.txt`) |
| **grocy** | 3 | `admin` / `admin`, created by grocy's own first-request migration | (b) the box replaces the password at install: `after_install` runs a PHP helper the compose entrypoint writes, which hashes with grocy's OWN `password_hash(..., PASSWORD_ARGON2ID)` and PROVES the result with `password_verify` before saying so. The code cannot live in the command itself — the controller expands every `$name` in an after_install command — and grocy's image has no other interpreter | **NEW 2026-10-10** — catalog, `onboarding/grocy.md` | **M 9202** (drill catalog): the documented default POSTed once a second as a stranger from before the install press, 65 attempts, **0 signed in** (404, then 401 behind the install hold, then `invalid=true`); after the install the default is refused, the generated password signs in, a wrong one is refused; 25 wrong tries then the household in at once (no lock); a family member added through `POST /api/users` signs in (`felhom.eu/documentation/audits/new-apps-2026-10-10/box/grocy/`) |
| **home-assistant** | 4 | onboarding | **setup gate**, opened by the household's press („Kész, beállítottam", confirm first); the app itself refuses a stranger's second first-admin / sign-up call; its status is a list (`/api/onboarding`) → button | **GATED** — catalog, 2026-09-29 (decisions 46–47) | **M 9202**: stranger → gate page / 401; household reached the first-setup screen; gate opened after the setup; the app answered after (`felhom.eu/documentation/audits/gate-rollout-2026-09-29/`) |
| **homebox** | 4 | open registration | **setup gate**, opened by the household's press („Kész, beállítottam", confirm first); sign-up closed by the box after the setup: `/api/v1/users/register` | **GATED** — catalog, 2026-09-29 (decisions 46–47) | **M 9202**: stranger → gate page / 401; household reached the first-setup screen; gate opened after the setup; the app answered after (`felhom.eu/documentation/audits/gate-rollout-2026-09-29/`) |
| homepage | 5 | static start page | – | fine | R |
| **immich** | 4 | first visitor admin sign-up | **setup gate**, probe `GET /api/server/config` → `isInitialized` | **GATED** — catalog, 2026-09-29 (decision 46) | **M 9202**: stranger 302→gate page / 401 during and after the install; household in 0.2 s; probe opened it 0–20 s after the sign-up; phone-app API (Bearer) 200 after (`felhom.eu/documentation/audits/login-gate-2026-09-29/C/`) |
| **jellyfin** | 4 | startup wizard | **setup gate**, opened by probe `/System/Info/Public` → `StartupWizardCompleted`; the app itself refuses a stranger's second first-admin / sign-up call | **GATED** — catalog, 2026-09-29 (decisions 46–47) | **M 9202**: stranger → gate page / 401; household reached the first-setup screen; gate opened after the setup; the app answered after (`felhom.eu/documentation/audits/gate-rollout-2026-09-29/`) |
| **karakeep** | 4 | the first account that signs up becomes the admin (`users.create`) | **setup gate**, opened by the household's press („Kész, beállítottam"); sign-up closed twice after the setup: `after_setup` → `DISABLE_SIGNUPS=true` (it also refuses the FIRST account, so never at install) + a case-insensitive block of `/signup` and any tRPC call naming `users.create` (batched too) | **NEW 2026-10-01** — catalog, `onboarding/karakeep.md` | **M 9202** (drill catalog): stranger 401 before the press; after it users.create / batched / upper-case / `//` / `/signup` all 403; 12 wrong key exchanges → the right one at once; after remove + restore the app's own switch still refuses, the route block is gone (R-773) (`felhom.eu/documentation/audits/new-apps-2026-10-01/box/karakeep/`) |
| kimai | 1 | `ADMIN_PASSWORD` → `ADMINPASS` | – | fine | R |
| **komga** | 4 | first visitor claims | **setup gate**, opened by probe `/api/v1/claim` → `isClaimed`; the app itself refuses a stranger's second first-admin / sign-up call | **GATED** — catalog, 2026-09-29 (decisions 46–47) | **M 9202**: stranger → gate page / 401; household reached the first-setup screen; gate opened after the setup; the app answered after (`felhom.eu/documentation/audits/gate-rollout-2026-09-29/`) |
| **lubelogger** | 1 | the image ships **NO LOGIN AT ALL** (`EnableAuth: false`) and the middleware mints a ticket with the `IsRootUser` role for every visitor | the compose entrypoint exports `EnableAuth=true` and the SHA-256 of the generated name and password (`StaticHelper.GetHash`), so the app's own login is on **before its first byte**; nothing to run after the install | **NEW 2026-10-10** — catalog, `onboarding/lubelogger.md` | **M bench + M 9202**: on a DEFAULT start a stranger got 200 on `/`, on `/api/vehicles` and on `/Home/Settings` and **created a vehicle**; with the template, the first answer the app ever gave a stranger was 401 / 302 → `/Login/Index`. Sign-up is closed by the app itself (`Invalid Token`); 25 wrong passwords then the household in at once (`felhom.eu/documentation/audits/new-apps-2026-10-10/box/lubelogger/`) |
| **mealie** | 3 | `changeme@example.com / MyPassword` | (b) its own user repository (`update_password`), password as `sys.argv[1]` | **FIXED** — catalog, 2026-09-29 | **M 9202**: fresh install — default 401, generated 200, wrong 401 (`felhom.eu/documentation/audits/login-gate-2026-09-29/D/`) |
| **metube** | 5 | NO login at all, by design | **the family gate** only (decision 64), no exceptions | **NEW 2026-10-02** — catalog, `onboarding/metube.md` | **M 9202**: a stranger's every path, the websocket included, 401/302 from the gate (`audits/family-gate-2026-10-02/A/items.txt`) |
| **n8n** | 4 | owner setup | **setup gate**, probe `GET /rest/settings` → `data.userManagement.showSetupOnFirstLoad` = false | **GATED** — catalog, 2026-09-29 | **M 9202**: as immich; opened by the probe ~20 s after the owner setup |
| **navidrome** | 4 | first user is admin | **setup gate**, opened by the household's press („Kész, beállítottam", confirm first); the app itself refuses a stranger's second first-admin / sign-up call | **GATED** — catalog, 2026-09-29 (decisions 46–47) | **M 9202**: stranger → gate page / 401; household reached the first-setup screen; gate opened after the setup; the app answered after (`felhom.eu/documentation/audits/gate-rollout-2026-09-29/`) |
| nextcloud | 1 | `NEXTCLOUD_ADMIN_PASSWORD` → auto-install | – | fine | R; **M** demo-hp 2026-09-28 |
| onlyoffice | 5 | JWT-protected API, no login screen | – | fine | R |
| **opengist** | 4 | first registered user is admin | **setup gate**, opened by the household's press („Kész, beállítottam", confirm first); sign-up closed by the box after the setup: `/-/register` | **GATED** — catalog, 2026-09-29 (decisions 46–47) | **M 9202**: stranger → gate page / 401; household reached the first-setup screen; gate opened after the setup; the app answered after (`felhom.eu/documentation/audits/gate-rollout-2026-09-29/`) |
| **outline** | 4 | e-mail / magic-link onboarding | **setup gate**, the household's press; needs e-mail or SSO to sign anyone in — not provable on 9202 | **GATED** — catalog, 2026-09-29; the opening NOT proven | **M 9202**: stranger → gate page / 401, household reached the first-setup screen (`felhom.eu/documentation/audits/gate-rollout-2026-09-29/`) |
| paperless-ngx | 1 | `PAPERLESS_ADMIN_PASSWORD` | – | fine | R |
| **papra** | 4 | open e-mail sign-up | **setup gate**, opened by the household's press („Kész, beállítottam", confirm first); sign-up closed by the box after the setup: `/api/auth/sign-up` | **GATED** — catalog, 2026-09-29 (decisions 46–47) | **M 9202**: stranger → gate page / 401; household reached the first-setup screen; gate opened after the setup; the app answered after (`felhom.eu/documentation/audits/gate-rollout-2026-09-29/`) |
| plant-it | 4 | open, `USERS_LIMIT=-1` | `setup_gate: true` in the template; the catalog marks it NOT installable (`lifecycle: abandoned`) — nothing to prove | not installable | R |
| plex | 2 | the household's plex.tv claim token | – | fine | R |
| privatebin | 5 | anonymous pastes by design | – | fine | R |
| **radarr** | 4 | first visitor sets auth | **setup gate**, opened by the household's press („Kész, beállítottam", confirm first); no sign-up to close (single user / no accounts); `/initialize.json` hands anyone the API key BEFORE the setup — the gate hides it; after, it needs a login | **GATED** — catalog, 2026-09-29 (decisions 46–47) | **M 9202**: stranger → gate page / 401; household reached the first-setup screen; gate opened after the setup; the app answered after (`felhom.eu/documentation/audits/gate-rollout-2026-09-29/`) |
| **radicale** | 1 | the box generates the password at install; the container writes Radicale's login file from it on the FIRST start only (Radicale 3 ships with `auth = denyall`) | — (no first-run screen, no sign-up route) | **NEW 2026-10-01** — catalog, onboarding record `onboarding/radicale.md` | **M 9202** (drill catalog): no login 401 from the first answer; wrong 401; right 207; 15 wrong tries → right at once (no lock, ~1.3 s delay each); remove + restore → the household's login comes back with the data (`felhom.eu/documentation/audits/new-apps-2026-10-01/box/radicale/`) |
| **rallly** | 4 | magic link to any e-mail | **setup gate**, the household's press; magic link by e-mail — not provable on 9202 | **GATED** — catalog, 2026-09-29; the opening NOT proven | **M 9202**: stranger → gate page / 401, household reached the first-setup screen (`felhom.eu/documentation/audits/gate-rollout-2026-09-29/`) |
| **recipe-importer** | 4 | our own image, open until set | **setup gate**, opened by the household's press („Kész, beállítottam", confirm first); no sign-up to close (single user / no accounts); our own app: open until a password is set in its settings — the confirm says so | **GATED** — catalog, 2026-09-29 (decisions 46–47) | **M 9202**: stranger → gate page / 401; household reached the first-setup screen; gate opened after the setup; the app answered after (`felhom.eu/documentation/audits/gate-rollout-2026-09-29/`) |
| **romm** | 4 | first unauthenticated `POST /api/users` creates the user (harness); the stale `admin / admin` note is **removed** (2026-09-29) | **setup gate**, opened by probe `/api/heartbeat` → `SYSTEM.SHOW_SETUP_WIZARD` = false; the app itself refuses a stranger's second first-admin / sign-up call | **GATED** — catalog, 2026-09-29 (decisions 46–47) | **M 9202**: stranger → gate page / 401; household reached the first-setup screen; gate opened after the setup; the app answered after (`felhom.eu/documentation/audits/gate-rollout-2026-09-29/`) |
| **seerr** | 4 | setup wizard (needs a media server) | **setup gate**, the household's press; needs a media server to finish its setup — not provable on 9202 (its `initialized` status measured false before) | **GATED** — catalog, 2026-09-29; the opening NOT proven | **M 9202**: stranger → gate page / 401, household reached the first-setup screen (`felhom.eu/documentation/audits/gate-rollout-2026-09-29/`) |
| **sonarr** | 4 | as radarr | **setup gate**, opened by the household's press („Kész, beállítottam", confirm first); no sign-up to close (single user / no accounts); as radarr | **GATED** — catalog, 2026-09-29 (decisions 46–47) | **M 9202**: stranger → gate page / 401; household reached the first-setup screen; gate opened after the setup; the app answered after (`felhom.eu/documentation/audits/gate-rollout-2026-09-29/`) |
| **sparkyfitness** | 4 | open registration | **setup gate**, opened by the household's press („Kész, beállítottam", confirm first); sign-up closed by the box after the setup: `/api/auth/sign-up` | **GATED** — catalog, 2026-09-29 (decisions 46–47) | **M 9202**: stranger → gate page / 401; household reached the first-setup screen; gate opened after the setup; the app answered after (`felhom.eu/documentation/audits/gate-rollout-2026-09-29/`) |
| **tandoor** | 4 | setup page while no users | **setup gate**, opened by the household's press („Kész, beállítottam", confirm first); the app itself refuses a stranger's second first-admin / sign-up call | **GATED** — catalog, 2026-09-29 (decisions 46–47) | **M 9202**: stranger → gate page / 401; household reached the first-setup screen; gate opened after the setup; the app answered after (`felhom.eu/documentation/audits/gate-rollout-2026-09-29/`) |
| **termix** | 4 | first registered user is admin | **setup gate**, opened by probe `/users/setup-required` → `setup_required` = false; sign-up closed by the box after the setup: `/users/create` | **GATED** — catalog, 2026-09-29 (decisions 46–47) | **M 9202**: stranger → gate page / 401; household reached the first-setup screen; gate opened after the setup; the app answered after (`felhom.eu/documentation/audits/gate-rollout-2026-09-29/`) |
| **uptime-kuma** | 4 | first visitor creates admin (socket.io `setup`) | **setup gate**, no HTTP probe → the household's „Kész, beállítottam" | **GATED** — catalog, 2026-09-29 | **M 9202**: gate held across a controller restart; the press opened it (second press 409). The proof pressed WITHOUT doing the socket.io setup — the press trusts the household |
| vaultwarden | 1 | `ADMIN_TOKEN` generated; invite-only (R-512) | – | fine | R |
| **vikunja** | 4 | open registration | **setup gate**, opened by the household's press („Kész, beállítottam", confirm first); sign-up closed by the box after the setup: `/api/v1/register` | **GATED** — catalog, 2026-09-29 (decisions 46–47) | **M 9202**: stranger → gate page / 401; household reached the first-setup screen; gate opened after the setup; the app answered after (`felhom.eu/documentation/audits/gate-rollout-2026-09-29/`) |
| **wanderer** | 4 | `PUBLIC_DISABLE_SIGNUP=false` | NOT gated (its web server calls its own database through the public name); no first-admin screen to take (PocketBase's superuser installer needs the one-time link from its log); sign-up closed by the household's „Close sign-up now” after its account (decision 48) | **LOCKED on the household's press** — catalog, 2026-09-29 evening | **M 9202**: before, a stranger made an account through PocketBase; after the press, every sign-up shape refused (`felhom.eu/documentation/audits/signup-lock-2026-09-29/D/`) |
| **wger** | 3 | `admin / adminadmin` | (b) Django `set_password`, password as `sys.argv[1]` | **FIXED** — catalog, 2026-09-29; + R-712 (a browser's https Origin was refused by CSRF) | **M 9202**: fresh install — default refused, generated signs in (302), wrong refused, with the browser's https Origin |
| **wishlist** | 4 | first sign-up is admin | **setup gate**, opened by the household's press („Kész, beállítottam", confirm first); sign-up closed by the box after the setup: `/signup` | **GATED** — catalog, 2026-09-29 (decisions 46–47) | **M 9202**: stranger → gate page / 401; household reached the first-setup screen; gate opened after the setup; the app answered after (`felhom.eu/documentation/audits/gate-rollout-2026-09-29/`) |
| **zipline** | 4 | v4 sets up on first run; the stale `admin / zipline` note is **removed** (2026-09-29) | **setup gate**, opened by probe `/api/server/public` → `firstSetup` = false; the app itself refuses a stranger's second first-admin / sign-up call | **GATED** — catalog, 2026-09-29 (decisions 46–47) | **M 9202**: stranger → gate page / 401; household reached the first-setup screen; gate opened after the setup; the app answered after (`felhom.eu/documentation/audits/gate-rollout-2026-09-29/`) |
## Sign-up locks after the first admin (decision 47; controller ≥ 0.282.0) — measured on 9202, 2026-09-29 evening
Two locks per app where the app has its own switch: the app's own setting (set by `after_setup` when the gate opens) and
the address block (`signup_block`, case-insensitive). Tricks: each sign-up route tried in 8 shapes (as written, trailing
slash, UPPER, Mixed, last part UPPER, one letter percent-encoded, double slash, query string), through the public route.
Evidence: `felhom.eu/documentation/audits/signup-lock-2026-09-29/`.
| app | its own switch (env) | that switch also refuses the household's FIRST account? | address block (routes) | tricks (final run) |
|---|---|---|---|---|
| adventurelog | `DISABLE_REGISTRATION` | yes | `/signup`, allauth API, `/accounts/signup` | all refused (block) |
| calcom | `NEXT_PUBLIC_DISABLE_SIGNUP` (read at runtime; invites with a token still work) | no | `/signup`, `/auth/signup`, `/api/auth/signup` | all refused |
| gitea | `GITEA__service__DISABLE_REGISTRATION` (env-to-ini at every start) | no (the installer makes the admin) | `/user/sign_up` | all refused |
| gramps-web | `GRAMPSWEB_REGISTRATION_DISABLED` | no (create_owner) | `/api/users/<name>/register/` | all refused |
| homebox | `HBOX_OPTIONS_ALLOW_REGISTRATION` | yes | `/api/v1/users/register` | all refused |
| papra | `AUTH_IS_REGISTRATION_ENABLED` | yes | `/api/auth/sign-up/*` | all refused |
| sparkyfitness | `SPARKY_FITNESS_DISABLE_SIGNUP` | yes | `/api/auth/sign-up/*` | all refused |
| termix | `ALLOW_REGISTRATION` | yes | `/users/create` — **its router ignores case**: `/users/CREATE` reached the sign-up before the block was made case-insensitive (its own switch refused it) | all refused |
| vikunja | `VIKUNJA_SERVICE_ENABLEREGISTRATION` | yes (its register route answers 404) | `/api/v1/register` | all refused (its `/register` PAGE is only HTML; the API is blocked) |
| karakeep | `DISABLE_SIGNUPS` | yes | `/signup`, tRPC `users.create` (any batch, any case) | all refused (2026-10-01) |
| opengist | none reachable (an admin setting in its database) | — | `/-/register` | all refused (block only) |
| wishlist | none reachable (`system_config.enableSignup` in its database) | — | `/signup` | all refused (block only) |
| wanderer | `PUBLIC_DISABLE_SIGNUP` (web only — PocketBase's API ignores it) | — | `/register` + PocketBase `POST /api/collections/(users\|_pb_users_auth_)/records`, any case — **the collection id and a case change both got in before the fix** | all refused; the batch API is off by the app |
The household's 15-minute window lifts both locks (the app restarts once) and the loop puts both back (measured on
homebox). "Close sign-up now" (decision 49) is offered on an app installed before the rule — and on wanderer, which is
not gated: the household makes its account, then presses it (measured on 9202).
**Counts (computed from the table, 2026-09-29 afternoon):** class 1: 8 · class 2: 1 · class 3: 5 · class 4: 34 · class 5: 5.
**Fixed: 5.** **Gated: 32** (3 of them without a proven opening). **Not gated: 2** (wanderer — its sign-up is locked by the household's press instead, decision 48; plant-it — not installable). Fine: 14.