# REPORT — 2026-09-29 afternoon: the setup gate on the other 30 apps; "Done" asks the app first; open sign-up closed after the first admin; claper's password never in code Architecture read first: `09` §3 decisions 45–47, `01-topology-and-trust.md` §5, `audits/login-gate-2026-09-29/B/B-VERDICT.md`, `app-catalog-felhom.eu/FIRST-ADMIN.md`, rows R-707, R-711, R-713. Controller **v0.281.0** (one release). Floor 0.281.0; both demo boxes run it. Catalog `6faf432`. Evidence: `documentation/audits/gate-rollout-2026-09-29/` (0 = Part 0, A, B = per-app, C = sign-up, D = claper, redproofs). ## The Parts | Part | Step | State | Note | |---|---|---|---| | — | decision 47 recorded first | done | `09` §3 + `01` §5 before any work | | 0 | installed app never gated by a catalog change | done — **holds** | 9202: vikunja installed ungated and set up, then the drill catalog added `setup_gate: true`, synced, two loop ticks, a controller restart: still answered strangers, no gate file, no record (`0/P0-1`, `P0-2`). Code: the gate is set only in `DeployStack`. Pinned by `TestSignupBlock_NeverOnAnAppThisBoxDidNotGate` (RP23). After the live push, the demo boxes' installed class-4 apps (adventurelog, docmost, opengist, romm; opengist) got no gate and no block (`0/P0-3`) | | A1 | "Done" asks the probe first | done | refuses while the app says not done and when it cannot be read (409 + the sentence). Live: zipline pressed before its setup → 409, twice (`A/`, `B/B2-zipline-probe-before.txt`) | | A2 | confirm for an app without a probe | done | in-page `felhomConfirm` with the sentence (native confirm is banned by a gate) | | A3 | tests + red-proofs | done | RP17, RP18 | | B | the gate on the other 30 | done — **28 gated (25 fully proven, 3 opening not provable here), 2 not gated** | per-app table below | | C1 | spike: how sign-up can be closed | done, **mechanism changed** | opengist, wishlist: only in their own admin settings; vikunja: an env, but then no way to add a user but its CLI. Chosen: a box-side block of the app's own sign-up address + a household 15-minute window (CC-unattended decision, `09` §3 decision 47) | | C2 | per app | done | 11 apps let a stranger sign up after the setup → blocked and refused; 11 more refuse by themselves; window proven (gitea, calcom, vikunja) and closes again (gitea, calcom) | | C3 | apps that cannot close sign-up | **1: wanderer** | not gated at all (R-714) — STATUS asks | | D | R-713 | done | code-bound values refused; `${NAME|base64}`; claper proven live with a typed password holding `"` and `#{` (default refused, typed signs in). RP24 | **Recommendation not followed, one line why (standing rule 4):** the ruling says "only the admin adds people, from the app's own user page"; for apps with no such page (opengist, vikunja, termix, sparkyfitness, adventurelog) the page says to open sign-up for 15 minutes instead — the only way a family member can join those apps at all. ### Part B / C — one row per app (9202, controller 0.281.0, drill catalog) | app | gate proven (stranger refused · household reached setup · opened · answered after) | opened by | sign-up after the setup | |---|---|---|---| | actualbudget | yes | probe `data.bootstrapped` (M) | refused by the app | | komga | yes | probe `isClaimed` (M) | refused by the app | | jellyfin | yes | probe `StartupWizardCompleted` (M) | refused by the app | | romm | yes | probe `SYSTEM.SHOW_SETUP_WIZARD` (M) | refused by the app | | zipline | yes | probe `/api/server/public firstSetup` (M) | refused by the app (`userRegistration: false`) | | termix | yes | probe `setup_required` (M) | **was open → blocked** | | emby | yes | button | refused by the app | | navidrome | yes | button (no JSON status) | refused by the app | | ghost | yes | button (status is a list — R-715) | refused by the app | | home-assistant | yes | button (status is a list — R-715) | refused by the app | | gitea | yes | button | **was open → blocked** | | docmost | yes | button | no public sign-up | | calcom | yes | button | **was open (a stranger's account was created) → blocked** | | tandoor | yes | button | "Sign Up Closed" by the app | | gramps-web | yes | button (its status answers 405 — R-715) | answered 500 → blocked anyway | | adventurelog | yes | button | **was open → blocked** | | homebox | yes | button | **was open → blocked** | | papra | yes | button | **was open → blocked** | | sparkyfitness | yes | button | **was open → blocked** | | vikunja | yes | button | **was open → blocked**; window let a family member in | | opengist | yes | button | **was open → blocked** | | wishlist | yes | button | **was open → blocked** | | radarr, sonarr | yes | button | single user; their API key was public BEFORE the setup — the gate hides it | | recipe-importer | yes | button | our own app, open until a password is set — the confirm says so | | seerr | stranger refused, household reached setup | **opening not proven** (needs a media server) | not measured | | outline | stranger refused, household reached setup | **opening not proven** (needs e-mail / SSO) | not measured | | rallly | stranger refused, household reached setup | **opening not proven** (e-mail) | not measured | | wanderer | **not gated** | — | open (R-714) | | plant-it | not installable (`lifecycle: abandoned`); the template carries the gate | — | — | ## Claims in the brief that turned out wrong (or right), named - **"A catalog change never gates an installed app"** — **right** (measured on 9202 and read on both demo boxes). - **"14 of the 34 have a probe"** — **wrong**: 9 have a probe that works (3 from the morning, 6 today); 3 more have a status the box cannot read (ghost, home-assistant — lists; gramps-web — 405, R-715); zipline's upstream route was wrong (`/api/setup` answers 403 after the setup; `/api/server/public` works). - **"The first admin is the first registered user on opengist and wishlist"** — **right** (measured: the household's sign-up became the admin; after it, a stranger could still sign up). - **"Each app in R-711's list can close sign-up"** — **wrong** as a per-app switch: opengist and wishlist keep it only in their admin settings, vikunja only as a start-up env; and wanderer cannot be closed at all today. The box-side block closes all but wanderer. - **"An env switch needs a restart"** — **right** for vikunja (read at start); not used — the block needs no restart. - **"Register 346 rows"** — was **350** at the start of this session (the morning session ended at 350). ## Also found - A probe that never flips blocks the household's press (fail closed; measured on gramps-web) → R-715. - calcom created a stranger's account after the setup (measured) — closed. - Apps installed before today keep their open sign-up (demo boxes only) → R-716, needs an operator word. ## Rows Closed: R-707, R-711, R-713. Opened: R-714 (wanderer), R-715 (probe shapes), R-716 (installed apps' sign-up, operator). **Register 350 → 353 rows.** ## Teardown Machines: 9202 — every test app removed through the product (the scratch drive keeps some app folders, R-442's refusal as before); no gate or block file left; back on the live catalog; the drill catalog reset to live `main`. Demo boxes — read only (plus the floor). Host: nothing. Hub: floor 0.281.0. ep0: untouched.