Skip to main content

Roles and Tenancy

Definitions

Tenant

An isolated organization within the platform. A tenant represents a single customer, project, or organizational unit.

  • Scope: All data belongs to exactly one tenant
  • Isolation: Tenants cannot access each other's data
  • Identity: Each tenant has a unique ID and display name

Pack

A named collection of requirements bound to a tenant. Packs define what compliance criteria apply to a specific use case.

  • Binding: Packs are linked to tenants via pack bindings
  • Configuration: Each pack can have different criteria, corpus, and engine settings
  • Versioning: Pack configurations are versioned and can be activated per environment

Environment

The deployment stage where configurations are active.

EnvironmentPurpose
DEVDevelopment and testing
STAGINGPre-production validation
PRODProduction use

User roles

RoleCodeDescriptionAccess level
Portal UserCM_USERSubmits evidence and reviews assessmentsPortal only (/cm)
Admin ViewerADMIN_VIEWERMonitors runs and reviews audit logsAdmin console (read-only)
C_AI EngineerC_AI_ENGINEERFull platform configuration accessAdmin console (full access)

Legacy terminology mapping

The codebase uses some legacy naming conventions. Here's how they map to current terminology:

Legacy termCurrent termCode reference
CMPortal UserRoute /cm, type CM_USER
ComplianceManagerPortal UserComponent ComplianceManager.tsx
CM_USERPortal UserSession role constant
AdminRoleUser roleSchema AdminRole constant
note

When you see "CM" in routes, API responses, or component names, it refers to Portal Users—the users who submit evidence and review assessments.

Invariants

These conditions must always be true:

  1. Every resource belongs to exactly one tenant: Documents, runs, assessments, and all other data are tenant-scoped.

  2. Tenant ID is required for all data operations: API requests that modify or retrieve data must include tenant context.

  3. Cross-tenant access is forbidden: A user authenticated in Tenant A cannot access Tenant B's data, even via direct API calls.

  4. Session establishes tenant context: The admin login flow sets the tenant ID in the session, which persists until logout.

  5. Portal Users are restricted to portal routes: Users with CM_USER role cannot access /admin routes.

  6. Admin Viewers cannot modify data: The ADMIN_VIEWER role provides read-only access to monitoring and audit logs.

How it shows up in the UI

Portal interface (/cm)

  • Portal Users see only their tenant's data
  • The requirements panel shows the pack bound to their tenant
  • Uploaded documents are stored in the tenant's document collection
  • Assessment runs are scoped to the tenant

Admin login (/admin/login)

  1. User selects a role (C_AI Engineer or Admin Viewer)
  2. User selects a tenant from the available list
  3. Upon login, session is established with role + tenant
  4. All subsequent admin operations use this context

Admin console (/admin)

  • The current tenant is shown in the header
  • Switching tenants requires returning to login
  • Run History shows only the current tenant's runs
  • Audit logs are filtered to the current tenant

Tenant indicator

A badge in the admin interface shows:

  • Current tenant name
  • Current environment (DEV/STAGING/PROD)
  • Current pack (if applicable)

How it shows up in the API

Session endpoint

GET /api/admin/session

Response:

{
"authenticated": true,
"role": "C_AI_ENGINEER",
"tenantId": "tenant-abc-123",
"packId": "project-pack-v1"
}

Tenant-scoped endpoints

All data endpoints require tenant context. The platform resolves tenant from:

  1. Session (primary): The logged-in user's session contains tenantId
  2. Query parameter (admin override): ?tenantId=xxx (requires super-admin)
  3. Default fallback: Returns error if no tenant can be resolved

Example request with tenant context:

GET /api/runs
Cookie: session=...

The server extracts tenantId from the session and filters results accordingly.

Cross-tenant protection

Attempting to access another tenant's data returns:

{
"error": "CROSS_TENANT_ACCESS_DENIED",
"message": "Access to requested resource is forbidden"
}

Tenant list (admin only)

GET /api/admin/tenants

Response:

{
"tenants": [
{ "id": "tenant-abc", "name": "Project Alpha" },
{ "id": "tenant-xyz", "name": "Project Beta" }
]
}

Flow: tenant resolution

Request arrives

┌─────────────────────────────┐
│ Check session for tenantId │
└─────────────────────────────┘

Has tenant?
/ \
Yes No
↓ ↓
Use Check query param
session (requires super-admin)
↓ ↓
└──────┬───────┘

Tenant resolved?
/ \
Yes No
↓ ↓
Execute Return 400
query TENANT_REQUIRED

Common misconceptions

1. "I can switch tenants without re-authenticating"

Reality: Tenant context is set at login time. To switch tenants, you must log out and log back in, selecting the new tenant.

2. "Admin Viewer can make changes if they know the API"

Reality: Role enforcement happens at the API layer, not just the UI. ADMIN_VIEWER requests to write endpoints are rejected.

3. "Portal Users can access the admin console if they know the URL"

Reality: The AdminAuthGuard component checks both authentication and role. CM_USER role is explicitly denied access to /admin routes.

4. "Tenants share the same requirements"

Reality: While the same pack definition might be used by multiple tenants, each tenant has its own binding. Criteria can be customized per tenant.

5. "Environment affects tenant isolation"

Reality: Environments (DEV/STAGING/PROD) affect configuration activation, not tenant isolation. A tenant's data remains isolated across all environments.