Super-admin (rubot-superadmin)

Manages dashboard access across both rubot-client (bearer mode) and rubot-open-client (open mode). Single source of truth — the managers.is_superadmin flag, scoped to no tenant.

Account states

register
   │
   ▼
email_confirmed=0 ──confirm-email──▶ email_confirmed=1, approved=0
                                              │
                                              ▼
                                    approved=1 (super-admin click)
                                              │
                              ┌───────────────┴──────────────┐
                              ▼                              ▼
                          normal manager              is_superadmin=1

The bootstrap super-admin (env SUPERADMIN_EMAIL) skips both gates — on register the row is inserted with email_confirmed=1, approved=1, is_superadmin=1.

Bootstrap

Set in rubot-middleware's wrangler.jsonc:

"vars": {
  "SUPERADMIN_EMAIL": "you@example.com"
}

Then register on any dashboard (rubot-client, rubot-open-client, or rubot-superadmin itself) using that email. The response payload comes back as { pending_confirmation: false, bootstrapped: true } and you can sign in immediately.

If SUPERADMIN_EMAIL is unset, promote a row by hand:

wrangler d1 execute rubot_data --local --command "\
UPDATE managers SET email_confirmed=1, approved=1, is_superadmin=1 \
WHERE email='you@example.com';"

Routes (always mounted, both modes)

Method + pathOp
`GET /api/admin/managers?status=pendingapproved
POST /api/admin/managers/:id/approveflip approved=1
POST /api/admin/managers/:id/revokeflip approved=0 (with optional reason)
POST /api/admin/managers/:id/superadmin{grant: bool}
GET /api/admin/managers/:id/auditaudit trail for one manager
GET /api/admin/logsplaceholder (see observability.md)
GET /api/admin/agent-logsplaceholder

All gated by: requireApprovedManager → is_superadmin === 1.

Audit

Every state change writes to account_audit(manager_id, actor_id, action, reason, created_at). actor_id is NULL for the bootstrap insert (action='bootstrap').

Dashboard

Routes on rubot-superadmin:

PathPurpose
/redirect → /admin (logged in) or /login
/loginPOST /api/proxy/auth/login
/adminmanager list (pending/approved/all tabs) + actions
/admin/logsplaceholder
/admin/agent-logsplaceholder
/api/proxy/[...path]cookie-forwarder to middleware

The SuperAdmin.astro layout gates every /admin/* page on session + is_superadmin=1 && approved=1. Non-super-admins hitting the URL see a 403 banner with a link back to the operator dashboards.

Eventual log integration

The two placeholder pages (/admin/logs, /admin/agent-logs) wait on a log sink (docs/observability.md). The pattern is the same one used by tenant.usage: pick a sink, add a middleware endpoint that queries it, swap the placeholder for a fetch + table. Trace IDs are already end-to-end so the backfill is a SELECT.