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 current deployment it lives at https://bloom-admin.pages.dev (Cloudflare Pages), reaching the API through a same-origin /api/* proxy.

The admin shell: resource navigation, read-only banner, and identity with role pill

The screens follow the approved design of record - the frames on this page are those boards.

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.

The Overview: resource total tiles, grants by role, recent sessions

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://bloom-server.exe.xyz 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.