Roles & access
Roles
| Role | Description |
|---|---|
analyst | read-only on own tenant |
admin | administrator of own tenant — agents, rules, billing, identity |
owner | full control of own tenant + role management |
auditor | external accountant; bundle export download via Auditor Workbench (TOTP-prod) |
msp_operator | cross-tenant operator — exclusively /api/v1/msp/overview |
(is_superadmin) | platform superadmin; all MSP endpoints + tenant-suspend |
Endpoint gates
Cookie auth + tenant scope is the baseline for every user.* route. On
top of that:
| Endpoint | Extra gate |
|---|---|
POST /api/v1/trust-score/weights | role=admin|owner + TOTP-prod |
POST /api/v1/auditor/bundle | role=admin|owner|auditor + TOTP-prod |
GET /api/v1/auditor/bundles/:t/download | atomic one-shot, 24h expiry |
GET /api/v1/msp/overview | role=msp_operator OR is_superadmin |
POST /api/v1/agents/:id/console/start | role=admin|owner + TOTP-prod + reason ≥20 chars |
All agentAuth.* routes (ingest, register, whoami, heartbeat) go through
the SHA-256 bearer-token check; no role check needed.
TOTP purpose buckets
Every TOTP-gated action has its own purpose so cross-feature replay is
impossible:
| Purpose | Used by |
|---|---|
console_session | console start |
trust_score_weights | weights override POST |
auditor_bundle | bundle generation POST |
force_update | force-update push to agents |
unlock_evidence | AI evidence pack content unlock |
A 6-digit code consumed for console_session stays invalid for that same
purpose for 30 seconds, but IS usable for auditor_bundle within that
same 30-second window — different row in consumed_totp_codes.
RLS
Every tenant table has ENABLE ROW LEVEL SECURITY with a
tenant_isolation policy. Important: this is defense in depth.
set_config('app.current_tenant', …, true) does not survive pgxpool’s
connection recycling, so every production query MUST contain an explicit
WHERE tenant_id = $1.
Exception: hub-side workers (TrustScoreWorker, IdentityHygieneWorker, SupplyChainWorker, CorrelationWorker, AuthGeoWorker) intentionally run cross-tenant — they emit/aggregate per tenant via the inner-loop scope.
Audit log
Every role change, weights update, bundle creation/download, link creation,
and delete operation goes through audit_log with event_type =
<feature>.<action>. Filterable in the Audit page (/audit).