Roles & Permissions (RBAC)
ShieldAgent enforces role-based access control on every API endpoint. This page documents the built-in roles, the permission matrix, and the exact permission required by each endpoint.
Overview
Every authenticated request is checked against the caller's role within the target tenant. Permissions follow a resource:action pattern (e.g. agent:write). Platform admins bypass tenant scoping and have all permissions. Tenant-scoped roles are assigned per-tenant, so a user can be an auditor in one tenant and a tenant_admin in another.
The permission cache has a 60-second TTL with graceful degradation — if the database is temporarily unreachable, stale permissions are served rather than denying access.
Built-in Roles
ShieldAgent ships with six system roles. Custom roles are not yet supported — assignments use these built-in roles.
| Role | Scope | Description |
|---|---|---|
| platform_admin | Platform | Full platform access across all tenants. Reserved for ShieldAgent operators. |
| tenant_admin | Tenant | Full management of one tenant: users, settings, billing, all sub-resources. |
| security_operator | Tenant | Monitoring, incident triage, alert management, risk review. |
| auditor | Tenant | Read-only access to compliance reports, audit trail, and export. |
| aiops_engineer | Tenant | Agent lifecycle, MCP server/binding config, policy authoring. |
| viewer | Tenant | Read-only dashboard and summary data. |
Permission Matrix
The matrix below shows every permission and which roles include it. platform_admin and tenant_admin have all permissions and are omitted for readability.
| Permission | Description | sec_op | auditor | aiops | viewer |
|---|---|---|---|---|---|
| agent:read | View / list agents | ✓ | ✓ | ✓ | ✓ |
| agent:write | Create or update agents | — | — | ✓ | — |
| agent:delete | Remove agents | — | — | ✓ | — |
| agent:configure | Modify agent settings / baselines | — | — | ✓ | — |
| policy:read | View / list policies | ✓ | ✓ | ✓ | ✓ |
| policy:write | Create or update policies | — | — | ✓ | — |
| policy:delete | Remove policies | — | — | ✓ | — |
| audit:read | View audit trail events | ✓ | ✓ | ✓ | — |
| audit:export | Export audit data | — | ✓ | — | — |
| compliance:read | View compliance reports | ✓ | ✓ | — | — |
| compliance:write | Create or update compliance records | — | — | — | — |
| compliance:export | Export compliance data | — | ✓ | — | — |
| risk:read | View risk scores and trending | ✓ | ✓ | ✓ | ✓ |
| risk:configure | Modify risk thresholds / overrides | ✓ | — | ✓ | — |
| incident:read | View incidents | ✓ | ✓ | — | — |
| incident:write | Create or update incidents | ✓ | — | — | — |
| incident:triage | Acknowledge, assign, or resolve | ✓ | — | — | — |
| mcp_server:read | View MCP server registrations | ✓ | — | ✓ | ✓ |
| mcp_server:write | Create or update MCP servers | — | — | ✓ | — |
| mcp_server:delete | Remove MCP servers | — | — | ✓ | — |
| alert:read | View alerts | ✓ | ✓ | ✓ | ✓ |
| alert:write | Create or update alert rules | ✓ | — | ✓ | — |
| alert:delete | Remove alert rules | — | — | — | — |
| alert:triage | Acknowledge or resolve alert events | ✓ | — | — | — |
| review:read | View pending reviews | ✓ | ✓ | ✓ | — |
| review:triage | Approve or reject reviews | ✓ | — | — | — |
| user:read | View user accounts | — | — | — | — |
| user:write | Create or update users and roles | — | — | — | — |
| user:delete | Remove user accounts | — | — | — | — |
| tenant:read | View tenant settings | ✓ | ✓ | ✓ | ✓ |
| tenant:write | Modify tenant settings / billing | — | — | — | — |
| export:read | View export configurations | — | ✓ | — | — |
| export:write | Create or update export configs | — | — | — | — |
| dashboard:read | View aggregated dashboard data | ✓ | ✓ | ✓ | ✓ |
Endpoint Auth Requirements
Every tenant-scoped endpoint requires a valid JWT plus the listed permission. The middleware validates both tenant membership and the specific permission before the handler runs. A 403 Forbidden is returned if the check fails.
Agents
| Method | Path | Permission |
|---|---|---|
| GET | /tenants/:tenantId/agents | agent:read |
| POST | /tenants/:tenantId/agents | agent:write |
| PUT | /tenants/:tenantId/agents/:agentId | agent:write |
| PATCH | /tenants/:tenantId/agents/:agentId | agent:configure |
| DELETE | /tenants/:tenantId/agents/:agentId | agent:delete |
Policies
| Method | Path | Permission |
|---|---|---|
| GET | /tenants/:tenantId/policies | policy:read |
| POST | /tenants/:tenantId/policies | policy:write |
| PUT | /tenants/:tenantId/policies/:policyId | policy:write |
| DELETE | /tenants/:tenantId/policies/:policyId | policy:delete |
Audit Trail
| Method | Path | Permission |
|---|---|---|
| GET | /tenants/:tenantId/audit | audit:read |
| GET | /tenants/:tenantId/audit/verify-chain | audit:read |
Compliance
| Method | Path | Permission |
|---|---|---|
| GET | /tenants/:tenantId/compliance/report | compliance:read |
| GET | /tenants/:tenantId/compliance/controls | compliance:read |
| POST | /tenants/:tenantId/compliance/versions | compliance:write |
Incidents
| Method | Path | Permission |
|---|---|---|
| GET | /tenants/:tenantId/incidents | incident:read |
| POST | /tenants/:tenantId/incidents | incident:write |
| PATCH | /tenants/:tenantId/incidents/:incidentId | incident:triage |
Users & Tenants
| Method | Path | Permission |
|---|---|---|
| GET | /tenants/:tenantId/users | user:read |
| POST | /tenants/:tenantId/users/invite | user:write |
| PATCH | /tenants/:tenantId/users/:userId | user:write |
| GET | /tenants/:tenantId | tenant:read |
| PUT | /tenants/:tenantId | tenant:write |
Middleware Usage
Route handlers attach RBAC checks as Fastify preHandler hooks. Two variants are available:
requireTenantPermission(...perms)
Validates that the caller belongs to the tenant in :tenantId and holds at least one of the listed permissions. Used by all tenant-scoped endpoints.
fastify.get(
'/tenants/:tenantId/agents',
{ preHandler: [authenticate, rbac.requireTenantPermission('agent:read')] },
async (request, reply) => { /* handler */ }
);requirePermission(...perms)
Checks the permission without tenant scoping. Used for platform-level endpoints like policy templates or billing plan listing.
fastify.get(
'/policy-templates',
{ preHandler: [authenticate, rbac.requirePermission('policy:read')] },
async (request, reply) => { /* handler */ }
);Public Endpoints
A small number of endpoints bypass authentication entirely:
Role Assignment
Roles are assigned per user per tenant via the user_roles table. A user can hold different roles in different tenants. Assignments track who granted the role and support optional expiration.
// Assign a role via the API
PATCH /tenants/:tenantId/users/:userId
{
"role": "security_operator"
}
// Requires: user:write permissionLegacy role values (admin, tenant) are automatically normalized to their RBAC equivalents (platform_admin, tenant_admin) at middleware level.