Skip to content

Roles & access

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

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.

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.

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.

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).