Repository navigation
feat(beyondleetcode): C34b Users registration toggle - #184
Conversation
… action switches it Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…r tests Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
Security review (orchestrator-dispatched, 2026-10-16): verdict merge after two small fail-closed fixes. All five decisions held:
Every reader goes through the live getter. The test servers and fixtures cannot reach the build. Asked of the worker before merge:
Routed to C41: document that one web process owns the Instance (a second process keeps its boot-time mode). Accepted: the switch queue has no timeout. Codex second opinion did not run (not installed). 🤖 Generated with Claude Code |
…iled write and an unreadable stored mode Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…own server Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
Fail-closed fixes verified (orchestrator, 2026-10-16) at 5daeba2. After any failed switch, memory takes the stored row's mode, or Invite-only if that read fails. A bad stored value reads as Invite-only. The registration browser tests now use only their own server. CI green. Merging. 🤖 Generated with Claude Code |
Card C34b: the Users page switches the Registration Mode, and the change reaches
/signupon the next request with no restart.What changes
Instance.setRegistrationMode(next)(src/lib/server/instance.ts): writes the database first and changes memory only after the write succeeded, so a failed write never leaves memory Open over an Invite-only database. Accepts only'open'/'invite'; refuses in Local mode and before Setup (nothing written, no row created); the write is conditional on the stored mode still being multi; switches run one at a time so memory and database cannot end on different values. After security review: if a switch fails for any reason (including a write that commits and then rejects), memory is set to what the database holds, or to invite-only when that cannot be read, and the error is still thrown; a stored value at start that is neither mode reads as invite-only.registrationform action (src/routes/users/+page.server.ts): the role must beadminexactly (locals.identity) before anything else (403, also foradmin,learner), then 404 without Better Auth, then the Instance write. A POST form action, so SvelteKit's origin check covers CSRF (pinned by a browser test). A failed write answers a fixed message; the log carries the error's name only.accounts.ts: the role check is one line in the action.Tests
instance.test.ts: both ways and persistence; a write that commits then throws (memory follows the database, both directions); a failed write whose re-read fails too (invite-only); a bad stored value at start (4 values) reads as invite-only; write-before-memory (held write); failed write leaves memory alone; invalid values; Local and not-set-up refuse; a module that thinks it is multi over a database that is not; two switches at once agree (mutation-checked: fails without the queue); a failed switch does not block the next.users.test.ts(real hook, real Better Auth, real file database): admin toggles Open then Invite-only and a real sign-up is admitted then refused (403), and the Sign up page's load follows; learner,admin,learner, signed out, ended session and Local Mode are refused and nothing changes; bad values 400; failed write 500 with no error text; direct action calls pin the order of the checks.registration.multi.e2e.ts(new): see below.Browser test, and one deviation from the brief
Playwright runs
*.multi.e2e.tsfiles in parallel workers on the shared 4174 server (workers: 4, nothing serialises them), so a test that switches 4174 to Open could flip it whilesignup.multi.e2e.tsis asserting that it is closed, even with a restore at the end. So the switching tests run on a Multi-user server of their own (multi-server.ts: same build, fresh database and port, admin made through the app's owncreateFirstAdmin), assignup.multi.e2e.tsalready does for its open state. It is one running server from start to finish: Invite-only (closed, POST 403) → admin switches to Open → the next/signupshows the form → admin switches back → the next/signupis closed and a POST is refused with 403, no Account made. The shared 4174 server is not touched at all by this file: its first CI run hadusers.multi.e2e.tstime out in setup (SocketTimeoutcreating a user) because this file's setup was also writing to 4174's SQLite database at the same moment. The learner,admin,learnerand signed-out refusals therefore run on the own server too, with the stored mode read back unchanged. Also covered: cross-site POST refused, a bad value refused, the no-JavaScript Save button (with afinallyback to Invite-only), and no sideways scroll at 390px.Files outside the card's list
All inside
src/routes/users/: newmulti-server.tsandregistration.multi.e2e.ts;multi-fixture.tstakes an optional server target (default unchanged). Nothing in the other lane's files.Checklist
/signupshows the form, Invite-only, next/signupclosed, on one running servercodexis not installed on this machinebun run check,biome check .,vitest run(731 tests) green locally; Playwrighte2e-multiregistration file 11/11 green locallyFollow-ups for others
src/routes/signup/signup.multi.e2e.tsandseed-open-instance.tsstill say nothing in the app opens registration; that is stale once this lands (other lane's files, left alone).🤖 Generated with Claude Code