feat(setup): setup mode for first-user registration (VS-26) #38

Merged
David merged 1 commit from feat/setup-mode-first-user-VS-26 into main 2026-06-05 19:11:18 +02:00
Owner

VS-26: Setup mode for first-user registration

Adds a setup mode gated by VERVAIN_SETUP_ENABLE that exposes two unauthenticated endpoints driving the vervain-apps /setup onboarding flow. The first-user-is-admin decision lives server-side, never in the frontend.

Endpoints

  • GET /api/setup: 404 when setup mode is off; {"setup": true, "needs_first_user": <user count == 0>} when on. The object shape leaves room for future setup-only steps as additional fields.
  • POST /api/setup/register: unauthenticated JSON {"username","password"}. Guards in order: 404 when setup mode is off, 400 on empty input, 403 once any user exists. On success creates the first user as a full site admin (siteadmin = 0xFFFFFFFF) with the same doc shape maybe_bootstrap_admin produces, then auto-logs them in by minting a session and setting the same mc_session cookie login::post_login sets. The token is also echoed in the body so the frontend lands authenticated without a second round trip.

Behavior

  • Setup mode is on only when VERVAIN_SETUP_ENABLE is exactly true (case-insensitive); absent or any other value leaves it off.
  • The user-count check and the insert are serialized behind a per-process tokio::sync::Mutex so two concurrent registrations cannot both pass the zero-user gate and create two site admins. Single-instance SQLite makes this sufficient.
  • maybe_bootstrap_admin is unchanged: it already no-ops when any user exists, so the ADMIN_USERNAME/ADMIN_PASSWORD env path and the setup path cannot conflict; whichever creates the first user wins and the other becomes a no-op.

Tests

crates/meshcentral-web/tests/setup.rs stands up the full router over a plain-HTTP listener and covers: disabled-mode 404 (both routes), the zero-user happy path (probe, register, doc shape + siteadmin check, body token), existing-user 403 even with setup on, and auto-login session validity against /refresh.ashx.

Acceptance criteria

  • VERVAIN_SETUP_ENABLE=true enables setup mode; absent/other values disable it
  • GET /api/setup returns 404 when disabled; {"setup": true, "needs_first_user": bool} when enabled
  • POST /api/setup/register returns 404 when setup mode disabled
  • POST /api/setup/register returns 403 when any user already exists
  • Successful registration creates a user with siteadmin = 0xFFFFFFFF and the same doc shape as maybe_bootstrap_admin
  • Successful registration response carries a valid session token (body + cookie) accepted by existing authenticated routes
  • Concurrent duplicate registrations cannot create two users (check+insert serialized)
  • ADMIN_USERNAME/ADMIN_PASSWORD bootstrap still works and still no-ops when a user exists
  • Tests cover: disabled mode 404, zero-user happy path, existing-user 403, auto-login session validity

Closes VS-26.

## VS-26: Setup mode for first-user registration Adds a setup mode gated by `VERVAIN_SETUP_ENABLE` that exposes two unauthenticated endpoints driving the vervain-apps `/setup` onboarding flow. The first-user-is-admin decision lives server-side, never in the frontend. ### Endpoints - `GET /api/setup`: 404 when setup mode is off; `{"setup": true, "needs_first_user": <user count == 0>}` when on. The object shape leaves room for future setup-only steps as additional fields. - `POST /api/setup/register`: unauthenticated JSON `{"username","password"}`. Guards in order: 404 when setup mode is off, 400 on empty input, 403 once any user exists. On success creates the first user as a full site admin (`siteadmin = 0xFFFFFFFF`) with the same doc shape `maybe_bootstrap_admin` produces, then auto-logs them in by minting a session and setting the same `mc_session` cookie `login::post_login` sets. The token is also echoed in the body so the frontend lands authenticated without a second round trip. ### Behavior - Setup mode is on only when `VERVAIN_SETUP_ENABLE` is exactly `true` (case-insensitive); absent or any other value leaves it off. - The user-count check and the insert are serialized behind a per-process `tokio::sync::Mutex` so two concurrent registrations cannot both pass the zero-user gate and create two site admins. Single-instance SQLite makes this sufficient. - `maybe_bootstrap_admin` is unchanged: it already no-ops when any user exists, so the `ADMIN_USERNAME`/`ADMIN_PASSWORD` env path and the setup path cannot conflict; whichever creates the first user wins and the other becomes a no-op. ### Tests `crates/meshcentral-web/tests/setup.rs` stands up the full router over a plain-HTTP listener and covers: disabled-mode 404 (both routes), the zero-user happy path (probe, register, doc shape + `siteadmin` check, body token), existing-user 403 even with setup on, and auto-login session validity against `/refresh.ashx`. ### Acceptance criteria - [x] `VERVAIN_SETUP_ENABLE=true` enables setup mode; absent/other values disable it - [x] `GET /api/setup` returns 404 when disabled; `{"setup": true, "needs_first_user": bool}` when enabled - [x] `POST /api/setup/register` returns 404 when setup mode disabled - [x] `POST /api/setup/register` returns 403 when any user already exists - [x] Successful registration creates a user with `siteadmin = 0xFFFFFFFF` and the same doc shape as `maybe_bootstrap_admin` - [x] Successful registration response carries a valid session token (body + cookie) accepted by existing authenticated routes - [x] Concurrent duplicate registrations cannot create two users (check+insert serialized) - [x] `ADMIN_USERNAME`/`ADMIN_PASSWORD` bootstrap still works and still no-ops when a user exists - [x] Tests cover: disabled mode 404, zero-user happy path, existing-user 403, auto-login session validity Closes VS-26.
feat(setup): setup mode for first-user registration (VS-26)
Some checks failed
Check / fmt + clippy + build + tests (pull_request) Failing after 2s
Create release / Create release from merged PR (pull_request) Has been skipped
1b1b9d90dc
Add a setup mode gated by VERVAIN_SETUP_ENABLE that exposes two unauthenticated endpoints driving the vervain-apps /setup onboarding flow. The first-user-is-admin decision lives server-side, never in the frontend.

GET /api/setup returns 404 when setup mode is off and {"setup": true, "needs_first_user": <user count == 0>} when on. POST /api/setup/register creates the first user as a full site admin (siteadmin = 0xFFFFFFFF) with the same doc shape maybe_bootstrap_admin produces, then auto-logs them in by minting a session and setting the same mc_session cookie post_login sets (token also echoed in the body). Registration 404s when setup mode is off and 403s once any user exists, so it can only ever mint the first account.

Setup mode is on only when VERVAIN_SETUP_ENABLE is exactly "true" (case-insensitive); absent or any other value leaves it off. The user-count check and insert are serialized behind a per-process mutex so two concurrent registrations cannot both pass the zero-user gate. maybe_bootstrap_admin is unchanged: it already no-ops when any user exists, so the env path and the setup path cannot conflict.

Integration tests cover disabled-mode 404, the zero-user happy path, existing-user 403, and auto-login session validity against /refresh.ashx.

#VS-26
David merged commit 17b9604d15 into main 2026-06-05 19:11:18 +02:00
David deleted branch feat/setup-mode-first-user-VS-26 2026-06-05 19:11:18 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/vervain-server!38
No description provided.