Skip to content

Scopes

A scope is a string that names a specific action an agent is allowed to take. Every DelegationCert.scope field is a list of scope strings. The verifier checks that the action being attempted is covered by the effective scope of the delegation chain.

┌──────────────────────────────────────────────────────────────┐
│ Ratify v1 scope vocabulary │
│ │
│ 52 canonical scopes + 14 wildcards + custom: extension │
└──────────────────────────────────────────────────────────────┘

Without one, every integrator invents their own scope strings and you get the OAuth scope nightmare: read, read:files, files.read, Files.Read, FILE_READ, etc., all meaning the same thing and none cross-compatible. Ratify fixes this for v1: the protocol defines the canonical set, every SDK exports the same constants, every verifier knows the same vocabulary.

If you need an application-specific scope outside the canonical set, use the custom: prefix (see the bottom of this page).

Scopes are grouped by domain. Within a domain, wildcards expand to every concrete scope.

DomainConcrete scopesWildcardTypical use
meeting:attend, speak, video, chat, share_screen, recordmeeting:*Zoom/Teams/Meet agents
voice:inbound, outbound, transfer, record, dtmfvoice:*SIP / WebRTC voice agents
api:read, write, admin, deleteapi:*API gateways, MCP servers
files:read, write, delete, sharefiles:* ⚠️ sensitiveFile-system / cloud-drive agents
calendar:read, write, delete, sharecalendar:*Calendar assistants
email:read, send, deleteemail:* ⚠️ sensitiveEmail agents
payment:query, initiate, approvepayment:* ⚠️ sensitive — no wildcardFinancial agents
commerce:browse, purchase, returncommerce:*Shopping agents
identity:present, prove, vouchidentity:*Identity-vouching agents
system:execute, install, configuresystem:* ⚠️ sensitiveSysadmin agents
physical:enter, move, pickup, dropoff, actuatephysical:* ⚠️ sensitive — geo-gatedDrones, robots
vehicle:drive, unlock, startvehicle:* ⚠️ sensitiveVehicle agents
mcp:tool, resource, promptmcp:*Model Context Protocol
a2a:negotiate, commit, reporta2a:*Agent-to-agent transactions

52 concrete scopes + 14 domain wildcards + the custom: extension pattern = the complete vocabulary.

Some scopes are flagged sensitive in the canonical vocabulary:

  • files:write, files:delete, files:share
  • email:send, email:delete
  • payment:initiate, payment:approve
  • system:execute, system:install, system:configure
  • physical:* (all)
  • vehicle:drive, vehicle:unlock, vehicle:start

Sensitive scopes cannot be granted via wildcard expansion. If a delegation has scope: ["files:*"], it expands to files:read and files:share but not files:write or files:delete. To grant a sensitive scope, the issuer must enumerate it explicitly: scope: ["files:read", "files:write"].

This is the structural defense against “AI got * and somehow wiped the drive.” Sensitive scopes have to be intentionally typed.

Some domains are too sensitive even for non-sensitive wildcard expansion. payment:* is rejected by the verifier — payment scopes must always be enumerated. Same for the “no-wildcards-period” group: physical:* wildcards exist but every concrete physical: scope is sensitive and must be enumerated.

When Alice delegates to Agent-A and Agent-A sub-delegates to Agent-B, the effective scope of Agent-B’s chain is the intersection across every link.

Alice → Agent-A
scope: [meeting:*] meeting:*
expands to →
┌──────────────────┐
│ meeting:attend │
│ meeting:speak │
│ meeting:video │
│ meeting:chat │
│ meeting:share_ │
│ screen │
└──────────────────┘
Agent-A → Agent-B ┌──────────────────┐
scope: [meeting:attend, meeting:speak] │ meeting:attend │
│ meeting:speak │
└──────────────────┘
Effective scope at Agent-B: ┌──────────────────┐
│ meeting:attend │
│ meeting:speak │
└──────────────────┘
Note: meeting:record was NOT in either delegation's scope after expansion,
so it's not in the effective scope. Even if Alice intended to grant it,
she wrote meeting:* which expands to the 5 non-sensitive scopes only.

This is why the canonical vocabulary matters. The verifier knows exactly what each scope means, how each wildcard expands, and which scopes are sensitive. Implementations of expandScopes() and intersectScopes() are byte-identical across all four SDKs and validated by the conformance fixtures.

For application-specific rights not covered by the canonical set, use the custom: extension pattern:

custom:<your-namespace>:<verb>[:<resource>]

Examples:

custom:acme:inventory:read
custom:acme:warehouse:pickup
custom:bigco:hr:approve_pto
custom:globex:trading:execute

The verifier treats custom scopes as opaque strings. It checks them with literal string equality (no wildcard expansion, no domain semantics). The application is responsible for interpreting what its own custom scopes mean.

Custom scopes are not sensitive by default. If your application has a custom scope that should never ride a wildcard, just don’t ever issue a wildcard delegation in the first place; the canonical wildcards are scoped to the canonical domain prefixes only.

import {
SCOPE_MEETING_ATTEND, // "meeting:attend"
SCOPE_FILES_WRITE, // "files:write" — sensitive
expandScopes,
intersectScopes,
isSensitive,
validateScopes,
CUSTOM_SCOPE_PREFIX, // "custom:"
} from "@identities-ai/ratify-protocol";
expandScopes(["meeting:*"]);
// → ["meeting:attend", "meeting:speak", "meeting:video", "meeting:chat", "meeting:share_screen"]
// (NOT meeting:record — that's sensitive)
intersectScopes(["meeting:*"], ["meeting:attend", "meeting:record"]);
// → ["meeting:attend"] (record dropped because not in expanded LHS)
isSensitive("files:write");
// → true
validateScopes(["custom:acme:inventory:read"]);
// → null (valid)
validateScopes(["MEETING:ATTEND"]);
// → "scope must be lowercase: MEETING:ATTEND"

Equivalent constants exist in the Go, Python, and Rust SDKs.

The full scope vocabulary with sensitivity flags and wildcard expansion rules is normative — SPEC.md §9. The vocabulary is frozen for v1.x; any new scope in v1 requires a minor version bump. New sensitive scopes can ship in minor versions only if they default to non-wildcard-eligible.