Skip to main content

Admin console basics

The admin console (apps/admin) is Bloom's operations surface: a separate SPA on its own origin, beside the user dashboard, for the people who run Bloom - not for project owners. It is grant-gated, read-only by design, and every write it can perform is an explicitly guarded, audited action. In the dev deployment it lives at https://admin.dev.bloom.hiddenclaw.org (a Cloudflare Pages custom domain - its own origin, never app.*/admin, so the host-only admin session cookie stays invisible to the dashboard), reaching the API through a same-origin /api/* proxy.

The screens follow the approved design of record, the Penpot file Admin Dashboard.

Signing in​

Sign-in is Google, against the admin session surface. A Google account alone is not enough: you need an admin grant, and its role - viewer, operator, or superadmin - determines your permission set. An ungranted account lands on the access-denied page with no session cookie set; a granted one gets an admin session cookie scoped to the admin origin only. The server re-checks the grant on every admin API call - nothing about your access is decided client-side.

Reading data​

The console is a read-only resource browser over seven resources: users, admin_grants, sessions, oauth_identities, telegram_links, workspaces, and global_credentials.

  • Resource lists expose exactly the search/sort/filter controls the backend whitelists - no more. List state (filter, search, sort, page) round-trips through the URL, so a filtered view is a shareable link.
  • Record detail shows the record read-only, with related panels cross-linked by user_id - from a user, jump straight to their sessions, grants, identities, and links.
  • The raw-JSON inspector (/{resource}/{id}/raw) shows the exact API payload, making the structural-redaction contract visible: what the API redacts simply is not there, for anyone, at any role.
  • Overview aggregates only what the API can truthfully produce: per-resource totals, grants by role, recent sessions.
  • ⌘K / Ctrl+K opens a command palette for jumping between resources.

There are deliberately no create/edit/delete or bulk controls anywhere - the API has no generic mutation routes, and the console renders no affordance the server would refuse ("no fake affordances").

Acting: guarded verbs​

The only write path is the guarded action: POST /api/admin/{resource}(/{id})?/actions/{verb} - one explicit verb per operation (grant admin, change a role, revoke a session, ...), each gated on its exact permission. A button appears only when your grant carries that permission.

Safety scales with destructiveness:

  • Reversible-until-sent actions (revoking a session) fire optimistically with an undo window - the request is deferred, and Undo cancels it.
  • Irreversible actions get hard confirms: deleting a user first shows a real preview of the cascade it would trigger, then requires typing the account's email.

Every action - including denied attempts - lands in the audit trail with before/after snapshots, readable at GET /api/admin/audit_log.

Running it locally​

pnpm install # once, at the repo root
cd apps/admin
pnpm dev # http://localhost:5174

The dev server proxies /api to the deployed API (https://api.dev.bloom.hiddenclaw.org by default; override with VITE_DEV_API_TARGET). Unauthenticated you get the login screen, and an ungranted Google account lands on access-denied. Completing the admin sign-in against a local API requires pointing the API back at the SPA (BLOOM_ADMIN_DASHBOARD_URL=http://localhost:5174) - see the apps/admin README, which also documents the security model in depth.