# REPORT — 2026-09-29 evening: sign-up locked twice; "close sign-up now"; wanderer closable; three more probes Architecture read first: `09` §3 decisions 45–49, `01-topology-and-trust.md` §5, `audits/gate-rollout-2026-09-29/`, rows R-714, R-715, R-716. Controller **v0.282.0** (one release), floor 0.282.0, both demo boxes on it. Catalog `6446197`. Evidence: `documentation/audits/signup-lock-2026-09-29/` (A own switch, B tricks, C demo boxes, D wanderer, E probes, redproofs). ## The Parts | Part | Step | State | Note | |---|---|---|---| | — | decisions 48, 49 recorded first | done | `09` §3 | | A1 | own switch per app (spike) | done | 9 of 11 have an env switch (below); opengist, wishlist only in their database (R-717) | | A1 | "can be set only after the first admin" | measured | 6 of 9 refuse the household's own first account while on; 3 (calcom, gitea, gramps-web) do not | | A2 | `after_setup:` (controller) | done | env merged + one `compose up -d` when the gate opens; command form with after_install's argv rules; an old compose reported, not faked; retries every 30 min at most | | A2 | live | done | all 9 apps: record `ok`, running container carries the switch, a stranger straight at the app refused | | A3 | the window with an own switch | done — **lift and restore** | homebox: the window turned the switch off (one restart), a family member joined; after 15 min both locks back, a stranger refused through the web and straight at the app | | B | trick table | done, **changed** | termix's router ignores case: `/users/CREATE` got past the old prefix block (its own switch refused it). All blocks now `PathRegexp((?i)…)`, slash-tolerant. Final: 113 tries on 11 apps, 0 got in, 106 refused by the block, 7 were vikunja's plain HTML page (its API blocked) | | B | login / reset / sharing | done | household sign-in on 7 apps; password reset and share routes reach the apps, not the block | | C | "close sign-up now" (controller) | done | lock record `opened_by: close-signup`, block, own switch; offered once; never a gate | | C3 | demo boxes | done, **changed** | pressed on demo-hp adventurelog + opengist, demo-felhom opengist. "Before" proven WITHOUT making an account (the app answered an invalid sign-up with its own validation) — the apps' admin passwords are the operator's, so a test account could not be deleted through their admin pages. After: refused; login pages answer. adventurelog's backend restarted once (~30 s) for its own switch, same images (R-718: the card does not say so) | | D | wanderer | done, **changed** | no gate: one DB URL for server and browser (measured), and no first-admin screen for a stranger (PocketBase's installer needs the log link). Closed by "close sign-up now": two holes found and closed (collection id `_pb_users_auth_`, collection name in capitals); proven on 9202 | | E1 | probe reads lists / a done status | done | RP29, RP30 | | E2 | ghost, home-assistant, gramps-web | done | before/after measured on fresh installs; each gate opened by itself within seconds of the setup; a press before the setup refused on all three | | E3 | catalog gate `probe-measured` | done | 5 decoys seen failing; it caught immich/n8n/audiobookshelf (their note sat one block too high) | ### Part A / B — one row per app | app | own switch | blocks the household's first account? | own switch live after the gate | tricks (8 shapes per route) | |---|---|---|---|---| | adventurelog | `DISABLE_REGISTRATION` | yes | on; `is_disabled: true` | 0 in | | calcom | `NEXT_PUBLIC_DISABLE_SIGNUP` | no | on; "Signup is disabled" | 0 in | | gitea | `GITEA__service__DISABLE_REGISTRATION` | no | on; "Registration is disabled" | 0 in | | gramps-web | `GRAMPSWEB_REGISTRATION_DISABLED` | no | on; 405 "Registration is disabled" | 0 in | | homebox | `HBOX_OPTIONS_ALLOW_REGISTRATION` | yes | on; "user registration disabled" | 0 in | | papra | `AUTH_IS_REGISTRATION_ENABLED` | yes | on | 0 in | | sparkyfitness | `SPARKY_FITNESS_DISABLE_SIGNUP` | yes | on | 0 in | | termix | `ALLOW_REGISTRATION` | yes | on — and it caught the case hole | 0 in | | vikunja | `VIKUNJA_SERVICE_ENABLEREGISTRATION` | yes | on | 0 in | | opengist | none reachable (DB) | — | block only | 0 in | | wishlist | none reachable (DB) | — | block only | 0 in | | wanderer | `PUBLIC_DISABLE_SIGNUP` (web only) | — | on after the press | 0 in after the fix (2 holes before) | ## Claims in the brief that turned out wrong (or right), named - **The three env names given from memory** — **right**: gitea `GITEA__service__DISABLE_REGISTRATION` (through its env-to-ini), homebox `HBOX_OPTIONS_ALLOW_REGISTRATION`, papra `AUTH_IS_REGISTRATION_ENABLED` — each measured working. - **"A native setting can be set only after the first admin exists"** — **true for 6 of 9**, **wrong for 3** (calcom, gitea, gramps-web make their first admin by another route). - **"The address block can be passed by case or encoding tricks"** — **right for case, wrong for encoding**: termix (router ignores case) and PocketBase (collection name in any case, and by id) got past the old blocks; percent-encoding, double slashes, trailing slashes and query strings never did (traefik decodes and cleans before matching). - **"wanderer separates its internal and public database URL"** — **wrong**: one `PUBLIC_POCKETBASE_URL` for both. - **"gramps-web answers 405 only after its setup"** — **right**: 200 with an owner token before, 405 after. ## Also found - The scratch box's Docker disk was full of old images (1 GB free) — the install check refused correctly; 44 unused images removed by name (no prune). - `test_gate_decoys.py` had stopped running any case after docmost moved to PostgreSQL 18 (a typed "16"); fixed to read it. - **My own slip:** I restarted the scratch box's controller while a removal job was running; one removal was cut off (502). Re-run; nothing left behind. ## Rows Closed: R-714, R-715, R-716. Opened: R-717 (opengist/wishlist own switch in their DB), R-718 (the close card should say the app restarts). **Register 353 → 355 rows.** ## Teardown Machines: 9202 — every test app removed through the product; no gate or block file left; back on the live catalog; the drill catalog reset. Demo boxes — the floor, and the three "close sign-up now" presses (ruled). Host: nothing. Hub: floor 0.282.0. ep0: untouched.