Super-admin (the platform view above orgs)
September 12, 2026 · View on GitHub
Everything else is org-scoped; super-admin is the one capability that sees across all orgs. It's deliberately separate from org roles.
Authorization (require_superadmin in domain.identity.access) — hybrid
A caller is a super-admin if EITHER:
- the presented
X-Treg-Tokenequals the envadmin_token(get_settings().admin_token, fromTREG_ADMIN_TOKEN), compared withhmac.compare_digest→ principal"env-admin"; OR - the token resolves to a
MembershipwhoseUser.is_superadminis set (and notsuspended) → principal = that user's email.
Otherwise 403. The env key bootstraps; POST /admin/users/{id}/superadmin then grants named users the
flag (so a web portal can log in with either). Returns a principal string (for audit).
The dependency lives in domain.identity.access and every consumer imports it from there; the
transitional api.py re-export retired with the rest of the stage-3 compatibility surface.
On the admin pool. The gate takes Depends(get_admin_session), and so does every /admin/*
handler it guards — 3 connections, no overflow, separate from the API's
(ops/deploy.md § Database pools). It must be the SAME dependency callable on both: FastAPI caches
dependencies per request by identity, so a gate on get_session would put admin traffic back on the
API pool through the back door. Staff pages are therefore bounded by construction — a panel that
polls itself into saturation costs admins their own 503s, not the product's.
The cross-tenant read, mutation, and reconciliation handlers live in three ordered blocks in
routers.admin. The mutation block shares the org deletion and member-rule cleanup helpers from
routers.orgs.
Suspension enforcement (in require_member)
Two flags gate the org-scoped path: require_member raises 403 if user.suspended ("account
suspended") or org.suspended ("org suspended"). Set by the admin endpoints below. Super-admin
endpoints are unaffected (they use require_superadmin).
Endpoints (all under /admin/*, gated by require_superadmin)
- Feedback:
GET /admin/feedback(routers.feedback.admin_feedback) returns private reports, filtered by optional category, withlimitand descending-IDbeforepagination. It sharesget_admin_sessionwith the super-admin gate. See feedback. - Reads:
admin_stats(totals,tools_by_injector/tools_by_host,credential_healthrollup, call volume + success rate,growthcounts — computed in-process over small result sets),admin_orgs(every org + member/role/tool/secret/bundle counts),admin_org_detail,admin_users(+ their memberships),admin_tools,admin_calls,admin_health(non-oksecrets). - Failure evidence:
admin_errors(?days=7&limit=100&provider=&status=&tier=) — failed calls at every credential tier, including plain own tools (tier: null), withCallRecord.error_request/error_response, the redacted capture of what the caller sent and what the provider answered (see data-model).tierfilters an exact marketplace tier; an empty value selects plain own tools. Superadmin and not org-admin because the rows hold customers' request content;GET /callsdeliberately does not expose these columns, and it defers them so they are not even fetched. This route also performs the 14-day retention pass (_purge_expired_error_evidence, blanking to'<expired>'on its own committed session) — ageing lives here because there is no scheduler and the request path cannot hold a lazy marker,get_admin_sessionnever committing one. - Reconciliation (Phase 5):
admin_reconcile_drift|spend|repeats(?since_days=30) — cross-org aggregates over platform-tier spend, so super-admin and not org-admin: price drift per endpoint, settled spend per provider (the invoice comparison), and the repeat-query rate. Query-time reports over existing rows; no scheduler. Logic insrc/treg/reconcile.py;scripts/provider_balances.pyis the manual companion that reads the providers' own balances. The async-task settlement report follows the same super-admin and dedicated admin-pool boundary. - Mutations (Phase 2):
admin_set_superadmin,admin_suspend_user,admin_delete_user(removes memberships, thencascade_delete_org(indomain/governance/teams.py) any org left with zero members, and promotes a survivor to owner in any org left without one),admin_suspend_org,admin_delete_org(force, cross-tenant). Org deletion sharescascade_delete_org(indomain/governance/teams.py) with the owner's owndelete_org(one cascade helper: tools, secrets, bundles, pending-oauth, call records, memberships, then the org). - Last-superadmin floor:
require_superadminreturns the principal ("env-admin"or the user's email); the three destructive user ops refuse (409) when demoting/suspending/deleting would drop the count of active (is_superadmin and not suspended) users to zero — so a superadmin can't self-lock the platform out of/admin/*. The env token bypasses the floor (it can always recover). - Org credit:
admin_credit_org(POST /admin/orgs/{org_id}/credit) — the HTTP equivalent ofscripts/manual_grant.py. Credits an org with promotional balance throughmoney.grant(), preserving the invariant that balance = sum(blocks) - sum(holds). Requiresamount_usd,ref(idempotency key — a duplicate ref for the same org returns 409), andreason. Always useskind="promotional"so the credit burns before purchased (non-refundable marketing expense). The script remains valid for airgapped / direct-DB ops, and the route removes the need to open Render Postgres IP allowlists.
Model + migration
User gains is_superadmin + suspended; Org gains suspended (booleans). The columns are part
of the Alembic baseline schema; a live DB picks up schema changes through the explicit
python -m treg upgrade release phase, never on restart.
CLI (interface/cli.md)
treg admin login --token (saves the env key), admin stats|orgs|org <id>|users|tools|calls|health,
admin grant|revoke|suspend-user|rm-user|suspend-org|rm-org, and
admin credit <org_id> --amount-usd <n> --ref <ticket> --reason <text>. _admin_client sends the saved
admin_token if present, else the active org token (works for an is_superadmin user).