SIVO
RBAC — roles and permissions

Security

RBAC — roles and permissions

SIVO's access control model. 5 base roles, granular permissions, audited cross-tenant superadmin access.

Updated:
securityrbacroles

Executive summary

SIVO uses RBAC (Role-Based Access Control) with 5 base roles. Every REST endpoint and dashboard action checks the JWT role before executing. There’s no “god mode”: even superadmin is audited in audit_logs when operating cross-tenant.

Base roles

RoleScopeKey capability
agentOwn profile + queue(s)Handle calls, pause, view own call history
supervisorTenant-wide (read + live actions)Wallboard, spy/whisper/barge, force pause, see all activity
adminTenant-wide (CRUD)Manage agents, queues, IVR, DIDs, trunks, policies, integrations
superadminCross-tenantSIVO support operations, audited
viewerTenant-wide (read-only)Queries, reports, exports — no changes

Permission matrix by resource

Resourceagentsupervisoradminsuperadmin
Own profileR/W (limited)R/W (limited)R/WR/W
Other tenant agentsR (basic)RR/WR/W
Agents of another tenantR/W (audited)
Queues — configRR/WR/W
Queues — live actions (force pause, etc.)WWW
IVR flowsRR/WR/W
DIDs and trunksRR/WR/W
Recordings — listenR (own)R (all)RR
Recordings — download/deleteR/WR/W
TranscriptsR (own)R (all)RR
WebhooksRR/WR/W
Salesforce integrationRR/WR/W
Audit logsRRR
Tenant feature flagsR/W
Tenants (create, delete)R/W

How it is enforced

In the JWT

Each token carries the role signed RS256. No privilege escalation by modifying the token:

{
  "sub": "uuid-of-user",
  "tenantSlug": "acme",
  "sipDomain": "acme.sip.sivocenter.com",
  "role": "supervisor",
  "iat": 1715000000,
  "exp": 1715086400
}

On each REST endpoint

Each router mounts requireRole(...) middleware:

router.delete('/agents/:id', requireRole('admin', 'superadmin'), deleteAgentHandler);

If insufficient role: 403 Forbidden with code: INSUFFICIENT_ROLE.

In the dashboard

UI components conditional to role. But backend is the source of truth: even if the UI shows a button by bug, the endpoint rejects with 403.

In PostgreSQL (RLS)

SIVO roles ≠ PostgreSQL roles. What isolates data at the engine level are the RLS policies filtering by tenant_id (session variable app.current_tenant).

RBAC roles are orthogonal: they define what the user can do within their tenant, not which tenant they see.

The superadmin role (SIVO ops)

We operate SIVO. For support and troubleshooting there’s a superadmin role with cross-tenant access. Mitigations:

  1. Only 3-5 people max from the SIVO team have this role. Their accounts are subject to:
    • Mandatory MFA (TOTP).
    • Short sessions (8h).
    • Quarterly password rotation.
  2. To operate on another tenant, they send X-Tenant-Id: <tenant-uuid> header. Without it, they see nothing of other tenants.
  3. Each use of the header is logged in audit_logs with cross_tenant: true flag. The customer sees this in their backoffice (“SIVO team access to your account”).
  4. Some critical ops (mass export, tenant delete) require pair-approval: two superadmins must confirm.

Custom permissions / Enterprise

For Enterprise customers needing custom roles (e.g. “QA reviewer can view recordings of certain queues but not others”):

  • We define permission_overrides per user in JSON.
  • Implemented with additional middleware after requireRole.
  • Documented case by case, part of onboarding consulting.

What we intentionally DON’T do

  • Per-user API keys to call the API. JWT is the only mechanism — 24h TTL, revocable, auditable.
  • Permanent sessions. After 24h, login again.
  • Sharing accounts between people. One account = one person. Enforced with MFA + unique email.

Rotate password

Each user can change their password from the top-right avatar → Profile → Change password. Rules:

  • Min 8 characters.
  • Cannot equal the hash of last 5.
  • Temp lockout after 5 failures (15 min).

Admin/superadmin can force reset any user’s password. The user gets an email with a one-time link (1h TTL).

Support

[email protected] — for vulnerability reports or suspected role misuse.