Skip to content

feat(beyondleetcode): C34b Users registration toggle - #184

Merged
GauranshMathur merged 4 commits into
mainfrom
C34b-registration-toggle
Oct 16, 2026
Merged

GauranshMathur merged 4 commits into
mainfrom
C34b-registration-toggle

Conversation

@GauranshMathur

@GauranshMathur GauranshMathur commented Oct 16, 2026 •

Copy link
Copy Markdown
Owner

Card C34b: the Users page switches the Registration Mode, and the change reaches /signup on 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.
  • Users registration form action (src/routes/users/+page.server.ts): the role must be admin exactly (locals.identity) before anything else (403, also for admin,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.
  • Users page: the Registration section from the canvas Users board (select + one-line explanation, above "All users"), same select-submits-on-change pattern as Settings with a Save button when JavaScript is off. The load now also returns the mode the Instance holds.
  • No change to 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.ts files in parallel workers on the shared 4174 server (workers: 4, nothing serialises them), so a test that switches 4174 to Open could flip it while signup.multi.e2e.ts is 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 own createFirstAdmin), as signup.multi.e2e.ts already 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 /signup shows the form → admin switches back → the next /signup is 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 had users.multi.e2e.ts time out in setup (SocketTimeout creating a user) because this file's setup was also writing to 4174's SQLite database at the same moment. The learner, admin,learner and 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 a finally back to Invite-only), and no sideways scroll at 390px.

Files outside the card's list

All inside src/routes/users/: new multi-server.ts and registration.multi.e2e.ts; multi-fixture.ts takes an optional server target (default unchanged). Nothing in the other lane's files.

Checklist

  • Instance setter + tests
  • Action + tests through the real hook and Better Auth
  • Registration section on the page, matches the canvas (screenshots checked at 1440, dark and 390)
  • Browser test: Open, /signup shows the form, Invite-only, next /signup closed, on one running server
  • Self-review pass (against the card, the orchestrator's notes and the C33a/C34a/C33b review notes)
  • Codex second opinion: not run, codex is not installed on this machine
  • bun run check, biome check ., vitest run (731 tests) green locally; Playwright e2e-multi registration file 11/11 green locally
  • Security review notes: fail-closed after a failed or committed-then-rejected write, validated boot value (tests first, 7 failed before the fix)
  • CI green on the review fixes, then ready to merge (the worker does not merge)

Follow-ups for others

  • src/routes/signup/signup.multi.e2e.ts and seed-open-instance.ts still say nothing in the app opens registration; that is stale once this lands (other lane's files, left alone).

🤖 Generated with Claude Code

GauranshMathur and others added 2 commits October 16, 2026 10:13
… 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>
@GauranshMathur
GauranshMathur marked this pull request as ready for review October 16, 2026 10:23
@GauranshMathur

Copy link
Copy Markdown
Owner Author

Security review (orchestrator-dispatched, 2026-10-16): verdict merge after two small fail-closed fixes. All five decisions held:

  • /signup closes on the next request.
  • The database is written before memory.
  • Only the two values are accepted, and Local and not-set-up are refused.
  • The exact-admin check runs first.
  • SvelteKit's origin check covers the form.

Every reader goes through the live getter. The test servers and fixtures cannot reach the build.

Asked of the worker before merge:

  • A write that commits but then rejects must re-read the row, or fall back to Invite-only, instead of leaving memory Open.
  • The value read at start must be validated, falling back to Invite-only.

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

GauranshMathur and others added 2 commits October 16, 2026 10:38
…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>
@GauranshMathur

Copy link
Copy Markdown
Owner Author

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

@GauranshMathur
GauranshMathur merged commit 9f3c5ed into main Oct 16, 2026
10 checks passed
@GauranshMathur
GauranshMathur deleted the C34b-registration-toggle branch October 16, 2026 10:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant