> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mudraid.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Platforms and scopes

> How a platform decides what each agent is allowed to do, per route.

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.

```yaml theme={null}
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:

| Setting         | Meaning                                                            |
| --------------- | ------------------------------------------------------------------ |
| `scope: <name>` | The token must carry this scope, or the request is denied.         |
| `public: true`  | No token required. For health checks, status pages, anything open. |
| `skip: true`    | The 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:

```yaml theme={null}
  - 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:

```yaml theme={null}
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

* [Define scopes](/guides/platform-operator/define-scopes) — designing your scope set.
* [Manage the scopes lifecycle](/guides/platform-operator/scopes-lifecycle) — how changes roll out.
