8cadacb553
gates / gates (push) Successful in 26s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
74 lines
6.2 KiB
Markdown
74 lines
6.2 KiB
Markdown
# 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.
|