Skip to content

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.

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.

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.

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.
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.

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.

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.

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.

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.