Administration (console guide)
tinyguard ships a built-in admin console: a React + TypeScript single-page application styled with Tailwind CSS, built with Vite. The server remains the single authorization authority; every console-side derivation only hides, disables, or reshapes presentation.
Serving model
Section titled “Serving model”The console is served from the same origin as the API: set
server.console_static_dir to the built bundle directory and the versioned
root /auth/v1/ serves the SPA (hash routing, so only the document itself is
served), with content-hashed assets mounted immutably under
/auth/v1/assets. The published image ships the bundle at /app/console, and
its bundled configuration already points server.console_static_dir there;
keep that value when you mount your own configuration.
Every browser write carries the session-established anti-CSRF header; reads never carry a body. One server-sent-events stream and one plain-text proof-of-work challenge endpoint complete the transport set.
Entry and gating
Section titled “Entry and gating”Console entry fetches the session-info document once and then the backend
gating read GET /auth/v1/console/access:
| Outcome | Meaning |
|---|---|
401 |
Anonymous — the console sign-in redirect starts. |
403 |
Authenticated but holding no administration role (needs-admin-role notice). |
406 |
Administration requires an unsatisfied second factor (MFA-required notice). |
200 |
Administrative: the administration class (full or delegated with the managed groups), the account identifier, and the session’s anti-CSRF token. |
Any unauthenticated API answer handled by the shared client reloads the whole page, driving the viewer into the login flow.
Console sign-in
Section titled “Console sign-in”The console-initiated login uses the authorization-code flow with PKCE
(S256), a nonce, a fixed minimal scope set, and the console’s own fixed
client identity (admin-console, registered automatically on boot). The
verifier lives in browser-local storage only until the callback exchange and
is deleted afterwards. The state parameter carries the area marker so the
callback returns the viewer to where they started. Sign-out purges the stored
credentials and leaves through the end-session endpoint.
- Hash-based URL namespace (
#/users,#/clients, …) with the users list as the default landing view; selections and filters reflect into the URL query. - Navigation sets split by admin kind: full administrators see every section; delegated group administrators see the reduced read-side set (users, sessions, events, docs) with read-only presentations.
- Responsive sidebar (collapsible rail on wide screens, overlay on small), theme toggle with OS-preference fallback, version footer.
Sections
Section titled “Sections”| Section | Route | Visible to | What it edits / shows |
|---|---|---|---|
| Users | #/users |
both | Account list (filter, ordering, paged mode above the threshold, server search at 3+ characters), account detail tabs, password tab with live policy, passkey administration, force logout, delete. Self-registered accounts that have not confirmed their address show Not confirmed, with Send confirmation again and Mark as confirmed; the Registration panel holds the tenant’s registration settings. |
| Email jobs | #/users/mail |
full only | Bulk email: recipients by roles and/or groups, content modes, optional scheduling, jobs board with cancellation. |
| Clients | #/clients |
full only | Search, creation (reserved dynamic identifier prefix refused), config/secret/branding tabs, one-time secret handling with masked display and command examples, rotation with the 1..24-hour grace window. |
| Roles | #/roles |
full only | Role registry: list/add/delete with the shared name pattern. |
| Groups | #/groups |
full only | Group registry: list/add/delete with the shared name pattern. |
| API keys | #/api-keys |
full only | Name/expiry validation, area-and-right access matrix, one-time generation with masked display and command examples, self-test, rotation. |
| Sessions | #/sessions |
both | Server-driven paging, three-character server search, ordering, per-session termination, invalidate-all; read-only for group administrators. |
| Events | #/events |
both | Live server-sent stream (severity coloring, capped newest-first buffer, reconnect cadence, full-admin-only test-event trigger) and the archive (start/end/level/type filters, client-side search, ordering). |
| IP blacklist | #/blacklist |
full only | Address/expiry listing, adds (required address, length cap 40, non-past expiry), removal and click-to-copy; read-only for group administrators. |
| Signing keys | #/keys |
full only | The published key set as expandable entries; confirmed rotation with transient success marker. |
| Sign-in domains | #/sign-in-domains |
full only | The selected tenant’s e-mail domains: target, state in words (“Waiting for DNS record until …”, “Verified”, “Verified · record missing since …”, “Verified by the platform”), and beside the list the selected domain’s DNS record to publish, each value copyable, its last check and Check now. A claim refused because another tenant holds the domain offers Prove it’s ours. See Sign-in domains. |
| Tenants | #/tenants |
full only | The multitenancy management surface — see below and Tenants. |
| Account dashboard | (self-service) | account owner | Profile read/edit, account-type-aware password change, passkey management behind the short-lived modification token, registered devices. |
The account self-service routes live under /users/... on the server side.
Identity providers and tenant secret-manager connections now have dedicated
console sections; see Federation for their current limits.
Some remaining administrative surfaces, including scopes and password-policy
editing, are not yet exposed consistently through the console.
Tenants section
Section titled “Tenants section”Platform rights are detected by the tenant list itself (the backend answers
200 only for platform administrators, 403 otherwise); operators without
the role get the empty state explaining it. Creation, edit, the
suspend/activate toggle, and deletion — with the force checkbox and the
server’s verbatim refusal — all go through write dialogs. The default tenant
renders without a delete.
A tenant’s Administrators (admin_emails) are the people who
administer that tenant. They are not the way to give someone from another
customer access to it: each person has one home tenant, and applications that
need a person in several of their tenants invite that identity themselves
(see one person in several tenants).
Each tenant also has E-mail first (Auto, the default, turns the e-mail step on while the tenant has a verified sign-in domain; Off keeps the classic login page) and Hostnames: the primary hostname and aliases, with their state, kind, certificate status and, for custom hostnames, the DNS record to publish. Changing the primary hostname warns that every application using the tenant must be reconfigured. Platform administrators also see every tenant’s sign-in domains and hostnames in one list, with Mark as verified… and Release… (a reason is required). See Tenant hostnames.
Write dialogs
Section titled “Write dialogs”One native <dialog> per write, in the console design’s
edit → confirm → done shape:
- The dialog opens with an eyebrow (for example
CONFIRM), title, optional summary, the form body, an error callout, and a right-aligned footer (Cancel left of Confirm; danger writes swap the confirm button to the filled danger variant). - Focus lands on the first editable field; close returns focus to the opener; Escape cancels.
- The confirm button label swaps to “Working…” while busy (
aria-disabled). - On success the result stays on screen and Cancel becomes “Close”.
- The server’s refusal is shown verbatim in a monospace block inside the error callout (“The server refused this change.”).
Destructive actions (force logout, delete, tenant purge) are confirmed destructive dialogs; the account surface refuses the anti-lockout self-deletion.
Tenant switcher
Section titled “Tenant switcher”The appbar tenant control probes platform rights by fetching the tenant list.
Its choice persists in browser-local storage and in the URL’s tenant hash
parameter. The selected tenant is preserved across section navigation. Users
and Sessions list/search requests use tenant-prefixed server paths and show
the selected tenant’s records; their non-default views are currently read-only.
Identity providers and the tenant secret manager also use the selected tenant.
Other console sections still need a tenant-scoping audit—do not interpret the
switcher as proof that every section is isolated yet.
Backend console-support surface
Section titled “Backend console-support surface”| Endpoint | Method | Purpose |
|---|---|---|
/auth/v1/console/access |
GET |
The gating read (401/403/406/200 as above). |
/auth/v1/providers |
GET/POST |
Tenant OIDC provider registry when federation is enabled; see Federation. |
/auth/v1/console/users/search |
GET |
Paginated-mode server search; below three characters it refuses with the recorded boundary. |
/auth/v1/console/sessions/search |
GET |
Paginated-mode server search; same three-character boundary. |
/auth/v1/blacklist(/…) |
GET/POST/DELETE |
Blacklist administration under the deny-by-default area-and-right matrix; delegated group administrators are read-only; invalid addresses are refused verbatim. |