The live instance had no way to add a passkey or a user. Two core pages now
make the signed-in experience usable (SPEC-010):
* the ACCOUNT page (GET /{GUID}/account) shows the user's factors (passkey
count · TOTP enrolled), an "Add a passkey" button that LINKS to the
existing D-041 ceremony screen — the WebAuthn flow is never duplicated —
and a "Don't ask again" control writing users.passkey_opt_out;
* the admin USERS page (GET /{GUID}/users) lists every user (name · email ·
role · manager · identity status) and creates one: only roles below the
creator (D-019), the manager defaulting to the creator, then users.Create
-> identities.Mint -> the /{GUID} invite is EMAILED and shown for
copy/paste.
Both ride the door as core dispatch modules, so the door still gates them
(session + role + CSRF). The entrypoint requires admin for the users
functions; account stays at the team floor.
Additive support: auth.Identities.TOTPEnrolled, users.Users.List,
server.Deps.Credentials (the banner's "has a passkey?" read) and the
CoreDispatchNames guard now covers account/users.
- store: SQLite (WAL, foreign keys ON, CGO-free driver) + the embedded
migration runner; each module's migrations are name-prefixed (D-012)
- audit: the append-only audit log; the actor threads via context (R-1)
- users: the user_id/GUID/email anchors (D-013)
- config: the YAML settings file (D-035) + validation
- httpx: cookies · loopback detection · trusted-proxy XFF · CSRF · security
headers · redaction
- middleware: correlation id · audit · rate limiting
- mail: the invite/recovery mailer seam
- server: the core DOOR — the resolution switch, the dispatch registry
(RegisterModule), the role gates, /setup · enroll/login · recovery flows,
the reserved `static` workflow, and the loopback-only bootstrap (D-025)