RBAC & Scope System Design¶
The registry uses a two-layer authorization model: scopes control which API endpoints a user can call, and ACL controls which specific resources they can see or modify within those endpoints.
The canonical source of truth for scope definitions is registry-pkgs/src/registry_pkgs/scopes.yml. The enforcement logic lives in registry/src/registry/middleware/rbac.py.
Two-Layer Authorization¶
Scopes are coarse-grained (endpoint access). ACL is fine-grained (per-resource access). Both must pass for a request to succeed on protected resources.
Layer 1 — ScopePermissionMiddleware: loaded from scopes.yml at startup, checks whether the user's token includes the scope required for the requested endpoint/method. Returns 403 immediately if not.
Layer 2 — ACL (resource-level): enforced inside the service layer via access_control_service.py, checks whether the user has VIEW, EDIT, or DELETE permission on the specific resource being accessed.
Roles and Their Scopes¶
Four built-in roles are defined via IdP group mappings in scopes.yml under group_mappings.
| Role | Group Name | Description |
|---|---|---|
| Admin | jarvis-registry-admin | Full access including system ops and ACL management |
| Power User | jarvis-registry-power-user | Full CRUD on all resources + share; no system ops |
| User | jarvis-registry-user | CRUD on servers/agents/federations; no sharing or ACL write |
| Read-Only | jarvis-registry-read-only | Read-only access to servers, agents, federations |
Role-to-scope mapping is defined entirely in scopes.yml — no code changes needed to adjust which scopes a role carries.
Scope Catalog¶
Each scope maps to one or more API endpoints. ScopePermissionMiddleware compiles these at startup and enforces them on every authenticated request.
| Scope | What It Covers |
|---|---|
servers-read | List/get MCP servers, check connections, get tools |
server-write | Register, update, delete, toggle MCP servers |
agents-read | List/get A2A agents, skills, health, well-known card |
agents-write | Create, update, delete, toggle A2A agents, sync well-known |
federations-read | List/get federations |
federations-write | Create, update, delete, sync federations |
federations-share | Manage ACL on federation resources |
servers-share | Manage ACL on MCP server resources |
agents-share | Manage ACL on A2A agent resources |
acl-read | Search principals, read resource permissions |
acl-write | Update resource permissions |
user-read | Auth info, tokens, search, MCP connection/OAuth management |
system-ops | Admin stats, token admin, AgentCore runtime sync |
mcp-proxy-ops | MCP proxy and session endpoints |
How Scopes Are Resolved¶
Scopes for an incoming request are resolved from the JWT token via effective_scopes_from_context():
- Explicit scopes in token — if the
scopeclaim is present (e.g., a down-scoped token), those scopes are used as-is. No group mappings are applied, preventing unintended privilege escalation. - Group-derived scopes — if no explicit scopes exist, the user's IdP groups are looked up in
group_mappingsand expanded into their scope list.
This lets agents and services operate with minimally scoped tokens while human users get their full role-based scope set from their IdP group membership.
Sharing and ACL Independence¶
Sharing scopes (federations-share, servers-share, agents-share) are intentionally decoupled:
- Sharing a federation does not cascade to the servers or agents it synced in. Each resource is shared independently.
- This means a user can have access to a federation definition without automatically gaining access to every resource it produced.
Adding or Modifying Scopes¶
All changes are made in scopes.yml — no code changes required:
- New endpoint: add an entry under the appropriate scope block with
action,method, andendpoint. - New role: add a key under
group_mappingsand list the scopes it should carry. - New scope: define a new top-level key with its endpoint rules, then reference it from roles in
group_mappings.
ScopePermissionMiddleware loads rules at application startup, so a service restart is required after any change to scopes.yml.