{
  "docId": "baas.iam.authz.superadmin",
  "title": "Superadmin",
  "summary": "The iam.svc control plane — the reserved superadmin tenant, operator login, the role and grant model, the tenant pin that refuses a plane token on a tenant surface, and the complete guard inventory.",
  "url": "https://docs.kis.ai/blocks/baas/iam/authz/superadmin/",
  "markdown": "https://docs.kis.ai/blocks/baas/iam/authz/superadmin.md",
  "product": "iam",
  "area": "guide",
  "documentType": "guide",
  "status": "active",
  "authority": "canonical",
  "visibility": "public",
  "documentationVersion": "2026.07",
  "productVersion": "2.0.0",
  "apiContractVersion": null,
  "httpPathVersioning": "none",
  "intent": [],
  "audience": [],
  "lastModified": "2026-09-09",
  "lastReviewed": null,
  "owners": [],
  "appliesTo": [
    ">=2.0.0 <2.1.0"
  ],
  "prerequisites": [],
  "related": [],
  "supersedes": [],
  "supersededBy": null,
  "ai": {
    "discoverable": true,
    "retrievable": true,
    "authoritative": true,
    "chunking": "heading",
    "answerableQuestions": []
  },
  "headings": [
    {
      "depth": 2,
      "text": "The plane",
      "id": "the-plane"
    },
    {
      "depth": 2,
      "text": "Getting an operator token",
      "id": "getting-an-operator-token"
    },
    {
      "depth": 3,
      "text": "Bootstrap the first operator",
      "id": "bootstrap-the-first-operator"
    },
    {
      "depth": 3,
      "text": "Operator login",
      "id": "operator-login"
    },
    {
      "depth": 2,
      "text": "Membership is not authority",
      "id": "membership-is-not-authority"
    },
    {
      "depth": 3,
      "text": "Exactly which rule admits which role",
      "id": "exactly-which-rule-admits-which-role"
    },
    {
      "depth": 2,
      "text": "Grants: which tenants",
      "id": "grants-which-tenants"
    },
    {
      "depth": 3,
      "text": "An ungranted tenant is invisible",
      "id": "an-ungranted-tenant-is-invisible"
    },
    {
      "depth": 2,
      "text": "A plane token is not a master key",
      "id": "a-plane-token-is-not-a-master-key"
    },
    {
      "depth": 2,
      "text": "The operator surfaces",
      "id": "the-operator-surfaces"
    },
    {
      "depth": 3,
      "text": "Tenant registry",
      "id": "tenant-registry"
    },
    {
      "depth": 2,
      "text": "The one door into tenant data",
      "id": "the-one-door-into-tenant-data"
    },
    {
      "depth": 2,
      "text": "Guard inventory",
      "id": "guard-inventory"
    },
    {
      "depth": 3,
      "text": "Enforced",
      "id": "enforced"
    },
    {
      "depth": 2,
      "text": "Not implemented",
      "id": "not-implemented"
    },
    {
      "depth": 2,
      "text": "Errors",
      "id": "errors"
    },
    {
      "depth": 2,
      "text": "Continue with",
      "id": "continue-with"
    }
  ],
  "wordCount": 3482,
  "body": "A superadmin is an **operator identity** that lives in a reserved control plane, not in any tenant. It administers the tenant registry and the operator roster for one product in one environment. It is not a tenant user with extra roles, and its token is not a master key: a control-plane token presented on a tenant's surface is rejected.\n\n## The plane\n\nThe control plane is the reserved CPET whose **tenant** slot is the literal name `superadmin`:\n\n```text\n<customer>:<product>:<env>:superadmin\n```\n\nThe plane a request can reach is a pure function of the CPET the request arrived with — the tenant slot is replaced, customer/product/env are kept. `acme:shop:prod:main` resolves to `acme:shop:prod:superadmin` and to nothing else. **There is no deployment-global plane.** Two products, or two customers, sharing one database get different planes, in different schemas, signing with different keys. An operator of `acme:shop:prod` is not an operator of `globex:crm:prod` and cannot address that plane at all.\n\n| Property | Value |\n|---|---|\n| Address | `<customer>:<product>:<env>:superadmin` |\n| Scale | one plane per customer + product + environment |\n| PostgreSQL schema | `superadmin` (hardcoded) |\n| Signing keys | the plane's own keyring — the plane's JWKS carries no tenant's keys |\n| Entities | `superadmin` (operator roster), `superadmin_grant`, `tenant` (registry), `tenant_eventlog` |\n| Deployment | a reserved hive binding; unresolvable = `503 superadmin_not_deployed` on every plane route |\n\n`customer` and `product`, and their prior spellings `superadmin` and `shared`, are reserved scope names. All are refused — case-insensitively, trimmed — in **any** CPET segment and in any tenant short name, in every deployment, whether or not the scope is deployed.\n\nPlane entities declare `scope: customer` (the prior `superadmin` is folded) and `final: true`, so they sit in the reserved schema and are sealed against product and tenant schema layering. See [Identity entities](/blocks/baas/iam/identity/entities/) for the tenant-side model.\n\n:::note\n`503 superadmin_not_deployed` covers every failure to resolve the plane's engine, not only an absent hive binding: an unregistered product, a tenant config the running service cannot fetch, and a pool that will not build all answer the same way. The response is deliberately vague — an unauthenticated caller learns nothing about which planes exist — and the real cause is written to the service log.\n:::\n\n## Getting an operator token\n\n### Bootstrap the first operator\n\n`POST /auth/bootstrap` creates exactly one operator, seeded with `properties.roles: [\"root\"]`, in the plane governing the request's product and environment. It is gated by the `bootstrap.token` config key, compared in constant time.\n\n```bash\ncurl -s localhost:5030/auth/bootstrap \\\n  -H 'Content-Type: application/json' \\\n  -H 'X-Customer: acme' -H 'X-Product: shop' \\\n  -H 'X-Env: prod'     -H 'X-Tenant: superadmin' \\\n  -d '{\"token\":\"<bootstrap.token>\",\"email\":\"root@ops.io\",\"password\":\"…\",\"firstname\":\"Root\",\"lastname\":\"Operator\"}'\n```\n\n```json\n{\"ok\": true, \"id\": \"U01…\", \"email\": \"root@ops.io\"}\n```\n\nAn **empty `bootstrap.token` disables the endpoint entirely**. Token unset, token mismatch, and \"an operator already exists\" all return the same `403 already_bootstrapped`, so a stranger probing the endpoint learns nothing about which of the three it hit. Rotate or remove the secret once the first operator exists. The route carries no rate-limit profile; the constant-time compare and the single envelope are its only protection.\n\nThe CLI equivalent seeds through the same core — see [Operating](/blocks/baas/iam/operating/configuration/).\n\n### Operator login\n\n`POST /auth/login` with `\"realm\":\"superadmin\"` (matched case-insensitively) under the plane's tenancy headers. The handler resolves the plane from the request CPET, reads the `superadmin` entity, verifies argon2id, mints with the plane's own keyring, and issues a refresh token in the plane's own table.\n\n```bash\ncurl -s localhost:5030/auth/login \\\n  -H 'Content-Type: application/json' \\\n  -H 'X-Customer: acme' -H 'X-Product: shop' \\\n  -H 'X-Env: prod'     -H 'X-Tenant: superadmin' \\\n  -d '{\"identity\":\"root@ops.io\",\"password\":\"…\",\"realm\":\"superadmin\"}'\n```\n\n```json\n{\n  \"token\": \"v4.public.…\",\n  \"refresh_token\": \"…\",\n  \"expires_in\": 900,\n  \"user\": {\"id\": \"U01…\", \"email\": \"root@ops.io\", \"displayname\": \"Root\"}\n}\n```\n\nThe claims carry `tenant: acme:shop:prod:superadmin`, `realm: superadmin`, `type: user`, and the `roles` array described below. `/auth/refresh` and `/auth/logout` work unchanged for the plane when the client sends the plane's headers.\n\n| Field | Value |\n|---|---|\n| `identity` | An operator identifier: `email` (the default `identity_type`) or `mobile`. Both unique, both `nolog` |\n| `identity_type` | Optional. Anything not declared on the `superadmin` entity → `400 bad_identity_type` |\n| `realm` | `superadmin`, case-insensitive. Any other value goes to the ordinary tenant realm machinery |\n\nThe lookup filters on `active: true`. Identity not found, no stored hash, and a failed argon2 verify all return the same `401 invalid_credentials`.\n\n:::caution\nOperator login has **no MFA and no lockout**. The plane carries no MFA tables, and although the `superadmin` entity has a `locked` field, nothing in the login path reads it. The only brake is the `auth_login` rate-limit profile — token bucket, capacity 30, refill 0.5/s, keyed on tenant plus caller IP — and its default store is in-memory and per instance, so the cap is per instance rather than fleet-wide. Supply a distributed-store limiter for a fleet-wide limit.\n:::\n\n## Membership is not authority\n\nAt mint, `realm == \"superadmin\"` forces the single role `superadmin`. That marker is derived from the **realm**, never from row data, and it is what route gates read. The operator row's `properties.roles` are then **unioned** on top — union, not assignment, so a row declaring roles of its own does not erase the marker.\n\nThe marker grants **reach and nothing else**: it admits an operator to the plane's routes, not to the plane's data. Every plane entity gates on a granular role, so a fresh operator with no roles and no grants authenticates successfully and can do nothing to anyone. That is the authority floor — deny by default, with the marker at zero.\n\n| Role | Authority |\n|---|---|\n| `superadmin` | Plane membership marker. Route reach only; no data access |\n| `root` | Everything on every plane entity, including deletes; an implicit grant over tenants |\n| `superadmin-admin` | Roster read/create/update (**not** delete); full grant CRUD |\n| `tenant-provision` | Registry read/create/update; grant read |\n| `tenant-read` | Registry read; grant read — a read-only auditor |\n| `impersonate-readonly` | `POST /superadmin/impersonate` with `read_write` false |\n| `impersonate` | `POST /superadmin/impersonate` with `read_write` true |\n| `iam-service` | The service's own trusted handlers. Not assignable to a person |\n\nRoles are plain strings in `properties.roles`. **There is no role catalog entity for operator roles** — a role exists because a rule names it. Grant them lowercase-exact: the impersonation handler compares role strings byte-for-byte, so `Root` does not satisfy a check for `root`, while the platform route gate lowercases and trims.\n\n### Exactly which rule admits which role\n\n| Entity | read | create | update | delete | unmask |\n|---|---|---|---|---|---|\n| `superadmin` | `iam-service`, `root`, `superadmin-admin` | same | same | `iam-service`, `root` | `iam-service` only |\n| `superadmin_grant` | `iam-service`, `root`, `superadmin-admin`, `tenant-read`, `tenant-provision` | `iam-service`, `root`, `superadmin-admin` | same as create | same as create | — |\n| `tenant` | `iam-service`, `root`, `tenant-read`, `tenant-provision` | `iam-service`, `root`, `tenant-provision` | same as create | `iam-service`, `root` | — |\n\nGrant `read` is deliberately open to anyone who may read the registry: an operator can see what they have been given. It exposes the mapping, never tenant data.\n\n`unmask` on the roster is `iam-service`-only, even for `root`. Managing an operator account and holding its credential are different powers; the `password` field also carries `redact`, so the at-rest argon2id hash never leaves over REST or the admin surface.\n\n:::caution\n`root` may update the roster, and roles live in the row, so **root can add `impersonate` or `impersonate-readonly` to its own row**. The explicit-grant requirement still converts a silent capability into a recorded act, but root is not prevented from self-granting.\n:::\n\n## Grants: which tenants\n\nRoles say what kind of operation an operator may perform. A `superadmin_grant` row says which tenants they may perform it on. Both are deny-by-default.\n\n| Field | Type | Notes |\n|---|---|---|\n| `superadmin_id` | string, required | The operator's `superadmin` row id, in this plane. `final` |\n| `tenant_id` | string, required | A `tenant` **registry row id**, not a CPET — ids survive a rename. `final` |\n| `reason` | string, max 500 | Why this operator holds this grant |\n| `active` | boolean, default `true` | Soft revoke |\n\nUnique index `superadmin_grant_uq` on (`superadmin_id`, `tenant_id`) makes re-granting idempotent instead of leaking duplicate rows a revoke would then have to chase; `superadmin_grant_operator_idx` covers lookups by operator. Both id columns are `final`: a grant is revoked and re-granted, never moved.\n\nThe table is **flat — there is no per-tenant role column**. \"Provisioner on t1, read-only on t2\" is not expressible.\n\n`root` needs no rows: the role carries an implicit grant over the tenants in its own plane.\n\n### An ungranted tenant is invisible\n\nThe `tenant` entity carries a row-level-security rule bound to `user.grants`:\n\n```yaml\nrls:\n  read:\n    language: expr\n    expression: '(\"iam-service\" in user.roles || \"root\" in user.roles) ? \"\" : user.grants'\n```\n\nOn a control-plane engine only, the RLS evaluator loads the operator's granted tenant ids **per request** from `superadmin_grant WHERE superadmin_id = $1 AND active = true`. Flipping `active` to false takes effect on the very next request rather than at token expiry. Tenant engines get no loader and pay nothing for the binding.\n\nThe semantics are fail-closed:\n\n| Rule returns | Effect |\n|---|---|\n| `\"\"` | Unrestricted — `iam-service` and `root` only |\n| A list of ids | `id IN (…)` over the granted tenants |\n| An empty list | `id IN (\"\")` — matches no row. \"Granted nothing\" means \"sees nothing\" |\n| Anything not a string or list | Denied. It does not fall through to unrestricted |\n| A grant-load failure | An error, never an empty set |\n\nAn ungranted tenant is not merely unactionable: the operator cannot learn its existence, name, or count. Absence is the right failure mode for a control plane, because the alternative is handing out a customer list. The general mechanism is documented on the [Data API access-rules page](/blocks/baas/data-api/security/access-rules/).\n\n:::caution\nThis enforcement lives on the **engine**, so it covers the generic entity surface (`/rest/tenant`, `/admin/*`). It does **not** cover the purpose-built `/superadmin/registry/*` routes, which issue their engine calls as the internal `iam-service` principal. See [Guard inventory](#guard-inventory).\n:::\n\n## A plane token is not a master key\n\nEvery token is verified against the keyring of the tenant it targets and pinned to it: the token's `tenant` claim must equal the request's CPET. **There is no superadmin exception**, in the service's own middleware or in the shared platform PASETO/JWT verifier. Presenting an operator's plane token with a tenant's headers returns `401 tenant_mismatch`.\n\nTwo bypasses previously existed and are **deleted**:\n\n- A client-supplied `X-SuperAdmin` request header, trusted with no corroboration. Holding a valid token for your own tenant, aiming it at another tenant's domain and adding the header served you as the victim. The gateway set the header inside a gated branch but never stripped an inbound one, so it was reachable across customers.\n- A verified plane-token predicate that admitted a plane token onto tenant surfaces within the request's own product space.\n\nNeither is a supported pattern, and neither header nor predicate exists to re-enable. Any document, client, or runbook that presents a plane token on a tenant CPET is describing removed behaviour.\n\nOperator-gated routes are reached with the **plane's own headers**. On the shared operator surface (`/superadmin/tenant/:tenant/*`) the `:tenant` path slot takes a **bare tenant name**, composed onto the caller's own customer:product:env; a full CPET in that slot is rejected. A cross-product target is unrepresentable there rather than merely refused.\n\n:::note\nThis service mounts every surface with an empty prefix. The real paths are `/superadmin/…`, `/admin/…`, `/rest/…`, `/auth/…`, `/internal/session/…` — there is no `/v2/` prefix, whatever the shared library's own comments say. Wire shapes for all of them are in the [endpoint reference](/blocks/baas/iam/api/).\n:::\n\n## The operator surfaces\n\nEverything an operator token reaches, and what gates it.\n\n| Surface | Gate |\n|---|---|\n| `POST /auth/login` (`realm: superadmin`), `/auth/refresh`, `/auth/logout` | None — this is the auth surface. `auth_login` rate limit on login |\n| `POST /auth/bootstrap` | `bootstrap.token`, constant-time; self-closing |\n| `POST /superadmin/impersonate` | Verified operator principal, exact impersonation role, grant, per-user consent — all in the handler, no route-level role gate |\n| `POST`/`GET /superadmin/registry/tenants`, `POST …/provision` | Platform token wrapper + `RequireSuperadmin` (the membership marker) only |\n| `GET /superadmin/tenant` | Platform token wrapper + `RequireSuperadmin` |\n| `/superadmin/tenant/:tenant/*` — cache bust, seeds, applied migrations, DDL plans and apply, locks and force-release, actions, references, query hashes | Platform token wrapper + `RequireSuperadmin`; `:tenant` composed onto the caller's own c:p:e |\n| `/superadmin/tenant/:tenant/retention/*`, `/materialize/*` | Registered through the shared surface but unsupported on this service |\n| `/rest/tenant`, `/rest/superadmin*`, `/admin/*` | The real operator principal — granular roles **and** the grant RLS both bite here |\n\n`RequireSuperadmin` reads the `roles` claim and asks only whether `superadmin` is present, matched case-insensitively; `admin` does not qualify, and `admin` routes accept `superadmin` as well. Denials are `403 requires_superadmin` and `403 requires_admin`.\n\nDDL apply on this service touches signing-key, credential and identity tables, which is why it is operator-gated rather than tenant self-service.\n\n### Tenant registry\n\n| Method | Path | Behaviour |\n|---|---|---|\n| `POST` | `/superadmin/registry/tenants` | Register a tenant. Sets `active: true`, `markedforseed: true`. `201` |\n| `GET` | `/superadmin/registry/tenants` | List, capped at 500, projecting `id`, `slug`, `displayname`, `active` |\n| `POST` | `/superadmin/registry/tenants/provision` | Create schema and tables, seed the tenant admin, activate. Idempotent |\n\n```bash\ncurl -s localhost:5030/superadmin/registry/tenants \\\n  -H \"Authorization: Bearer $OPERATOR_TOKEN\" \\\n  -H 'Content-Type: application/json' \\\n  -H 'X-Customer: acme' -H 'X-Product: shop' \\\n  -H 'X-Env: prod'     -H 'X-Tenant: superadmin' \\\n  -d '{\"slug\":\"acme:shop:prod:eu1\",\"displayname\":\"Acme EU\"}'\n```\n\n```json\n{\"id\": \"01J…\", \"slug\": \"acme:shop:prod:eu1\"}\n```\n\nA slug is accepted either as a bare short name or as a full `customer:product:env:tenant` key; reserved scope names are refused in either spelling (`400 reserved_or_invalid_name`). Registration records a tenant; provisioning brings it live. Provisioning requires the tenant's datastore to already exist in the tenant hive and the **database** to exist — the service creates the schema, never the database. See [Operating](/blocks/baas/iam/operating/configuration/).\n\n:::caution\nThe registry slug is the one place the two halves of the system disagree about what a slug is. Provisioning looks the registry up by **full CPET**; the impersonation grant check looks it up by the **tenant segment alone**. Store slugs one way and one of the two paths misses. See [Guard inventory](#guard-inventory).\n:::\n\n:::caution\nAll three registry routes are gated by the membership marker alone. Read the guard inventory before treating operator roles or grants as a control over them.\n:::\n\n## The one door into tenant data\n\nAn operator's plane token is refused on every tenant surface, and no tenant entity grants operator roles. An operator holding `root` plus a grant on every tenant in their plane still reads zero rows of customer data directly. The single sanctioned path is `POST /superadmin/impersonate`, which mints the **target user's own tenant-signed token** carrying an `act` claim that names the operator and a `readonly` or `readwrite` mode.\n\n| Role | Mints | Implied by |\n|---|---|---|\n| `impersonate-readonly` | `mode: readonly` | nothing |\n| `impersonate` | `mode: readwrite` | nothing |\n\nNeither is implied by `root`, by `superadmin-admin`, or by the other, and matching is case-sensitive. The read-only mode is enforced by a floor in the data layer's access gate that refuses `create`, `update`, `delete`, `purge` and `restore` before any entity rule runs — a property of the credential, not a rule an entity can opt out of. Anything the verifier cannot read as exactly `readwrite` falls closed to read-only.\n\nThe full flow — the twelve ordered checks, the consent gate, the claim shape, audit attribution, and how a session is terminated — is on [Impersonation](/blocks/baas/iam/authz/impersonation/).\n\n## Guard inventory\n\n### Enforced\n\n| Guard | Covers |\n|---|---|\n| Reserved-name refusal | `superadmin` / `shared` rejected in any CPET segment and any tenant short name, deployed or not |\n| Plane derivation | The plane is a pure function of the request CPET; another customer's or product's plane is unaddressable |\n| Keyring isolation | The plane signs with its own keys; its JWKS carries no tenant's keys |\n| Realm-derived marker | Plane membership comes from the realm, never from row data |\n| Role union | Row roles are unioned onto the marker, so granular privileges never cost an operator their membership |\n| Deny-by-default plane entities | A fresh operator holds the marker only, and every plane entity refuses them |\n| Grant RLS on the registry | Per-request grant load; empty grants match no row; non-string/non-list returns deny; a load failure is an error |\n| Tenant pin | Token `tenant` claim must equal the request CPET, with no superadmin exception, in both the service and the shared middleware |\n| Bare tenant slot | `/superadmin/tenant/:tenant/*` composes the slot onto the caller's own c:p:e; a full CPET is rejected |\n| Exact-role impersonation check | Byte-for-byte; read-write does not imply read-only, and `root` implies neither |\n| Grant check on impersonation | Non-`root` operators need an active grant on the target tenant |\n| Per-user consent | `users.allow_impersonation`, default false, structurally unwritable by an operator |\n| Reserved `act` claim | Refused at parse and skipped at mint; a tenant cannot inject or shadow an actor |\n| Read-only floor | Checked above every early return in the access gate; an unreadable mode falls closed to read-only |\n| Operator attribution | Audit `actor` is the operator (`changed_by` stays the target user), written into the tenant's own schema |\n| Credential separation | `unmask` on the roster is `iam-service`-only; `password` is hashed and redacted |\n| No enumeration oracle | `not_granted` is identical for \"no grant\" and \"no such tenant\"; `already_bootstrapped` covers three distinct causes; `invalid_credentials` covers three |\n| Constant-time bootstrap compare | The bootstrap secret is compared without a timing signal, and an empty value disables the route |\n\n## Not implemented\n\nSpecified, with no shipping code path. **Do not design against them.**\n\n| Feature | State |\n|---|---|\n| Per-tenant operator roles | The grant table is deliberately flat — no role column |\n| Impersonation start notification | No notification is sent when an impersonation begins |\n| Step-up re-authentication before impersonation | The handler requires a role and a reason |\n| MFA and lockout for operator login | The operator plane carries no MFA tables |\n\nIf your process needs any of these, build it around the endpoint rather than expecting it from\nthe endpoint — an operator runbook, an out-of-band notification, or an approval step in front.\n\n## Errors\n\nControl-plane codes. Impersonation's own set is on [Impersonation](/blocks/baas/iam/authz/impersonation/#errors).\n\n| Code | HTTP | Meaning |\n|---|---|---|\n| `bad_request` | 400 | Malformed JSON body |\n| `no_tenant` | 400 | No tenancy on the request, so the plane cannot be resolved (401 from the token middleware) |\n| `bad_identity_type` | 400 | `identity_type` is not a declared operator identifier |\n| `missing_fields` | 400 | Bootstrap body missing token, email, password, firstname or lastname |\n| `reserved_or_invalid_name` | 400 | Registry slug used a reserved scope name, or is not a valid CPET |\n| `invalid_credentials` | 401 | Operator not found, no stored hash, or argon2 verify failed — one envelope for all three |\n| `tenant_mismatch` | 401 | Token `tenant` claim does not equal the request tenant. This is the guard that refuses a plane token on a tenant surface |\n| `invalid_token` | 401 | Verification failure; also covers the tenant mismatch in the shared middleware |\n| `token_expired` | 401 | Outside the validity window. A token with no `exp` is invalid — every mint sets one |\n| `already_bootstrapped` | 403 | Token unset, token mismatch, or an operator already exists |\n| `requires_superadmin` | 403 | The roles claim does not contain `superadmin` (case-insensitive; `admin` does not qualify) |\n| `requires_admin` | 403 | The `/admin` gate. `superadmin` passes it too |\n| `user_query_failed` | 500 | Operator identity lookup failed against the plane engine |\n| `sign_failed` | 500 | Could not mint the operator's access token |\n| `issue_failed` | 500 | Could not issue the operator refresh token |\n| `persist_failed` | 500 | Bootstrap could not create the first operator |\n| `create_failed` / `list_failed` | 500 | Registry create or list failed at the engine |\n| `provision_failed` | 500 | Provisioning failed. Operator-fixable causes remap to `400 bad_request`, an unresolvable plane to `503 superadmin_not_deployed` |\n| `superadmin_not_deployed` | 503 | The plane's engine could not be resolved for this product and environment |\n\nThe full external table is on [Errors](/blocks/baas/iam/error/).\n\n## Continue with\n\n- [Impersonation](/blocks/baas/iam/authz/impersonation/) — the one door into tenant data, end to end\n- [Tokens](/blocks/baas/iam/tokens/) — claims, reserved claims, PASETO minting, managed sessions\n- [Identity entities](/blocks/baas/iam/identity/entities/) — `users`, `session`, and the tenant-side model\n- [Operating](/blocks/baas/iam/operating/configuration/) — bootstrap, tenant hive config, migrations, event-hook wiring\n- [Endpoint reference](/blocks/baas/iam/api/) — every route with its wire shape"
}