Task: Create the first administrator from the setup screen

Table of Contents

This page documents a task in the Implement the setup journeys in ores.web story. It captures the goal, current status, acceptance, and any notes or results.

1. Goal

The setup screen reports the state and cannot change it. Give it the one action that does: create the first administrator, which is the write that closes bootstrap mode and opens the sign-in screen.

2. Status

Field Value
State DONE
Parent story Implement the setup journeys in ores.web
Now Nothing.
Waiting on Nothing.
Next Nothing.
Last touched 2026-09-27

3. Acceptance

  • The setup screen creates the first administrator with no session, through a hand-written BFF route and a wire-protocol client method, parsing the generated schema.
  • The password field shows the rules the server enforces, and the form refuses to submit until they are met.
  • The server closes bootstrap mode when the administrator is created, and the screen asks the gate again rather than deciding the new state itself.
  • The route refuses when the installation is not in bootstrap mode, because the function behind the subject grants SuperAdmin and does not check the flag itself.
  • The path is proven against the running fleet: create, the flag flipping, a second create refused, and a sign-in with the created account.
  • The workspace typechecks, passes the format check, tests green, and builds.

4. Plan

One commit, because it is one action end to end.

4.1. Unit 1 — the call

createInitialAdmin on the wire client, over the subject the generator already declares (iam.v1.bootstrap.create-admin), with hand-written request and reply schemas beside the one the status read uses. It is the only call that needs no session, and it is slow: the server hashes the password and provisions the account, its role and its party, so it takes the slow timeout rather than the fast one.

POST /api/bootstrap/administrator on the BFF, unauthenticated for the same reason, returning the account rather than a session — the person signs in with it next, which is the point of the screen.

4.2. Unit 2 — the guard, which is the interesting part

The route refuses unless the deployment says it is still in bootstrap mode. The SQL function behind the subject does not check that itself: it creates the account, grants SuperAdmin, associates it with the system party and clears the flag, with nothing stopping a second call under a different username. So the route's check is not defence in depth, it is the only guard on the highest privilege the deployment has. The refusal carries its own code, bootstrap-complete, and the code joined the shared error enum rather than being reported as a generic bad request.

4.3. Unit 3 — the form, and who decides the state

The form asks for the username, the email, and a password through the shared field, so the rules shown while typing are the rules the server enforces and the submit waits until they are met. A failure shows the server's message.

On success the gate asks again rather than assuming. The server clears the flag inside the same call, so the honest thing to do with the news is to re-read it; a screen that flipped its own state would be a second answer to a question the server owns.

5. Measurements

Every number this task claims about a tree, with the command that produced it and the commit it was measured at. A number without both is not a measurement, because a clean-up record describes a tree and trunk moves under a branch: a consumer count, a gate's component count and a suite's size all change with no code changing, and the reader of a bare number cannot tell which tree it came from. Where the raw output matters, put it in an evidence/ file beside the task and name that file in the Command column.

What Value Command Commit
The workspace suite 111 cases in 15 files npm test 5771d053d3
… of which new 1, asserting the setup action npm test 5771d053d3
The browser bundle 417.88 kB npm run build --workspace @ores/web 5771d053d3

The live path, against this environment's running fleet, before the environment was reset:

What Observed Command
Create 200, accountId and tenantId returned curl -X POST /api/bootstrap/administrator
The flag isInBootstrapMode false after it curl /api/bootstrap
A second create 409 bootstrap-complete the same POST again
Sign-in with the new account 200, an active session on the system party curl -X POST /api/session
The environment after the reset isInBootstrapMode true again compass db reset-system -y

6. Notes

  • The route's guard is the only one. ores_iam_create_initial_admin_fn does not check bootstrap mode and does not refuse a second administrator under a different username; the unique index on (tenant_id, username) refuses only a repeat of the same name. Filed as a capture, because the fix belongs in the function, where the privilege is granted.
  • The reply carries no tenant name. The C++ response struct declares tenant_name and the handler leaves it empty, so the HTTP shape returns the account and the tenant id and does not promise a name. A field the server never fills is worse than an absent one: the browser would render a blank.
  • The environment was reset, not left provisioned. Creating the administrator closes bootstrap mode, so testing it changes what the next person sees. The reset returns the system to bootstrap mode with the account soft-deleted, which is why the setup screen is the live state again rather than the sign-in page.

7. Test Scenarios

Manual QA scenarios (scaffolded via compass add test_scenario) that verify this task. Link new ones here as they're created; the scenario doc itself links back via its "Verifies task" field.

Scenario State Notes
     

8. PRs

PR Title
#2299 [web] Create the first administrator from the setup screen

9. Review

Comment summary File Decision Notes
       

10. Result

The setup screen creates the first administrator, so the write that closes bootstrap mode is the one action the screen carries.

The call is the generated subject with hand-written schemas; the route is unauthenticated because the deployment has no account to present; and the route refuses once the deployment says it is no longer in bootstrap mode. That refusal is the only guard on a function that grants SuperAdmin and does not check the flag itself, and the function is filed as a capture because the fix belongs there.

The path was proven against the running fleet rather than only in tests: create, the flag flipping, a second create refused, and a sign-in with the new account. The environment was then reset to bootstrap mode, so the setup screen is what a browser sees here again.

The form's password rules are the rules the server enforces, because the field computes them from the same policy the validator does; the submit waits until they are met; and a failure shows the server's own words rather than a translation of them.

Emacs 29.3 (Org mode 9.6.15)