Skip to main content
Identity answers who an agent is. Scopes answer what it may do. A platform uses scopes to grant each agent precise, least-privilege access — and the middleware enforces them on every request.

What a scope is

A scope is a short permission string, by convention resource:action:
  • items:read
  • items:write
  • orders:cancel
A platform decides which scopes exist and which routes require them. An agent’s token carries only the scopes it was granted. To reach a protected route, the token must carry that route’s scope.

The scopes file

Each platform has a mudraid_scopes.yaml that maps every route to one of three outcomes. The middleware reads it and enforces it.
platform_id: 4f8e9c1d-2b4f-5e6a-7b8c-9d0e1f2a3b4c
version: 1
routes:
  - method: GET
    path: /health
    public: true            # anyone may call it — no token needed
  - method: GET
    path: /api/v1/items
    scope: items:read        # token must carry items:read
  - method: GET
    path: /api/v1/items/{item_id}
    scope: items:read
  - method: POST
    path: /api/v1/items
    scope: items:write       # token must carry items:write
The three choices per route:
SettingMeaning
scope: <name>The token must carry this scope, or the request is denied.
public: trueNo token required. For health checks, status pages, anything open.
skip: trueThe route returns 404 — it’s invisible to agents.
A route with no rule at all also returns 404. A hidden route and a route that doesn’t exist look identical to a caller — on purpose, so agents can’t probe for what’s there.

Path parameters

Use the framework’s brace syntax for variable path segments:
  - method: GET
    path: /api/v1/items/{item_id}
    scope: items:read
The middleware matches the shape of the path, so /api/v1/items/42 and /api/v1/items/abc both match this rule.

Scopes are flat — no implicit hierarchy

This is the rule that surprises people, so it’s worth stating plainly: holding one scope never implies another. items:write does not include items:read. If a route needs both, the agent must be granted both, and the route must require both. It looks stricter than necessary, but it removes a whole class of accidental over-permissioning. There’s no hidden ladder where a “higher” scope quietly unlocks “lower” ones. What you grant is exactly what the agent can do.

Naming scopes well

  • Use resource:action — readable and predictable.
  • Keep them specific: orders:read, orders:write, orders:cancel.
  • These names are what an agent’s owner sees when granting access, so make them self-explanatory.

The portal is the source of truth

The MudraID portal decides which agent gets which scope on which platform, and it generates the mudraid_scopes.yaml. The middleware trusts that file. To change what your routes require: update scopes in the portal, re-export the file, and redeploy your service. The middleware reads the file once at startup — it doesn’t hot-reload — so changes take effect on the next deploy.

A worked example

A small orders API:
routes:
  - method: GET
    path: /health
    public: true
  - method: GET
    path: /api/v1/orders
    scope: orders:read
  - method: GET
    path: /api/v1/orders/{order_id}
    scope: orders:read
  - method: POST
    path: /api/v1/orders
    scope: orders:write
  - method: POST
    path: /api/v1/orders/{order_id}/cancel
    scope: orders:cancel
  - method: GET
    path: /internal/metrics
    skip: true
A reporting agent granted only orders:read can list and read orders, but its calls to create or cancel are denied — and /internal/metrics is invisible to it entirely.

Next