Sign-up lock 2026-09-29 evening: decisions 48/49 outcomes, two locks, wanderer closable, R-714/715/716 closed, R-717/718 opened; STATUS, CONTEXT, report
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
This commit is contained in:
2026-09-29 17:19:43 +02:00
parent 5e97c44401
commit 8cadacb553
75 changed files with 1615 additions and 21 deletions
+5 -3
View File
@@ -825,9 +825,11 @@ class (an image `VOLUME` at an unmounted path) is still live — `immich-server`
| **R-711** | **[P2-MEDIUM] About a dozen class-4 apps keep open sign-up after their first admin exists — the setup gate (decision 46) does not close that.** FOUND 2026-09-29 by the gate spike (`audits/login-gate-2026-09-29/B/B-VERDICT.md` F3). The gate decides who becomes the admin; once it opens, a stranger can still make an ordinary account on adventurelog, homebox, papra, plant-it, sparkyfitness, vikunja, wanderer, rallly, opengist, wishlist, termix, docmost (READ from `app-catalog-felhom.eu/FIRST-ADMIN.md`, not measured). **Fix direction:** per app, route (a) — disable sign-up after the first user (env or the app's own setting), measured on 9202. **Built and proven (decision 47, controller v0.281.0, catalog `6faf432`):** a `signup_block:` per app, written when the gate opens (before the gate comes down), answered "sign-up is closed"; the household's 15-minute window. Measured on 9202 (`audits/gate-rollout-2026-09-29/`B, C): 11 apps let a stranger sign up after the setup (gitea, calcom, adventurelog, homebox, papra, sparkyfitness, vikunja, opengist, wishlist, termix; gramps-web 500) — all refused with the block, the apps still answered, the window let a family member in and closed again; 11 more refuse a stranger by themselves. wanderer → R-714. | **CLOSED — 2026-09-29** |
| **R-712** | **[P2-MEDIUM] wger refused every browser sign-in behind traefik: "CSRF verification failed".** MEASURED 2026-09-29 on 9202 (live catalog wger 2.6): a POST to `/en/user/login` with the browser's `Origin: https://…` answered 403 — Django saw the request as http (no trusted proxy header) and no `CSRF_TRUSTED_ORIGINS`. Found while proving R-707's wger route. **Fixed** (catalog `d0e7e2e`): `CSRF_TRUSTED_ORIGINS=https://${SUBDOMAIN}.${DOMAIN}` + `X_FORWARDED_PROTO_HEADER_SET=True`; proven on a fresh install: the generated password signs in (302) with the https Origin (`audits/login-gate-2026-09-29/D/D2-live.txt`). | **CLOSED — 2026-09-29** |
| **R-713** | **[P3-LOW] claper's `after_install` pastes the household's password into Elixir code, and the controller does not refuse a value that would break such code.** FOUND 2026-09-29 by a background security review of the drill commit (mealie/wger had the same shape and were changed to pass the password as `sys.argv[1]`). claper's `bin/claper rpc '… "${ADMIN_PASSWORD}" …'` has no argv: a household-typed password with `"` or `#{` breaks the command (recorded as failed; the page then warns) or changes the Elixir it runs — inside the household's own claper container, as that app. The generated value (letters + digits) is safe. **Fix direction:** (1) controller: `expandAfterInstall` refuses a value holding a quote, a backslash, `$`, `{`, `}`, a backtick or a newline — or a declared per-field encoding; (2) claper: read the value some other way (a file the command reads, or `System.get_env` from a one-shot env). **Fixed in controller v0.281.0** (RP24): a code-bound value holding a quote, backslash, `$`, `{`, `}`, backtick or line break is refused; `${NAME|base64}` is new. claper (catalog `6faf432`) decodes `Base.decode64!("${ADMIN_PASSWORD|base64}")`; proven live with a typed password holding `"` and `#{`: default refused, typed signs in (`audits/gate-rollout-2026-09-29/`D). | **CLOSED — 2026-09-29** |
| **R-714** | **[P2-MEDIUM] wanderer cannot be gated: its web part calls its own database host through the public name.** MEASURED 2026-09-29 on 9202: `PUBLIC_POCKETBASE_URL=https://${SUBDOMAIN_DB}.${DOMAIN}` is fetched by the web server itself; a gate on that host would refuse the web part (no gate cookie) and the household could not finish the setup. (On 9202 the name points to another box, so the app answered 500 either way.) Meanwhile wanderer keeps open sign-up (`PUBLIC_DISABLE_SIGNUP=false`) and PocketBase's own first-run screen on the second host. **Fix direction:** point the web part at PocketBase on the docker network (if wanderer separates the internal and public URL), then gate both hosts; or gate only the web host and close PocketBase's `/_/` installer with a block. | **OPEN — P2; owner: CC** |
| **R-715** | **[P3-LOW] The setup gate's probe reads only an HTTP-200 JSON object, so three apps with a real status get the button.** MEASURED 2026-09-29 on 9202: ghost (`{"setup":[{"status":…}]}` — a list), home-assistant (`/api/onboarding` — a top-level list), gramps-web (405 after the setup). And a probe that never flips BLOCKS the household's press (fail closed — measured on gramps-web while its check was still in the catalog): a wrong probe in a template would keep an app closed to everyone but the household until the catalog is fixed. **Fix direction:** list indexes in `field`, an optional `status:` to match, and a catalog gate that refuses a probe without a before/after measurement in its comment. | **OPEN — P3; owner: CC** |
| **R-716** | **[P3-LOW] Apps installed before controller 0.281.0 keep their open sign-up — decision 47 closes it only on apps whose gate the box opened.** READ 2026-09-29 on the demo boxes after catalog `6faf432` synced: demo-hp's adventurelog and opengist, demo-felhom's opengist carry `signup_block:` in their synced template and no gate record, so no block (`audits/gate-rollout-2026-09-29/0/P0-3-demo-boxes-after-push.txt`). This is Part 0's rule working as designed (a catalog change never touches an installed app). **Needs an operator word** before anything changes on an installed app: a one-time "close sign-up now" press on the app page for an installed app, or leave them. Only the demo boxes have such installs today. | **WAITING-ON-OPERATOR — P3; owner: operator** |
| **R-714** | **[P2-MEDIUM] wanderer cannot be gated: its web part calls its own database host through the public name.** MEASURED 2026-09-29 on 9202: `PUBLIC_POCKETBASE_URL=https://${SUBDOMAIN_DB}.${DOMAIN}` is fetched by the web server itself; a gate on that host would refuse the web part (no gate cookie) and the household could not finish the setup. (On 9202 the name points to another box, so the app answered 500 either way.) Meanwhile wanderer keeps open sign-up (`PUBLIC_DISABLE_SIGNUP=false`) and PocketBase's own first-run screen on the second host. **Fix direction:** point the web part at PocketBase on the docker network (if wanderer separates the internal and public URL), then gate both hosts; or gate only the web host and close PocketBase's `/_/` installer with a block. **Resolved without a gate (2026-09-29 evening, decision 48):** measured — no separate internal DB URL; no first-admin screen for a stranger (PocketBase's installer needs the log link); sign-up closed by the household's "Close sign-up now" (controller 0.282.0): case-insensitive block on `/register` + PocketBase `POST /api/collections/(users|_pb_users_auth_)/records` (the id and `USERS` both got in before), and `PUBLIC_DISABLE_SIGNUP`. Proven on 9202 (`audits/signup-lock-2026-09-29/`D). | **CLOSED — 2026-09-29** |
| **R-715** | **[P3-LOW] The setup gate's probe reads only an HTTP-200 JSON object, so three apps with a real status get the button.** MEASURED 2026-09-29 on 9202: ghost (`{"setup":[{"status":…}]}` — a list), home-assistant (`/api/onboarding` — a top-level list), gramps-web (405 after the setup). And a probe that never flips BLOCKS the household's press (fail closed — measured on gramps-web while its check was still in the catalog): a wrong probe in a template would keep an app closed to everyone but the household until the catalog is fixed. **Fix direction:** list indexes in `field`, an optional `status:` to match, and a catalog gate that refuses a probe without a before/after measurement in its comment. **Fixed in controller v0.282.0** (RP29, RP30): list indexes in `field`, `done_status:`. ghost, home-assistant, gramps-web measured before/after on fresh installs; each gate opened by itself; a press before the setup refused on all three (`audits/signup-lock-2026-09-29/`E). New catalog gate `check-probe-measured.py` (5 decoys). | **CLOSED — 2026-09-29** |
| **R-716** | **[P3-LOW] Apps installed before controller 0.281.0 keep their open sign-up — decision 47 closes it only on apps whose gate the box opened.** READ 2026-09-29 on the demo boxes after catalog `6faf432` synced: demo-hp's adventurelog and opengist, demo-felhom's opengist carry `signup_block:` in their synced template and no gate record, so no block (`audits/gate-rollout-2026-09-29/0/P0-3-demo-boxes-after-push.txt`). This is Part 0's rule working as designed (a catalog change never touches an installed app). **Needs an operator word** before anything changes on an installed app: a one-time "close sign-up now" press on the app page for an installed app, or leave them. Only the demo boxes have such installs today. **Operator ruled A (decision 49); built in controller v0.282.0 and pressed** on demo-hp's adventurelog and opengist and demo-felhom's opengist: before, sign-up served; after, refused; adventurelog's own switch on (`audits/signup-lock-2026-09-29/`C). | **CLOSED — 2026-09-29** |
| **R-717** | **[P3-LOW] opengist and wishlist keep their sign-up switch only in their own database — the box closes them with the address block alone.** MEASURED 2026-09-29: opengist `disable-signup` is an admin-panel setting (no env, no CLI); wishlist `system_config.enableSignup` (Prisma). Their blocks are case-insensitive and refused every trick shape (`audits/signup-lock-2026-09-29/B/`). **Fix direction:** an `after_setup` command that sets the database value (wishlist: a Node/Prisma one-liner; opengist: needs its sqlite with the app stopped). | **OPEN — P3; owner: CC** |
| **R-718** | **[P3-LOW] "Close sign-up now" restarts an app with its own switch, and the card does not say so.** MEASURED 2026-09-29 on demo-hp: pressing it on adventurelog recreated its backend (~30 s, one 500 on its login page). The window's card says the app restarts; the close card does not. **Fix direction:** the close card and the gate-open moment say "the app restarts once" where `after_setup.env` exists. | **OPEN — P3; owner: CC** |
<!-- DUE-CHECKS-BEGIN — machine-readable. Parsed by scripts/due_checks_gate.py.
One row per dated check. The R-number must have a row above. Dates are UTC.