---
title: RBAC
description: Seven built-in roles, 140-odd permissions, two extra gates, and how to build a custom role that is actually minimal.
---

Permissions are `resource:action`. The console uses them to decide what to
**render**; the API uses them to decide what to **allow**. Only the second one
is authoritative.

The complete list, with which roles hold what, is in
[Permissions and roles](/reference/permissions) — generated from the source, so
it cannot drift.

## The built-in roles

| Role | For |
| --- | --- |
| `OWNER` | Full control, including organization deletion and billing |
| `ADMIN` | Workspaces, people, policies, integrations |
| `SECURITY_ADMIN` | Security, risk, credentials, incidents, emergency controls |
| `DEVELOPER` | Build and operate agents, tools, policies, delegations |
| `APPROVER` | Review and decide approval requests |
| `AUDITOR` | Read-only access to audit, security and compliance evidence |
| `VIEWER` | Read-only operational data |

Roles are a starting point, not a ceiling. Create custom roles when a team needs
a different shape.

## Two extra gates

:::security High impact cannot come from a wildcard
Nine permissions must appear on a role **explicitly**:

`agent:revoke` · `agent:decommission` · `credential:revoke` ·
`policy:activate` · `policy:rollback` · `security:emergency_action` ·
`security:break_glass` · `organization:delete` · `environment:pause`

A role granted "everything in the agent resource" does not get `agent:revoke`.
The reasoning: these are irreversible or have a large blast radius, and a
wildcard is almost always someone being approximate rather than someone deciding
to grant terminal power.
:::

:::security Step-up is required at the moment of use
Thirteen permissions additionally require a fresh re-authentication, regardless
of how old the session is:

`agent:revoke` · `agent:decommission` · `credential:revoke` ·
`credential:rotate` · `policy:activate` · `policy:rollback` ·
`security:emergency_action` · `security:break_glass` · `sso:update` ·
`organization:delete` · `audit:configure_retention` · `environment:pause`

Holding the permission is not enough. An unattended, still-valid browser session
cannot revoke a production agent. [→ Step-up](/secure/step-up)
:::

Note `policy:activate` and `policy:rollback` are on both lists. Activating a
policy version moves the authorization boundary for every agent in scope, which
puts it in the same category as revoking an identity.

## Separation of duties, by default

Three defaults are worth knowing because they are deliberate and people
sometimes "fix" them:

**`SECURITY_ADMIN` cannot approve.** It does not hold `approval:approve`. The
person who wrote the rule requiring approval should not be the person satisfying
it.

**`DEVELOPER` cannot export audit.** It holds `audit:read` but not
`audit:export`. Reading the trail and removing a copy of it are different
privileges.

**`DEVELOPER` cannot record trust evidence.** `trust:manage` belongs to
`SECURITY_ADMIN`. An agent's author should not be the one attesting the agent is
safe.

Grant the overlap explicitly if your organization genuinely wants it, having
decided to.

## Build a minimal custom role

Start from what the task requires, not from a role you trim.

**An on-call responder** — can contain, cannot change the rules:

```json
{
  "key": "on-call-responder",
  "name": "On-call responder",
  "permissions": [
    "agent:read", "agent:pause", "agent:resume",
    "action:read", "security:read",
    "incident:read", "incident:manage",
    "tool:read", "tool:disable",
    "audit:read"
  ]
}
```

No `agent:revoke` — containment should be reversible at 3am. No
`policy:activate` — policy changes belong in a change process, not in an
incident.

**A CI policy gate:**

```json
{
  "key": "ci-policy-gate",
  "name": "CI policy gate",
  "permissions": ["policy:read", "policy:simulate", "agent:read", "tool:read"]
}
```

Enough to run `veefore test`. Not enough to change anything.

**An audit exporter for a SIEM:**

```json
{
  "key": "siem-exporter",
  "name": "SIEM export",
  "permissions": ["audit:read", "audit:export"]
}
```

## Scope a grant to a workspace

A role grant can be limited to one workspace, so the payments team's developers
operate payments agents and cannot touch the logistics team's.

```bash
curl -X POST $CP/v1/members/mbr_01ABC/roles \
  -H "Authorization: Bearer $KEY" -H 'content-type: application/json' \
  -d '{ "role_id": "role_01JAV…", "workspace_id": "wsp_01JAV…" }'
```

Scope grants whenever the team only needs one workspace. It is the cheapest
reduction in blast radius available.

## API keys

An API key carries a role's permissions and no more.

:::danger Never an `OWNER` key in CI
A CI secret lives in your CI provider's storage, is readable by every build step,
and ends up in logs if anyone echoes the environment. `OWNER` includes
`organization:delete`.

Create a custom role with exactly what the key needs. It takes two minutes.
[→ API keys](/build/api-keys)
:::

Step-up permissions are unavailable to API keys, so a key cannot activate a
policy or revoke an agent regardless of its role.

## Policy can see the principal

```json
{ "kind": "condition", "id": "c-principal", "field": "principal.type", "operator": "eq", "value": "API_KEY" }
```

`USER`, `AGENT`, `SYSTEM` or `API_KEY`. Useful for rules that should apply to
automation but not to people, or the reverse.

## Break-glass

`security:break_glass` grants emergency elevated access. High impact, step-up
gated, `CRITICAL` audit events, every use listed at
`GET /v1/security/break-glass`.

:::security Grant it to as few people as possible and review every use
Its whole purpose is to be the thing you notice. Held by half the team, it is
not an emergency mechanism — it is a standing privilege with a dramatic name.

Review the list monthly. Every entry should correspond to an incident somebody
remembers.
:::

## Review it quarterly

Three questions, which cover most of what goes wrong:

**Who holds `security:emergency_action`?** It should be a small, current set.

**Who holds `policy:activate`?** This is the authorization boundary. It should
be a deliberate list.

**Who holds `audit:export`?** Evidence leaving the system should be a short
list.

Then: does anyone hold `OWNER` who does not need to? Is any grant
organization-wide that could be workspace-scoped? Does any role still exist for
a team that no longer does?

SCIM deprovisioning handles leavers automatically if you have it configured.
[→ SSO and SCIM](/secure/sso-and-scim)

## Related

:::cards

:::card Permissions and roles | /reference/permissions
Every permission and role. Generated.
:::

:::card Step-up | /secure/step-up
Why thirteen permissions need re-authentication.
:::

:::card SSO and SCIM | /secure/sso-and-scim
Identity provider integration.
:::

:::card API keys | /build/api-keys
Scoping programmatic access.
:::

:::
