openapi: 3.1.0
info:
  title: TokenTriage Governance Management API
  version: "2.35"
  description: |-
    ★ RETIRED AT MS-B. THIS DOCUMENT IS DEPRECATED. Use docs/api/management.yaml, which is the authoritative whole-product contract and contains every operation in this file, unchanged.

    WHAT THIS FILE IS. The governance-FILTERED view of the TokenTriage Management API contract — the same registry, the same generator, the same epoch and the same two gates (the byte drift golden and the live-mux contract) as management.yaml. It was the governance contract under the VK4-01 plan, before one product had one contract; it is a projection, never a separate source of truth, and it has never been able to disagree with management.yaml about anything.

    WHAT RETIREMENT MEANS HERE, AND WHAT IT DOES NOT. The FILE is retired, not the operations. Every operation documented here is live, supported, and published identically in management.yaml — none of them carries `deprecated: true`, and a client calling any of these routes is calling a supported API. What is going away is this per-resource VIEW of them.

    THE MACHINE-READABLE RECORD is on `info`: x-deprecated-since (when the retirement was announced), x-sunset (when this file stops being regenerated and its final committed state becomes permanent), and x-superseded-by (where the contract went). OpenAPI has no document-level `deprecated` keyword — the flag exists on operations, parameters, schemas and headers and nowhere else — so the B.2 deprecation triple this contract already emits on operations and parameters is reused at document scope rather than a fourth spelling being invented.

    RETIREMENT, NOT DELETION (plan §4 row 6, verbatim: "the file's final committed state says where the contract went"). This file stays in the repository after sunset so that a reader who arrives with a stale link finds a document that tells them where to go, instead of a 404 that tells them nothing.

    MIGRATING: every operationId, path, schema name and problem type here appears in management.yaml unchanged. Point your generator at management.yaml and filter on the `governance` tag if you want the same subset; nothing needs to be renamed.
  x-deprecated-since: "2026-07-25T00:00:00Z"
  x-sunset: "2027-02-01T00:00:00Z"
  x-superseded-by: docs/api/management.yaml
jsonSchemaDialect: https://spec.openapis.org/oas/3.1/dialect/base
tags:
  - name: governance
    description: |-
      Tenancy, virtual keys, limits and overrides, refusal audit, usage rollups, preview, and delegated self-serve — the governance control plane. Every dollar is typed Money (usd null when unpriced); no read returns a secret or hash; every write is audited.

      THIS PLANE IS OPT-IN. The subsystems behind these operations are constructed only when the deployment's governance block is active (`governance.enabled`, a non-empty `governance.limits`, or `governance.require_key` set to `required`/`optional`). On an instance where it is not, the READ surfaces answer 200 with an honestly EMPTY view — never a fabricated one — while the operations that cannot answer at all return 404 `feature_absent` carrying an `enable_hint` that names the cure. A 404 on a governance write therefore means "this deployment has no governance plane", not "your object does not exist", and it is deliberately not a 503: no amount of retrying cures a condition only a config change can.

      SUBTREE SCOPING IS AUTHORIZATION, NOT FILTERING. Every read takes an optional `node`, and the reporter behind it returns only that subtree — so a constrained reader cannot name, let alone read, another tenant's node, key, limit or refusal. The delegated self-serve operations go further: their route floor is a viewer pre-filter and their real gate is a fail-closed binding check (`x-delegation: subtree`).
paths:
  /tt/api/v1/governance/exports/refusals:
    get:
      operationId: governanceRefusalsExport
      summary: Full subtree/window-scoped refusal-feed export (JSON or CSV via format=), provenance-stamped, secret-free.
      description: |-
        The archive twin of `governanceRefusals`: the FULL subtree- and window-scoped refusal feed, not the 1000-row live view. Same secret-free row shape, same no-key-existence-oracle rule (a 401 `invalid_key` refusal names no limit), plus the ExportMeta provenance stamp.

        An export is COMPLETE BY DEFAULT. `offset` and `limit` here are a RESUME position and a row cap for a caller who wants to pull a very large export in pieces — they are not a pagination contract, and the response carries no `next_offset` or `next_cursor` to follow. That is why this operation declares no `x-pagination` while `governanceRefusals` declares two styles: one is a paged feed, the other is a whole file you may fetch in chunks.

        REPRESENTATION IS SELECTED BY `format=` FIRST, AND BY `Accept` WHEN `format=` IS ABSENT — see that parameter.

        A 404 `feature_absent` means no canonical decision log is readable on this instance (with the cure named); a 503 `budget_exceeded` means the query budget was exhausted and is genuinely transient.
      tags:
        - governance
      x-role-floor: viewer
      x-required-scope: governance:read
      x-export-right: true
      security:
        - managementToken:
            - governance:read
        - session: []
      parameters:
        - name: node
          in: query
          description: Confine the export to this subtree. A tenancy node id is a '/'-joined path (`org:acme`, `org:acme/team:ml`); the constraint matches the node itself and everything below it, and the '/' separator is what stops `org:acme` from also matching `org:acme-evil`. Absent ⇒ unconstrained.
          schema:
            type: string
        - name: since
          in: query
          description: Window start, INCLUSIVE. Absent ⇒ from the beginning of the log.
          schema:
            type: string
            format: date-time
        - name: until
          in: query
          description: Window end, EXCLUSIVE. Absent ⇒ to the end of the log.
          schema:
            type: string
            format: date-time
        - name: offset
          in: query
          description: 'RESUME position: rows to skip. An export is complete by default, so this exists for chunked retrieval, not for paging — no cursor or next-offset is returned to follow.'
          schema:
            type: integer
            format: int64
        - name: limit
          in: query
          description: Row cap for this chunk. 0 or absent ⇒ ALL matching rows (this export is deliberately uncapped, unlike the 1000-row live feed).
          schema:
            type: integer
            format: int64
        - name: format
          in: query
          description: |-
            The representation to return: `json` (default) or `csv`. ★ THIS PARAMETER WINS OUTRIGHT. It is an INSTRUCTION, not a preference, so it beats any `Accept` header; if you know which representation you want, send it here and stop reading.

            `Accept` IS HONORED WHEN `format=` IS ABSENT, and the rule is deliberately coarse rather than a full RFC 9110 negotiation: a header that names `text/csv` and names NO json type selects csv. Everything else — including `Accept: application/json, text/csv`, a `*/*` wildcard, and a media type this operation does not serve — selects the documented `json` default. Two things this server deliberately does NOT do: it does not interpret `q=` weights (a negotiation that reads `q=0` as a weak preference rather than a refusal is wrong in a way the caller cannot see), and it does not answer `406` for an unmatched `Accept` (these operations have a default representation, so an unmatched header means "no preference this server can honor", not "no acceptable response exists").

            Both media types appear on the 200 response because both are genuinely served AND genuinely negotiated. The csv form carries the ExportMeta provenance as leading `#` comment lines and the literal `UNPRICED` for an unpriced dollar (never `0`), with every cell that carries operator- or client-supplied text csvSafe-neutralized against formula injection. The cells left raw are the numbers and booleans, the `UNPRICED` literal, the server-stamped request timestamp and period bucket, and the closed `type`/`limit_type`/`posture` vocabularies. That is the test to apply to a column added later: if a caller can put bytes into it, it is csvSafe'd like the rest.
          schema:
            type: string
            enum:
              - json
              - csv
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceRefusalExportResponse'
            text/csv:
              schema:
                type: string
                description: 'CSV encoding of this export''s rows, with ExportMeta as leading #-comment lines. An unpriced dollar is never re-emitted as 0. WHICH idiom stands in for it (an empty cell, or the literal UNPRICED) and whether the export neutralizes formula-injection triggers -- and if so whether its other formats carry the same neutralized bytes -- are properties of the individual export rather than of this media type.'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "503":
          description: 'Problem: budget_exceeded.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # This operation also serves text/csv: send Accept: text/csv (or
            # format=csv). The CSV twin renders an unpriced dollar as the literal
            # UNPRICED, never 0, and carries the ExportMeta stamp as leading #-comment
            # lines.

            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/exports/refusals" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # This operation also serves text/csv: send Accept: text/csv (or
            # format=csv). The CSV twin renders an unpriced dollar as the literal
            # UNPRICED, never 0, and carries the ExportMeta stamp as leading #-comment
            # lines.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/exports/refusals",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // This operation also serves text/csv: send Accept: text/csv (or
            // format=csv). The CSV twin renders an unpriced dollar as the literal
            // UNPRICED, never 0, and carries the ExportMeta stamp as leading #-comment
            // lines.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/exports/refusals`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/exports/usage:
    get:
      operationId: governanceUsageExport
      summary: Periodized per-node spend/usage rollup over the ledger (JSON or CSV via format=), provenance-stamped, unpriced+unattributed excluded and disclosed.
      description: |-
        The billing-shaped read: per (node, period-bucket) request counts, token counts and spend, rolled up over the canonical decision log.

        **WHAT IT EXCLUDES**, AND WHY THAT IS THE POINT. Two classes of row cannot honestly be placed in a per-node spend rollup, and neither is silently dropped. UNPRICED rows — rows the pricing layer could not price at all — are excluded from every dollar figure and counted in `excluded.unpriced` plus per-row `unpriced_rows`. UNATTRIBUTED rows — rows with no resolvable governance node — are excluded and counted in `excluded.unattributed`. A group with zero priced rows reports its spend as UNPRICED (`usd: null`), never as `$0.00`: a fabricated zero is indistinguishable from a real zero, and the whole reason this API exists is that those two must never look the same.

        Every response is stamped with the same ExportMeta provenance envelope the /exports surface uses — pricing snapshots, unpriced-excluded, estimated, demo, window, instance — so a figure can always be traced to the pricing data that produced it. `tokentriage_export.v` is the EXPORT SCHEMA version and stays 1 independently of the API epoch.

        REPRESENTATION IS SELECTED BY `format=` FIRST, AND BY `Accept` WHEN `format=` IS ABSENT — see that parameter.

        **THIS OPERATION CAN RETURN A PARTIAL 200, AND YOU MUST CHECK FOR IT.** The house convention across this API is that a read which trips the query budget answers a PARTIAL 200 rather than a 503 — an operator gets the rows that were scanned plus a disclosure, never an error that discards work already done. This operation follows it, and the disclosure is carried OUT OF BAND because the report shape has no member for it:

        - JSON: the body is TWO top-level objects separated by a newline. The first is the report. A SECOND object `{"tokentriage_export_truncated":true,"reason":"…"}` follows it if and only if the scan was cut short. A whole-body `JSON.parse` FAILS on a truncated export (two values, not one), and a decoder that reads only the first value silently drops the disclosure — so decode as a JSON STREAM and read until EOF.
        - CSV: a trailing `# truncated: true` comment line after the data rows.

        **ABSENCE OF THE SECOND OBJECT IS THE COMPLETENESS SIGNAL** — there is no positive `truncated: false` marker to look for, so a client that never checks cannot tell a complete rollup from a partial one, and the failure mode is a billing figure that is silently short. `tokentriage_export.excluded` counts rows the scan CHOSE to exclude (unpriced, unattributed); it does not count rows the scan never reached.

        **THIS OPERATION CAN RETURN A PARTIAL 200, AND YOU MUST CHECK FOR IT.** The house convention across this API is that a read which trips the query budget answers a PARTIAL 200 rather than a 503 — an operator gets the rows that were scanned plus a disclosure, never an error that discards work already done. This operation follows it, and the disclosure is carried OUT OF BAND because the report shape has no member for it:

        - JSON: the body is TWO top-level objects separated by a newline. The first is the report. A SECOND object `{"tokentriage_export_truncated":true,"reason":"…"}` follows it if and only if the scan was cut short. A whole-body `JSON.parse` FAILS on a truncated export (two values, not one), and a decoder that reads only the first value silently drops the disclosure — so decode as a JSON STREAM and read until EOF.
        - CSV: a trailing `# truncated: true` comment line after the data rows.

        **ABSENCE OF THE SECOND OBJECT IS THE COMPLETENESS SIGNAL** — there is no positive `truncated: false` marker to look for, so a client that never checks cannot tell a complete rollup from a partial one, and the failure mode is a billing figure that is silently short. `tokentriage_export.excluded` counts rows the scan CHOSE to exclude (unpriced, unattributed); it does not count rows the scan never reached.

        A 404 `feature_absent` means no canonical decision log is readable on this instance (with the cure named); a 503 `budget_exceeded` means the query budget was exhausted and is genuinely transient — retry it, with the disclosed retry delay.
      tags:
        - governance
      x-role-floor: viewer
      x-required-scope: governance:read
      x-export-right: true
      security:
        - managementToken:
            - governance:read
        - session: []
      parameters:
        - name: node
          in: query
          description: Confine the rollup to this subtree. A tenancy node id is a '/'-joined path (`org:acme`, `org:acme/team:ml`); the constraint matches the node itself and everything below it, and the '/' separator is what stops `org:acme` from also matching `org:acme-evil`. Absent ⇒ unconstrained.
          schema:
            type: string
        - name: period
          in: query
          description: 'Rollup bucket. DEFAULT `total` (one row per node, `bucket: "all"`); otherwise each row''s `bucket` is the RFC 3339 UTC start of its period. A value outside this enum is refused 400.'
          schema:
            type: string
            enum:
              - day
              - week
              - month
              - total
        - name: since
          in: query
          description: Window start, INCLUSIVE. Absent ⇒ from the beginning of the log.
          schema:
            type: string
            format: date-time
        - name: until
          in: query
          description: Window end, EXCLUSIVE. Absent ⇒ to the end of the log. Exclusive so adjacent windows tile without double-counting a boundary row.
          schema:
            type: string
            format: date-time
        - name: format
          in: query
          description: |-
            The representation to return: `json` (default) or `csv`. ★ THIS PARAMETER WINS OUTRIGHT. It is an INSTRUCTION, not a preference, so it beats any `Accept` header; if you know which representation you want, send it here and stop reading.

            `Accept` IS HONORED WHEN `format=` IS ABSENT, and the rule is deliberately coarse rather than a full RFC 9110 negotiation: a header that names `text/csv` and names NO json type selects csv. Everything else — including `Accept: application/json, text/csv`, a `*/*` wildcard, and a media type this operation does not serve — selects the documented `json` default. Two things this server deliberately does NOT do: it does not interpret `q=` weights (a negotiation that reads `q=0` as a weak preference rather than a refusal is wrong in a way the caller cannot see), and it does not answer `406` for an unmatched `Accept` (these operations have a default representation, so an unmatched header means "no preference this server can honor", not "no acceptable response exists").

            Both media types appear on the 200 response because both are genuinely served AND genuinely negotiated. The csv form carries the ExportMeta provenance as leading `#` comment lines and the literal `UNPRICED` for an unpriced dollar (never `0`), with every cell that carries operator- or client-supplied text csvSafe-neutralized against formula injection. The cells left raw are the numbers and booleans, the `UNPRICED` literal, the server-stamped request timestamp and period bucket, and the closed `type`/`limit_type`/`posture` vocabularies. That is the test to apply to a column added later: if a caller can put bytes into it, it is csvSafe'd like the rest.
          schema:
            type: string
            enum:
              - json
              - csv
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceUsageReport'
            text/csv:
              schema:
                type: string
                description: 'CSV encoding of this export''s rows, with ExportMeta as leading #-comment lines. An unpriced dollar is never re-emitted as 0. WHICH idiom stands in for it (an empty cell, or the literal UNPRICED) and whether the export neutralizes formula-injection triggers -- and if so whether its other formats carry the same neutralized bytes -- are properties of the individual export rather than of this media type.'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "503":
          description: 'Problem: budget_exceeded.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # This operation also serves text/csv: send Accept: text/csv (or
            # format=csv). The CSV twin renders an unpriced dollar as the literal
            # UNPRICED, never 0, and carries the ExportMeta stamp as leading #-comment
            # lines.

            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/exports/usage" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # This operation also serves text/csv: send Accept: text/csv (or
            # format=csv). The CSV twin renders an unpriced dollar as the literal
            # UNPRICED, never 0, and carries the ExportMeta stamp as leading #-comment
            # lines.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/exports/usage",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // This operation also serves text/csv: send Accept: text/csv (or
            // format=csv). The CSV twin renders an unpriced dollar as the literal
            // UNPRICED, never 0, and carries the ExportMeta stamp as leading #-comment
            // lines.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/exports/usage`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/health:
    get:
      operationId: governanceHealth
      summary: 'Distributed-governance health: shared-state reachability, breaker state, suspensions, convergence lag (honest air-gapped when single-node).'
      description: |-
        Whether the machinery behind distributed governance is actually working: the shared-state tier's reachability, the circuit-breaker state, which limits have had enforcement SUSPENDED, and how far behind each scope's convergence is running.

        **AIR-GAP HONESTY IS VISIBLE**, NOT INFERRED. On a single-node instance with no shared-state tier configured, this does NOT 404 and does not return an empty carcass that looks like a healthy system with nothing to report. It returns `capability.shared_state_enabled: false` with a plain note saying this is a single-node instance and that distributed limits, cross-replica convergence and reconcile are not active. The difference between "nothing is wrong" and "nothing is watching" is the entire value of this endpoint.

        `suspensions` is the loud one: a suspended limit is armed to enforce but its evaluator heartbeat is stale, so it has failed OPEN and traffic is flowing past the cap.
      tags:
        - governance
      x-role-floor: viewer
      x-required-scope: governance:read
      security:
        - managementToken:
            - governance:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceHealthResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/health" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/health",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/health`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/health/fleet:
    get:
      operationId: governanceHealthFleet
      summary: Per-replica governance health across the fleet, riding the same coverage envelope as the federated query fold.
      description: |-
        The fan-out form of the health read above: one card per replica in the coverage denominator, plus the envelope that says how much of the fleet actually answered.

        **AN UNREACHABLE REPLICA IS A TYPED CARD, NOT A GAP.** A replica that fails to answer appears in `replicas` with a non-ok `status` and a `health` of null, and it counts in the coverage denominator. It is not dropped from the list and it is not given a fabricated healthy posture. Dropping it would make a partial answer look complete — the fleet would appear entirely healthy because the sick member was the one that did not reply, which is precisely backwards.

        **`replicas` HAS ONE ENTRY PER REPLICA IN THE DENOMINATOR**, so `len(replicas)` matches `coverage.replicas_total` and the two halves of the response cannot disagree about who is in the fleet. The server re-verifies that alignment before serializing and refuses with a 500 rather than serve a self-inconsistent envelope — the same refusal the federated query fold makes for the same reason.

        **READ `coverage.is_partial` BEFORE COMPARING POSTURES.** With `is_partial: true` the answer describes the members that responded, not the fleet; `replicas_answered` over `replicas_total` is the fraction it rests on. Treating a partial fan-out as a fleet-wide verdict is the failure this envelope exists to prevent.

        **SINGLE-NODE AND AIR-GAPPED IS THE HONEST DEGENERATE**, not a special case and not a 404: one card, a denominator of one, and the local-only basis. The shape is the same one a real fleet returns, so a consumer needs no branch for it.

        This surface is READ-ONLY by design, like the health read it fans out — there is no adopt or write affordance here.
      tags:
        - governance
      x-role-floor: viewer
      x-required-scope: governance:read
      security:
        - managementToken:
            - governance:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FleetGovernanceHealth'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/health/fleet" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/health/fleet",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/health/fleet`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/keys:
    get:
      operationId: governanceKeysList
      summary: List virtual keys (kid+stub+label+state+scopes only — never a secret).
      description: |-
        Virtual-key metadata for the requested subtree. The read shape carries the public key id (`kid`), a display-only 4-character `stub`, the label, the owning tenancy node, state (active|revoked|expired), timestamps, rotation lineage, and the key's allowed surface — and it has NO field capable of holding a secret or a hash. That is structural, not a convention: "a key's stored value" is unrepresentable on this shape, so no bug in this handler can leak one. The secret exists on exactly one response in this entire resource, the shown-once mint reveal.

        `last_used` is deliberately COARSE (hour granularity): a precise last-use timestamp on a credential list is a traffic-analysis side channel, and hour resolution is what an operator actually needs to spot a dormant key.

        Empty `scopes` slices mean UNRESTRICTED for that dimension, not "denied" — an empty `models` list is a key that may call any model.

        An instance with no governance armed answers 200 with an empty `keys` array.
      tags:
        - governance
      x-role-floor: viewer
      x-required-scope: governance:read
      security:
        - managementToken:
            - governance:read
        - session: []
      parameters:
        - name: node
          in: query
          description: Confine the listing to keys owned within this subtree. A tenancy node id is a '/'-joined path (`org:acme`, `org:acme/team:ml`); the constraint matches the node itself and everything below it, and the '/' separator is what stops `org:acme` from also matching `org:acme-evil`. Absent ⇒ unconstrained.
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceKeysResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/keys" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/keys",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/keys`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    post:
      operationId: governanceKeyMint
      summary: Mint a virtual key; the secret is revealed exactly once.
      description: |-
        Create a virtual key owned by a tenancy node.

        **THE SECRET IS SHOWN ONCE AND IS NOT RECOVERABLE.** The `secret` field of this response is the only time the plaintext `ttv_` credential exists outside the caller: the store persists only its SHA-256, it is never logged, it is never written to an audit row (the row names the `kid`), and there is no endpoint that can return it again. A caller that loses it must rotate the key. This is one of a closed, allowlisted set of responses in the entire API permitted to carry a credential.

        Requires the PRIVILEGED `keys:mint` scope, which the `*:write` wildcard deliberately never expands to — minting a credential is not an ordinary write, so a broadly-scoped token cannot do it implicitly.

        `scopes` bounds what the key may call; an EMPTY list in any dimension means unrestricted for that dimension, not denied. `expires_at` is RFC 3339 UTC; absent means non-expiring.

        Every branch of this endpoint — including a malformed body — writes exactly one audit row, so a probing client leaves a complete trail rather than a gap.
      tags:
        - governance
      x-role-floor: admin
      x-required-scope: keys:mint
      x-writes: true
      security:
        - managementToken:
            - keys:mint
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GovernanceMintRequest'
            example:
              node: $TOKENTRIAGE_NODE
              scopes:
                models: []
                providers: []
                endpoints: []
                allow_passthrough: false
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceMintResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # JSON-escape a shell value before it enters the body below. Without this a
            # password containing a backslash or a quote produces an invalid body (or, with
            # a crafted value, extra members). A control character is refused rather than
            # silently mis-sent.
            json_str() {
              case $1 in *[[:cntrl:]]*)
                printf 'json_str: refusing a value containing a control character\n' >&2; exit 1;;
              esac
              printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'
            }

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/keys" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<JSON
            {
              "node": "$(json_str "$TOKENTRIAGE_NODE")",
              "scopes": {
                "models": [],
                "providers": [],
                "endpoints": [],
                "allow_passthrough": false
              }
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            NODE = os.environ["TOKENTRIAGE_NODE"]

            payload = {
              "node": NODE,
              "scopes": {
                "models": [],
                "providers": [],
                "endpoints": [],
                "allow_passthrough": False
              }
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/keys",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const node: string = process.env.TOKENTRIAGE_NODE ?? "";

            const payload = {
              "node": node,
              "scopes": {
                "models": [],
                "providers": [],
                "endpoints": [],
                "allow_passthrough": false
              }
            };

            const res: Response = await fetch(`${base}/tt/api/v1/governance/keys`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/keys/{kid}:
    get:
      operationId: governanceKeyGet
      summary: One virtual key's read shape by kid (subtree-scoped; no cross-scope existence oracle).
      description: |-
        One key's metadata, in the same secret-free shape the list returns.

        **NO EXISTENCE ORACLE.** When `node` constrains the read, a kid that exists OUTSIDE that subtree answers with exactly the same 404 as a kid that does not exist at all. The two cases are indistinguishable by design: if they differed, a caller scoped to one tenant could enumerate another tenant's key ids by watching which 404s came back differently.

        A 404 on this route is one of two things and the problem type tells them apart: `not_found` means no such key is visible to you, while `feature_absent` means this deployment has no key store at all and names the config that would create one.
      tags:
        - governance
      x-role-floor: viewer
      x-required-scope: governance:read
      security:
        - managementToken:
            - governance:read
        - session: []
      parameters:
        - name: node
          in: query
          description: The subtree scope for the read; an out-of-scope kid answers exactly like a missing one (no existence oracle). A tenancy node id is a '/'-joined path (`org:acme`, `org:acme/team:ml`); the constraint matches the node itself and everything below it, and the '/' separator is what stops `org:acme` from also matching `org:acme-evil`. Absent ⇒ unconstrained.
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceKeyResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/keys/$TOKENTRIAGE_KID" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            KID = os.environ["TOKENTRIAGE_KID"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/keys/{KID}",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const kid: string = process.env.TOKENTRIAGE_KID ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/keys/${kid}`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: kid
        in: path
        required: true
        description: The key's PUBLIC id, as listed by `GET /governance/keys` and returned at mint. It is a name for the key, not key material.
        schema:
          type: string
  /tt/api/v1/governance/keys/{kid}/revoke:
    post:
      operationId: governanceKeyRevoke
      summary: Revoke a virtual key.
      description: |-
        Move a key to `revoked`. The record is retained — revocation is a state transition, not a deletion — so the key remains visible on the read surfaces with its history intact, which is what makes it auditable after the fact.

        Revocation carries the COMMON `governance:write` scope rather than a privileged one, because destroying access to the DATA PLANE is the SAFE direction: a virtual key governs an application's model access, not the operator's own reach into this API, so an operator locked out of minting during an incident must still be able to kill a leaked key. Requiring an authority the incident may have taken away would make the containment step the one step you cannot perform.

        **The credential resources DIFFER on this, deliberately, and AP2-06 took the wave-level decision this paragraph used to defer.** The line is the PLANE the credential governs, not the verb: destroying a CONTROL-PLANE credential is privileged everywhere — `tokenRevoke` (`tokens:write`), `tokenRotate` (`tokens:mint`), `shareTokenRevoke` (`share:admin`) and `governanceKeyRotate` (`keys:mint`) — because those credentials are how an operator reaches this API at all, and a `*:write` wildcard that reaches them can lock the operator out of the plane they would use to recover. This operation is the single DECLARED exception, and it is held as one by a gate rather than left to decay back into an accident.

        A 404 `feature_absent` means this deployment has no key store.
      tags:
        - governance
      x-role-floor: operator
      x-required-scope: governance:write
      x-writes: true
      security:
        - managementToken:
            - governance:write
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceRevokeResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! IRREVERSIBLY REVOKES a virtual key. The application using it stops
            # !! reaching models immediately — which is the point during an
            # !! incident, and a surprise otherwise.
            # !! Read it before you paste it.

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/keys/$TOKENTRIAGE_KID/revoke" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! IRREVERSIBLY REVOKES a virtual key. The application using it stops
            # !! reaching models immediately — which is the point during an
            # !! incident, and a surprise otherwise.
            # !! Read it before you paste it.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            KID = os.environ["TOKENTRIAGE_KID"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/keys/{KID}/revoke",
                method="POST",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            // !! IRREVERSIBLY REVOKES a virtual key. The application using it stops
            // !! reaching models immediately — which is the point during an
            // !! incident, and a surprise otherwise.
            // !! Read it before you paste it.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const kid: string = process.env.TOKENTRIAGE_KID ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/keys/${kid}/revoke`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: kid
        in: path
        required: true
        description: The public id of the key to revoke.
        schema:
          type: string
  /tt/api/v1/governance/keys/{kid}/rotate:
    post:
      operationId: governanceKeyRotate
      summary: Rotate a key, minting a successor; the successor secret is revealed exactly once.
      description: |-
        Mint a SUCCESSOR to an existing key, recording the rotation lineage so the old and new key can be correlated in the audit trail and in `rotated_from` on the key read shape.

        The successor's secret is revealed exactly once, under the same shown-once discipline as mint: SHA-256 at rest, never logged, never audited, never re-fetchable.

        Rotation is a SEPARATE surface from mint rather than an alias for it — it reads an existing credential's grant, mints under it, and touches the predecessor — which is why it carries its own row in the shown-once allowlist rather than sheltering under the mint's.

        Requires the privileged `keys:mint` scope. A 404 `feature_absent` means this deployment has no key store.
      tags:
        - governance
      x-role-floor: admin
      x-required-scope: keys:mint
      x-writes: true
      security:
        - managementToken:
            - keys:mint
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceMintResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! REVOKES the predecessor key after its grace window. Deploy the
            # !! successor first.
            # !! Read it before you paste it.

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/keys/$TOKENTRIAGE_KID/rotate" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! REVOKES the predecessor key after its grace window. Deploy the
            # !! successor first.
            # !! Read it before you paste it.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            KID = os.environ["TOKENTRIAGE_KID"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/keys/{KID}/rotate",
                method="POST",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            // !! REVOKES the predecessor key after its grace window. Deploy the
            // !! successor first.
            // !! Read it before you paste it.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const kid: string = process.env.TOKENTRIAGE_KID ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/keys/${kid}/rotate`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: kid
        in: path
        required: true
        description: The public id of the key to rotate. The successor is a NEW key with its own kid; this one is recorded as its `rotated_from`.
        schema:
          type: string
  /tt/api/v1/governance/limits:
    get:
      operationId: governanceLimitsList
      summary: Compiled limit definitions (armed) + persisted per-node overrides (effective on next reload).
      description: |-
        The two halves of "what is the cap", kept deliberately separate because they are not the same fact.

        `definitions` are the ARMED compiled limits — what is enforcing RIGHT NOW. `overrides` are persisted per-node writes that are COMPILE-BOUND: they are durable and audited, but they do not take effect until the next governance reload, and every one of them is returned with `pending_reload: true` and a note saying so. No override is ever reported here as live-enforced. That is the whole point of the split: lowering a cap through this API cannot show "applied" while traffic still flows against the old ceiling — the definition keeps showing the amount actually enforced.

        A definition attaches either as a `template` (the config-plane catalog, attached at a level: org/team/user) or `pinned` to one node. Subtree scoping applies to PINNED definitions only: templates are the catalog, not node-private, so they are always shown. Overrides are node-scoped and filter normally.

        Spend-class amounts render as typed Money; count-class as `amount_count`. `override_allowed` plus `override_max` are the ceiling a delegated override must fit under.

        An instance with no governance armed answers 200 with both arrays empty and a note saying why.
      tags:
        - governance
      x-role-floor: viewer
      x-required-scope: governance:read
      security:
        - managementToken:
            - governance:read
        - session: []
      parameters:
        - name: node
          in: query
          description: Confine PINNED definitions and overrides to this subtree; template definitions are the config-plane catalog and are always shown. A tenancy node id is a '/'-joined path (`org:acme`, `org:acme/team:ml`); the constraint matches the node itself and everything below it, and the '/' separator is what stops `org:acme` from also matching `org:acme-evil`. Absent ⇒ unconstrained.
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceLimitsResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/limits" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/limits",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/limits`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/limits/overrides:
    post:
      operationId: governanceOverrideSet
      summary: Set a per-node limit override (persisted + audited; effective on next governance reload).
      description: |-
        Raise or lower one limit's amount for one tenancy node. The write is durable and audited immediately.

        **IT DOES NOT ENFORCE YET**, AND THE RESPONSE SAYS SO. Overrides are COMPILE-BOUND: the value lands in the store but the armed limit set is unchanged until the next governance reload. Every success returns `pending_reload: true` with a note. This surface will never tell you a cap is applied while traffic is still being measured against the old one — read `governanceLimitsList`'s `definitions` for what is enforcing right now.

        `amount` is the limit's STORED unit: NANODOLLARS for `budget_usd` (integer, so money never rounds through a float — $1.00 is 1000000000), a raw count otherwise. An `amount` ≤ 0 is REFUSED 400 rather than accepted as "unlimited": zero would DISABLE the cap, which is the exact inversion of what someone setting a limit meant.

        **THE WRITE REFUSES EXACTLY WHAT THE RELOAD WOULD DROP**, using the reload's own predicate rather than a mirror of it. When the `limit_id` names a COMPILED definition, the same rule that runs at every governance reload decides the write, so a 200 here means the override will actually take effect:

        A 422 `unsupported_override_class` means the definition cannot take a per-node override at all — the attachment is `pinned` rather than a level template (an override REPLACES a template instance's amount at one node, so a pinned limit has nothing to override), or the template reports `override_allowed: false`, or the limit's TYPE is structurally incapable: `rpm`, `tpm` and `concurrency` bake their cap into a frozen per-window store when governance compiles. Any of the three would be silently DROPPED at the reload, so each is refused at the boundary with the reload's own reason and the next action, instead of a cheerful 200 and a row that reports `pending_reload` forever. Both predictors are on the wire already: read `attach` and `override_allowed` off `governanceLimitsList`. Overrides apply only to the ledger-settled types (`budget_usd`, `quota_tokens`, `quota_requests`).

        A 400 `bad_request` also covers an `amount` above the template's declared `override_max` — the compiler enforces that ceiling, so an over-ceiling override would be dropped at reload too.

        A 422 `validation_failed` with an `/node` field error means the target node has NO ROW in the runtime tenancy tree. An override is stored against a node row, and a node named in config alone is not enough: create it first (Governance → Keys & Tenants, or `governanceNodeCreate`). This used to be a 500 whose remediation named `tokentriage index rebuild`, which is the wrong cure — no index rebuild creates a tenancy node.

        KNOWN DIVERGENCE FROM THE DELEGATED TWIN, stated because it will surprise you: an UNKNOWN `limit_id` is accepted HERE with a 200 and `pending_reload: true` (this plane defers unknown-limit validation to the reload), while `governanceSelfServeOverride` refuses the same input 400. So a typo'd `limit_id` looks like a successful write on this route and then never takes effect. Confirm the id against `governanceLimitsList`'s `definitions` before relying on the 200.

        A 404 `feature_absent` means this deployment has no governance plane to override.
      tags:
        - governance
      x-role-floor: operator
      x-required-scope: governance:write
      x-writes: true
      security:
        - managementToken:
            - governance:write
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GovernanceOverrideRequest'
            example:
              limit_id: $TOKENTRIAGE_LIMIT_ID
              node: $TOKENTRIAGE_NODE
              amount: 150000000000
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceOverrideResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "422":
          description: 'Problem: unsupported_override_class, validation_failed.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # JSON-escape a shell value before it enters the body below. Without this a
            # password containing a backslash or a quote produces an invalid body (or, with
            # a crafted value, extra members). A control character is refused rather than
            # silently mis-sent.
            json_str() {
              case $1 in *[[:cntrl:]]*)
                printf 'json_str: refusing a value containing a control character\n' >&2; exit 1;;
              esac
              printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'
            }

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/limits/overrides" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<JSON
            {
              "limit_id": "$(json_str "$TOKENTRIAGE_LIMIT_ID")",
              "node": "$(json_str "$TOKENTRIAGE_NODE")",
              "amount": 150000000000
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            LIMIT_ID = os.environ["TOKENTRIAGE_LIMIT_ID"]
            NODE = os.environ["TOKENTRIAGE_NODE"]

            payload = {
              "limit_id": LIMIT_ID,
              "node": NODE,
              "amount": 150000000000
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/limits/overrides",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const limitId: string = process.env.TOKENTRIAGE_LIMIT_ID ?? "";
            const node: string = process.env.TOKENTRIAGE_NODE ?? "";

            const payload = {
              "limit_id": limitId,
              "node": node,
              "amount": 150000000000
            };

            const res: Response = await fetch(`${base}/tt/api/v1/governance/limits/overrides`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/limits/overrides/{limit_id}/{node}:
    delete:
      operationId: governanceOverrideDelete
      summary: Delete a per-node limit override (persisted + audited; effective on next governance reload).
      description: |-
        Remove one node's override, returning that limit to its compiled definition at that node. Like setting one, deleting one is COMPILE-BOUND: the removal is durable and audited immediately but does not change what is enforcing until the next governance reload, and the response says so.

        The node is a PATH REMAINDER, not a single segment, because a tenancy node id contains '/' — the full path is `…/overrides/{limit_id}/org:acme/team:ml`. The OpenAPI parameter is documented under its clean name `node`.

        Deleting an override that is not set is 404 `not_found` — this operation is not idempotent-by-silence, because "there was nothing there" is information the caller asked for. A 404 `feature_absent` is the different case: this deployment has no governance plane at all.
      tags:
        - governance
      x-role-floor: operator
      x-required-scope: governance:write
      x-writes: true
      security:
        - managementToken:
            - governance:write
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceOverrideDeleteResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! DELETES a limit override, so the inherited limit applies again.
            # !! Read it before you paste it.

            curl -sS --fail-with-body \
              -X DELETE "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/limits/overrides/$TOKENTRIAGE_LIMIT_ID/$TOKENTRIAGE_NODE" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! DELETES a limit override, so the inherited limit applies again.
            # !! Read it before you paste it.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            LIMIT_ID = os.environ["TOKENTRIAGE_LIMIT_ID"]
            NODE = os.environ["TOKENTRIAGE_NODE"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/limits/overrides/{LIMIT_ID}/{NODE}",
                method="DELETE",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            // !! DELETES a limit override, so the inherited limit applies again.
            // !! Read it before you paste it.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const limitId: string = process.env.TOKENTRIAGE_LIMIT_ID ?? "";
            const node: string = process.env.TOKENTRIAGE_NODE ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/limits/overrides/${limitId}/${node}`, {
              method: "DELETE",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: limit_id
        in: path
        required: true
        description: The compiled limit's id, as it appears in `governanceLimitsList`'s definitions.
        schema:
          type: string
      - name: node
        in: path
        required: true
        description: 'The tenancy node whose override is being removed. A path REMAINDER: it may contain ''/'' (e.g. `org:acme/team:ml`). A path remainder: the value may itself contain ''/'' (e.g. a tenancy node id like org:acme/team:ml).'
        schema:
          type: string
  /tt/api/v1/governance/nodes:
    get:
      operationId: governanceNodesList
      summary: The compiled tenancy tree (flat, parent-pointed).
      description: |-
        The tenancy tree as a FLAT, parent-pointed list rather than a nested document: each node carries its own id, its `parent` id (empty for a root org), a label and a kind (org|team|user). Flat because a subtree-scoped read returns a SLICE of the tree, and a nested shape would have to either fabricate the ancestors the caller may not see or return a forest of orphans; a parent pointer says exactly what is known and nothing more.

        Node ids are '/'-joined paths (`org:acme/team:ml`), which is why the delete-override route takes the node as a path REMAINDER rather than a single segment.

        An instance with no governance armed answers 200 with an empty `nodes` array.
      tags:
        - governance
      x-role-floor: viewer
      x-required-scope: governance:read
      security:
        - managementToken:
            - governance:read
        - session: []
      parameters:
        - name: node
          in: query
          description: Confine the tree to this subtree. A tenancy node id is a '/'-joined path (`org:acme`, `org:acme/team:ml`); the constraint matches the node itself and everything below it, and the '/' separator is what stops `org:acme` from also matching `org:acme-evil`. Absent ⇒ unconstrained.
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceNodesResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/nodes" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/nodes",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/nodes`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    post:
      operationId: governanceNodeCreate
      summary: Create a runtime tenancy node.
      description: |-
        Add a node to the tenancy tree at RUNTIME, without a config edit or a restart — the runtime plane's counterpart to the config-declared tree.

        `id` is the full '/'-joined path (`org:acme/team:ml`) and `kind` is org|team|user; `parent` is the containing node. The tree shape is what every subtree scope in this resource is evaluated against, so creating a node is what makes a scope able to name it.

        A 404 `feature_absent` means this deployment has no governance plane.
      tags:
        - governance
      x-role-floor: operator
      x-required-scope: governance:write
      x-writes: true
      security:
        - managementToken:
            - governance:write
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GovernanceNodeCreateRequest'
            example:
              id: $TOKENTRIAGE_NEW_NODE
              kind: team
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceNodeResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # JSON-escape a shell value before it enters the body below. Without this a
            # password containing a backslash or a quote produces an invalid body (or, with
            # a crafted value, extra members). A control character is refused rather than
            # silently mis-sent.
            json_str() {
              case $1 in *[[:cntrl:]]*)
                printf 'json_str: refusing a value containing a control character\n' >&2; exit 1;;
              esac
              printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'
            }

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/nodes" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<JSON
            {
              "id": "$(json_str "$TOKENTRIAGE_NEW_NODE")",
              "kind": "team"
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            NEW_NODE = os.environ["TOKENTRIAGE_NEW_NODE"]

            payload = {
              "id": NEW_NODE,
              "kind": "team"
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/nodes",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const newNode: string = process.env.TOKENTRIAGE_NEW_NODE ?? "";

            const payload = {
              "id": newNode,
              "kind": "team"
            };

            const res: Response = await fetch(`${base}/tt/api/v1/governance/nodes`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/nodes/{id}:
    delete:
      operationId: governanceNodeDelete
      summary: Delete a runtime tenancy node (overrides cascade).
      description: |-
        Remove a runtime tenancy node. ★ PER-NODE OVERRIDES ATTACHED TO IT CASCADE — they are removed with it. That is deliberate and it is stated here because the alternative is worse: an orphaned override on a node that no longer exists is unreachable through every read and delete route in this API, and would silently re-arm if a node with the same id were ever created again.

        The id is a PATH REMAINDER, not a single segment, because a tenancy node id contains '/' — the full path is `…/nodes/org:acme/team:ml`. The OpenAPI parameter is documented under its clean name `id`.

        A 404 `feature_absent` means this deployment has no governance plane.
      tags:
        - governance
      x-role-floor: operator
      x-required-scope: governance:write
      x-writes: true
      security:
        - managementToken:
            - governance:write
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceNodeDeleteResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! DELETES a tenancy node. Limits and grants attached to it go with it.
            # !! Read it before you paste it.

            curl -sS --fail-with-body \
              -X DELETE "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/nodes/$TOKENTRIAGE_ID" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            # !! DELETES a tenancy node. Limits and grants attached to it go with it.
            # !! Read it before you paste it.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            ID = os.environ["TOKENTRIAGE_ID"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/nodes/{ID}",
                method="DELETE",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // !! DESTRUCTIVE — RUNNING THIS SAMPLE CHANGES A LIVE DEPLOYMENT.
            // !! DELETES a tenancy node. Limits and grants attached to it go with it.
            // !! Read it before you paste it.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const id: string = process.env.TOKENTRIAGE_ID ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/nodes/${id}`, {
              method: "DELETE",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: id
        in: path
        required: true
        description: 'The node id to delete. A path REMAINDER: it may contain ''/'' (e.g. `org:acme/team:ml`). A path remainder: the value may itself contain ''/'' (e.g. a tenancy node id like org:acme/team:ml).'
        schema:
          type: string
  /tt/api/v1/governance/preview:
    post:
      operationId: governancePreview
      summary: Dry-run a proposed limit set over ledger history + live counters (approximation-labeled).
      description: |-
        Answers "what would this limit set have done?" against real ledger history and the live counters, WITHOUT arming anything. It mutates no state, refuses no live traffic, and reveals no secret. It is a POST because the proposed limit set is a body, not because it writes — hence `x-read-only` and the viewer floor.

        **THE ANSWER IS LABELED**, PER SECTION, AND THE LABELS ARE STRUCTURAL. Ledger-settled limits (budget_usd, quota_tokens, quota_requests) can be replayed EXACTLY against recorded history. Rate-class limits (rpm/tpm/concurrency) cannot: their windows are ephemeral and are gone, so that section carries a mandatory `rate_windows_approximated` label. Where neither is possible the response says CANNOT-ESTIMATE rather than producing a number. The labels are stamped by the response types themselves, so no handler bug can strip them and leave a bare figure looking authoritative.

        A proposed set that does not compile — an unknown limit type, an unknown algorithm, an incoherent attachment — is refused 422 `validation_failed` carrying the compiler's own message. It is never answered with a fabricated preview.

        A 404 `feature_absent` means the dry-run seam is not wired on this instance; the response explicitly implies NO verdict about the proposed limits.
      tags:
        - governance
      x-role-floor: viewer
      x-required-scope: governance:read
      x-read-only: true
      security:
        - managementToken:
            - governance:read
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GovernancePreviewRequest'
            example:
              limits:
                - id: $TOKENTRIAGE_LIMIT_ID
                  type: budget_usd
                  attach:
                    node: $TOKENTRIAGE_NODE
                  amount_usd: 1
                  period: day
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernancePreviewResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "422":
          description: 'Problem: validation_failed.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # JSON-escape a shell value before it enters the body below. Without this a
            # password containing a backslash or a quote produces an invalid body (or, with
            # a crafted value, extra members). A control character is refused rather than
            # silently mis-sent.
            json_str() {
              case $1 in *[[:cntrl:]]*)
                printf 'json_str: refusing a value containing a control character\n' >&2; exit 1;;
              esac
              printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'
            }

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/preview" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<JSON
            {
              "limits": [
                {
                  "id": "$(json_str "$TOKENTRIAGE_LIMIT_ID")",
                  "type": "budget_usd",
                  "attach": {
                    "node": "$(json_str "$TOKENTRIAGE_NODE")"
                  },
                  "amount_usd": 1,
                  "period": "day"
                }
              ]
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            LIMIT_ID = os.environ["TOKENTRIAGE_LIMIT_ID"]
            NODE = os.environ["TOKENTRIAGE_NODE"]

            payload = {
              "limits": [
                {
                  "id": LIMIT_ID,
                  "type": "budget_usd",
                  "attach": {
                    "node": NODE
                  },
                  "amount_usd": 1,
                  "period": "day"
                }
              ]
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/preview",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const limitId: string = process.env.TOKENTRIAGE_LIMIT_ID ?? "";
            const node: string = process.env.TOKENTRIAGE_NODE ?? "";

            const payload = {
              "limits": [
                {
                  "id": limitId,
                  "type": "budget_usd",
                  "attach": {
                    "node": node
                  },
                  "amount_usd": 1,
                  "period": "day"
                }
              ]
            };

            const res: Response = await fetch(`${base}/tt/api/v1/governance/preview`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/reconcile:
    get:
      operationId: governanceReconcile
      summary: Reconcile drift report (display-and-alert only; never an adopt affordance).
      description: |-
        Per-scope drift between the canonical, ledger-recomputed burn and whatever a distributed rollup claims.

        **THIS SURFACE IS DISPLAY-AND-ALERT ONLY**, AND THAT IS STRUCTURAL. There is no write route here and `adopted` is always false — the response shape cannot express an adopt. Reconciliation tells you the two numbers disagree and by how much; it never resolves the disagreement by overwriting the canonical ledger with a rollup, because the ledger is the system of record and a drift report is evidence, not authority.

        On a single-node / air-gapped instance it returns an honest empty report saying there is no distributed rollup to reconcile against and that the canonical ledgers are the sole system of record.
      tags:
        - governance
      x-role-floor: viewer
      x-required-scope: governance:read
      security:
        - managementToken:
            - governance:read
        - session: []
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceReconcileResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/reconcile" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/reconcile",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/reconcile`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/refusals:
    get:
      operationId: governanceRefusals
      summary: Curated refusal-audit feed (newest first), cursor- or offset-paginated within the node subtree.
      description: |-
        Why requests were refused by governance, newest first: the refusal type, the limit that caused it, the node and key it was attributed to, the enforcement posture at the time, and the HTTP status the caller actually received.

        **ORDERING: NEWEST FIRST BY `ts`, AND ROWS SHARING A TIMESTAMP HAVE NO DEFINED RELATIVE ORDER.** That second half is a real limitation, not a formality: the feed is produced by walking the canonical decision log in reverse, so rows recorded in the same second come back in whatever order they were appended, and nothing sorts them afterwards. Two refusals in the same second are ordinary under load.

        This is WHY the cursor carries `req_id` alongside the timestamp: to IDENTIFY the boundary row, not to order it. A position that can only compare timestamps cannot tell two same-second rows apart and will skip one or repeat one across a page boundary while returning 200 — so the cursor locates the exact row it was minted from instead of inferring where it sits. The consequence for you: page boundaries are safe, but do not assume the relative order of same-timestamp rows is stable across calls, and do not derive a sequence number from it.

        **PAGINATION: TWO FORMS**, ONE POSITION. This operation answers the house cursor contract (`cursor` in, `page.next_cursor` + `page.has_more` out) AND the original offset block (`offset` in, `page.total` + `page.next_offset` out). They describe the same position in the same set, so you may follow either and may switch between them mid-feed. **`cursor` WINS when both are supplied** — a request carrying both has a fresh cursor and a stale offset, and honoring the offset would silently re-deliver or skip rows. `page.offset` in the response always reports the position the server actually paged from, so the body never claims a position it did not use.

        THE `offset` PARAMETER IS DEPRECATED (RFC 9745/8594) — it alone; `limit` is not, and neither is the `next_offset` response member. It still answers, and will keep answering until the sunset date; a response to a request that used one carries `Deprecation`, `Sunset` and a `Link; rel="deprecation"` header. Prefer `cursor`: an offset into a live, newest-first feed is read against a set that is still growing at the head, so rows shift under a paging client. Send no cursor for the first page and echo `page.next_cursor` back VERBATIM for each subsequent one; an unreadable cursor is a 400 naming the parameter, never a silent restart from the top.

        THIS IS THE CAPPED LIVE VIEW, not the archive. The feed is bounded at 1000 rows and `page.total` SATURATES there — it is the count within the bound, not the true subtree count. The full, uncapped, window-scoped feed is `governanceRefusalsExport`.

        NO KEY-EXISTENCE ORACLE: a 401 `invalid_key` refusal names no limit, even if the upstream record carried one. An authentication failure may not disclose which limits exist.

        An instance with no governance armed answers 200 with an empty `refusals` array.
      tags:
        - governance
      x-role-floor: viewer
      x-required-scope: governance:read
      x-pagination:
        - cursor
        - offset
      security:
        - managementToken:
            - governance:read
        - session: []
      parameters:
        - name: node
          in: query
          description: The subtree the feed — and its cursors and offsets — are scoped to. A tenancy node id is a '/'-joined path (`org:acme`, `org:acme/team:ml`); the constraint matches the node itself and everything below it, and the '/' separator is what stops `org:acme` from also matching `org:acme-evil`. Absent ⇒ unconstrained.
          schema:
            type: string
        - name: limit
          in: query
          description: 'Page size. DEFAULT 100; MAXIMUM 1000. A larger value is CLAMPED to the maximum rather than refused — and the clamp is OBSERVABLE, because `page.limit` in the response reports the size the server actually applied, so a client can always read back what it got. (Note the deliberate divergence from the newer list operations in this API, which REFUSE an over-cap limit with a 400: this feed shipped with the clamp and has live consumers, so changing it would be a breaking behavior change on a published route.) A value of 0, a negative number, or a non-integer is treated as ABSENT and takes the default rather than being refused.'
          schema:
            type: integer
            format: int64
        - name: cursor
          in: query
          description: |-
            An OPAQUE page position: echo back a previous response's `page.next_cursor` VERBATIM, never construct one. Absent ⇒ the first page. It encodes the boundary row's timestamp AND its request id, because a position that cannot break a timestamp tie is not a total order and would skip or repeat rows. Opaque is an interop promise, not confidentiality — a cursor carries only the position, never a credential or a principal identity. An unreadable cursor is a 400 `bad_request` naming the parameter, never a silently-restarted-from-the-top page. Supplying `cursor` makes `offset` ignored.

            **A CURSOR IS SCOPED TO THE `node` IT WAS MINTED UNDER.** It records a POSITION, not the SET the position was taken in — so replaying one under a different `node` resolves it against the new subtree and returns a plausible page that means nothing in the sequence you were walking. RESTART the sequence (send no cursor) whenever you change `node`. This is not a confinement hole: rows are always re-fetched under the CURRENT request's `node`, so a cursor can never widen your scope or reach another tenant's rows — the failure mode is a wrong page for you, not somebody else's page. The server cannot yet detect the mistake on your behalf, which is why it is stated here.

            A STALE cursor — one whose boundary row has aged out of this bounded live view — does NOT fail. It resolves to the nearest surviving position at or older than the boundary instant and answers 200, which may RE-DELIVER a row you have already seen. That is deliberate and it is the opposite trade from an UNREADABLE cursor (a named 400): re-delivering a row is a duplicate you can detect by `req_id`, whereas skipping one is silent data loss. There is no header or member signaling that a repositioning happened, so a consumer that must not double-process should key on `req_id`.
          schema:
            type: string
        - name: offset
          in: query
          deprecated: true
          description: 'DEPRECATED — use `cursor`. Rows to skip within the already-subtree-scoped set (never the global pre-filter feed). It still answers until the sunset date and responses carry the RFC 9745 `Deprecation` and RFC 8594 `Sunset` headers when you use it. It is deprecated because an offset into a live newest-first feed is unstable: rows arriving at the head shift every later page, so a client paging by offset silently re-reads or misses rows. Ignored when `cursor` is supplied.'
          schema:
            type: integer
            format: int64
          x-deprecated-since: "2026-07-25T00:00:00Z"
          x-sunset: "2027-02-01T00:00:00Z"
          x-successor: governanceRefusals
      responses:
        "200":
          description: OK
          headers:
            Deprecation:
              description: RFC 9745 structured-field Date, e.g. `@1784937600` — when this operation's deprecated `offset` parameter was announced. Present ONLY on a response to a request that USED a deprecated parameter; a request paging by `cursor` carries none of these three headers.
              schema:
                type: string
            Link:
              description: RFC 8288 link with `rel="deprecation"`, anchored at the specific parameter (`#governanceRefusals-offset`) rather than at the document — a bare docs link on an operation with four parameters and one deprecation says something is going away and refuses to say what.
              schema:
                type: string
            Sunset:
              description: RFC 8594 HTTP-date, e.g. `Mon, 01 Feb 2027 00:00:00 GMT` — the date after which the deprecated `offset` parameter becomes removal-eligible. Sunset is always >= Deprecation (the RFC 9745 ordering rule, validated when the descriptor is registered). Absent when no removal date is set.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceRefusalsResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # Cursor-paginated: the response carries next_cursor and has_more. Send
            # the returned next_cursor as `cursor` for the following page; its ABSENCE
            # is the end of the feed. Cursors are opaque — do not parse one.

            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/refusals?limit=50" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Cursor-paginated: the response carries next_cursor and has_more. Send
            # the returned next_cursor as `cursor` for the following page; its ABSENCE
            # is the end of the feed. Cursors are opaque — do not parse one.

            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/refusals?limit=50",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Cursor-paginated: the response carries next_cursor and has_more. Send
            // the returned next_cursor as `cursor` for the following page; its ABSENCE
            // is the end of the feed. Cursors are opaque — do not parse one.

            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/refusals?limit=50`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/selfserve:
    get:
      operationId: governanceSelfServeRead
      summary: A delegated admin's own subtree view (nodes/status/keys/refusals), scoped to their bindings; a read outside the subtree is refused 403.
      description: |-
        What a DELEGATED org- or team-admin sees: the same governance facts an operator sees, narrowed to the subtree(s) they actually administer.

        **READ THE AUTHORIZATION CAREFULLY** — THE FLOOR IS NOT THE GATE. The route's viewer floor is only a pre-filter. The real decision is a FAIL-CLOSED check against an admin-authored role binding: a caller with no binding administers nothing and is refused 403, and that refusal is audited so a probe is on the record. Bindings are admin-authored only — there is no route in this API by which a delegated admin can grant themselves one, or widen the one they have.

        `bindings` lists every subtree the caller administers, so a UI can offer a switcher. `node` drills into one of them; a node outside every binding is a WIDEN-403, never a 404 — hiding the attempt behind "not found" would also hide it from whoever reads the audit trail. With no `node`, the response covers the caller's first bound subtree.

        `keys` is the same metadata-only shape the operator surface serves; a delegated admin sees no more of a key than an operator does.

        DELEGATION: the operation's real gate is a fail-closed subtree-delegation check in the handler — the role floor admits the call, and it succeeds only within the caller's delegated subtree; an out-of-subtree attempt is refused 403 and audited.
      tags:
        - governance
      x-role-floor: viewer
      x-required-scope: governance:read
      x-delegation: subtree
      security:
        - managementToken:
            - governance:read
        - session: []
      parameters:
        - name: node
          in: query
          description: Drill into a sub-node WITHIN one of the caller's bindings; a node outside every binding is a widen-403, never a 404. A tenancy node id is a '/'-joined path (`org:acme`, `org:acme/team:ml`); the constraint matches the node itself and everything below it, and the '/' separator is what stops `org:acme` from also matching `org:acme-evil`. Absent ⇒ unconstrained.
          schema:
            type: string
        - name: limit
          in: query
          description: 'Cap the refusal-feed rows included in the view. DEFAULT 100; MAXIMUM 1000. A larger value is clamped to the maximum SILENTLY: this response carries no page block, so nothing on the wire reports the size the server actually applied. A value of 0, a negative number, or a non-integer is treated as ABSENT and takes the default.'
          schema:
            type: integer
            format: int64
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceSelfServeResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/selfserve" \
              -H "Cookie: tt_session=$TOKENTRIAGE_SESSION" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            SESSION = os.environ["TOKENTRIAGE_SESSION"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/selfserve",
                method="GET",
                data=None,
                headers={
                    "Cookie": f"tt_session={SESSION}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const session: string = process.env.TOKENTRIAGE_SESSION ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/selfserve`, {
              method: "GET",
              headers: {
                Cookie: `tt_session=${session}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/selfserve/keys:
    post:
      operationId: governanceSelfServeMint
      summary: Mint a virtual key within the caller's delegated subtree (secret revealed once); an owner node outside the subtree is refused 403.
      description: |-
        The delegated mint: an org- or team-admin creates a virtual key owned by a node INSIDE the subtree they administer.

        **THE VIEWER FLOOR ON THIS ROUTE IS A PRE-FILTER**, NOT THE AUTHORIZATION. Read `x-delegation: subtree` beside it: the real gate is a fail-closed check that the caller holds an admin-authored role binding whose subtree CONTAINS the requested owner node. A viewer with no binding clears the floor, reaches the handler, and is refused 403 there — and the privileged `keys:mint` scope this operation requires is not granted by a viewer-role token's expansion in the first place. Every denial is audited with the REAL acting user.

        The subtree check is prefix-safe: a binding on `org:acme` does not authorize `org:acme-evil`, and a traversal like `../` is rejected before the prefix comparison rather than after it.

        **THE SECRET IS SHOWN ONCE**, under exactly the same discipline as the operator mint: SHA-256 at rest, never logged, never audited (the row names the kid), never re-fetchable.

        The delegated write seam stamps the REAL acting principal into the key store's own audit trail — never a generic "dashboard" actor — so a delegated mint is attributable end to end to the human who performed it.

        A 404 `feature_absent` means the delegated write seam is not wired on this instance.

        DELEGATION: the operation's real gate is a fail-closed subtree-delegation check in the handler — the role floor admits the call, and it succeeds only within the caller's delegated subtree; an out-of-subtree attempt is refused 403 and audited.
      tags:
        - governance
      x-role-floor: viewer
      x-required-scope: keys:mint
      x-writes: true
      x-delegation: subtree
      security:
        - managementToken:
            - keys:mint
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GovernanceMintRequest'
            example:
              node: $TOKENTRIAGE_NODE
              scopes:
                models: []
                providers: []
                endpoints: []
                allow_passthrough: false
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceMintResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # JSON-escape a shell value before it enters the body below. Without this a
            # password containing a backslash or a quote produces an invalid body (or, with
            # a crafted value, extra members). A control character is refused rather than
            # silently mis-sent.
            json_str() {
              case $1 in *[[:cntrl:]]*)
                printf 'json_str: refusing a value containing a control character\n' >&2; exit 1;;
              esac
              printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'
            }

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/selfserve/keys" \
              -H "Cookie: tt_session=$TOKENTRIAGE_SESSION" \
              -H "x-tt-csrf: $TOKENTRIAGE_CSRF" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<JSON
            {
              "node": "$(json_str "$TOKENTRIAGE_NODE")",
              "scopes": {
                "models": [],
                "providers": [],
                "endpoints": [],
                "allow_passthrough": false
              }
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            SESSION = os.environ["TOKENTRIAGE_SESSION"]
            CSRF = os.environ["TOKENTRIAGE_CSRF"]
            NODE = os.environ["TOKENTRIAGE_NODE"]

            payload = {
              "node": NODE,
              "scopes": {
                "models": [],
                "providers": [],
                "endpoints": [],
                "allow_passthrough": False
              }
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/selfserve/keys",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Cookie": f"tt_session={SESSION}",
                    "x-tt-csrf": CSRF,
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const session: string = process.env.TOKENTRIAGE_SESSION ?? "";
            const csrf: string = process.env.TOKENTRIAGE_CSRF ?? "";
            const node: string = process.env.TOKENTRIAGE_NODE ?? "";

            const payload = {
              "node": node,
              "scopes": {
                "models": [],
                "providers": [],
                "endpoints": [],
                "allow_passthrough": false
              }
            };

            const res: Response = await fetch(`${base}/tt/api/v1/governance/selfserve/keys`, {
              method: "POST",
              headers: {
                Cookie: `tt_session=${session}`,
                "x-tt-csrf": csrf,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/selfserve/overrides:
    post:
      operationId: governanceSelfServeOverride
      summary: Set a bounded per-node override within the caller's delegated subtree, under the inherited template ceiling; out-of-subtree or over-ceiling is refused 403.
      description: |-
        The delegated override: an org- or team-admin adjusts a limit for a node INSIDE the subtree they administer, and only up to a ceiling somebody else set.

        **THE VIEWER FLOOR IS A PRE-FILTER** — read `x-delegation: subtree`. The real gate is the same fail-closed role-binding check the delegated mint uses, and every denial is audited with the real acting user.

        **BOUNDED MEANS BOUNDED, AND FOUR DIFFERENT THINGS ARE REFUSED 403.** The status alone cannot tell them apart, so check the definition in `governanceLimitsList`: (1) the target node is outside your delegated subtree; (2) the definition's `override_allowed` is FALSE, so this limit admits no per-node override from anyone; (3) the definition declares NO `override_max`, and an unbounded delegated raise is refused rather than treated as unlimited, because "no ceiling declared" is not a grant; (4) your `amount` exceeds that `override_max`. The ceiling is the one the compiler enforces, read from the armed definitions, so the delegated path can never exceed what the operator path would allow.

        A 422 `unsupported_override_class` is a DIFFERENT refusal with a different cause, and the two are easy to confuse: 403 case (2) means this limit is CONFIGURED not to accept overrides (`override_allowed: false`, or an `attach: pinned` definition, which can carry no override policy at all), while the 422 means its limit TYPE is structurally incapable of honoring one — `rpm`, `tpm` and `concurrency` freeze their cap into a per-window store when governance compiles, so a reload would silently drop the override. Overrides apply only to the ledger-settled types (`budget_usd`, `quota_tokens`, `quota_requests`). Every one of these refusals is the verdict the reload itself would reach — this surface runs the compiler's own predicate rather than a copy of it — instead of a 200 that never applies.

        A 422 `validation_failed` with a `/node` field error means the target node has NO ROW in the runtime tenancy tree. An override is stored against a node row, and a node named in config alone is not enough: the node must be created first. This used to be a 500 whose remediation named `tokentriage index rebuild`, which is the wrong cure — no index rebuild creates a tenancy node.

        Like every override in this API, a success is COMPILE-BOUND and returns `pending_reload: true` — durable and audited now, enforcing after the next governance reload.

        A 404 `feature_absent` means the delegated write seam is not wired on this instance.

        DELEGATION: the operation's real gate is a fail-closed subtree-delegation check in the handler — the role floor admits the call, and it succeeds only within the caller's delegated subtree; an out-of-subtree attempt is refused 403 and audited.
      tags:
        - governance
      x-role-floor: viewer
      x-required-scope: governance:write
      x-writes: true
      x-delegation: subtree
      security:
        - managementToken:
            - governance:write
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GovernanceOverrideRequest'
            example:
              limit_id: $TOKENTRIAGE_LIMIT_ID
              node: $TOKENTRIAGE_NODE
              amount: 150000000000
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceOverrideResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        "422":
          description: 'Problem: unsupported_override_class, validation_failed.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # JSON-escape a shell value before it enters the body below. Without this a
            # password containing a backslash or a quote produces an invalid body (or, with
            # a crafted value, extra members). A control character is refused rather than
            # silently mis-sent.
            json_str() {
              case $1 in *[[:cntrl:]]*)
                printf 'json_str: refusing a value containing a control character\n' >&2; exit 1;;
              esac
              printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'
            }

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/selfserve/overrides" \
              -H "Cookie: tt_session=$TOKENTRIAGE_SESSION" \
              -H "x-tt-csrf: $TOKENTRIAGE_CSRF" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<JSON
            {
              "limit_id": "$(json_str "$TOKENTRIAGE_LIMIT_ID")",
              "node": "$(json_str "$TOKENTRIAGE_NODE")",
              "amount": 150000000000
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            SESSION = os.environ["TOKENTRIAGE_SESSION"]
            CSRF = os.environ["TOKENTRIAGE_CSRF"]
            LIMIT_ID = os.environ["TOKENTRIAGE_LIMIT_ID"]
            NODE = os.environ["TOKENTRIAGE_NODE"]

            payload = {
              "limit_id": LIMIT_ID,
              "node": NODE,
              "amount": 150000000000
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/selfserve/overrides",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Cookie": f"tt_session={SESSION}",
                    "x-tt-csrf": CSRF,
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const session: string = process.env.TOKENTRIAGE_SESSION ?? "";
            const csrf: string = process.env.TOKENTRIAGE_CSRF ?? "";
            const limitId: string = process.env.TOKENTRIAGE_LIMIT_ID ?? "";
            const node: string = process.env.TOKENTRIAGE_NODE ?? "";

            const payload = {
              "limit_id": limitId,
              "node": node,
              "amount": 150000000000
            };

            const res: Response = await fetch(`${base}/tt/api/v1/governance/selfserve/overrides`, {
              method: "POST",
              headers: {
                Cookie: `tt_session=${session}`,
                "x-tt-csrf": csrf,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/shadow:
    get:
      operationId: governanceShadow
      summary: Shadow-mode would-refuse counts per limit.
      description: |-
        The trust-ladder instrument: for each limit running in shadow mode, how many requests it WOULD have refused had it been enforcing. Every figure on this surface is counterfactual and is labeled shadow — none of these requests was refused, and no traffic was affected. It is the evidence an operator uses to decide whether a proposed cap is safe to arm.

        An instance with no previewer wired answers 200 with an honest empty (still shadow-labeled) set rather than an error: "no shadow limits are running" is a true and useful answer, where a 404 would not be.
      tags:
        - governance
      x-role-floor: viewer
      x-required-scope: governance:read
      security:
        - managementToken:
            - governance:read
        - session: []
      parameters:
        - name: node
          in: query
          description: Confine the counts to this subtree. A tenancy node id is a '/'-joined path (`org:acme`, `org:acme/team:ml`); the constraint matches the node itself and everything below it, and the '/' separator is what stops `org:acme` from also matching `org:acme-evil`. Absent ⇒ unconstrained.
          schema:
            type: string
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceShadowResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/shadow" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/shadow",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/shadow`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/status:
    get:
      operationId: governanceStatus
      summary: Per-node governance status (spend/quota/rate posture, forecast, overrun bound). Filterable by node subtree, posture, class, limit_type.
      description: |-
        The live per-node view of what governance is doing right now: for each tenancy node, every compiled limit with its posture, its consumption, a burn forecast where one is computable, and an honest bound on how far the figure may already be wrong.

        POSTURE is the four-state honesty ladder, and the node's headline posture is the LOUDEST across its limits: `alert_only` (a soft/shadow limit — it observes and never refuses), `enforcing` (a hard limit refusing at the cap), `fail_closed` (hard, and it refuses when the limiter itself is unavailable), `suspended` (armed to enforce, but the evaluator heartbeat is stale so enforcement has FAILED OPEN and traffic is flowing past the cap). `suspended` is the state an operator most needs to see, which is why it outranks the others for the badge.

        SPEND-CLASS limits report `burn`/`limit` as typed Money and count what they could not price in `unpriced_excluded`: unpriced rows are EXCLUDED from the dollar figure rather than folded in as $0, so burn is an understatement of a known size rather than a confident wrong number. Count-class limits report `observed`/`ceiling` instead; the irrelevant representation is absent, never a misleading zero.

        `overrun` is the B.6 approximate bound — tee lag, dropped rows, in-flight reservations, estimate skew, convergence lag — and `approximate: true` on it means exactly what it says: spend admitted by in-flight requests may not be reflected yet.

        The filters narrow at the LIMIT level, and a node whose limits all fail the filter is dropped entirely; each surviving node's headline posture is RECOMPUTED over the limits that were kept, so the badge can never reflect a limit the caller filtered out.

        An instance with no governance armed answers 200 with an empty `nodes` array.
      tags:
        - governance
      x-role-floor: viewer
      x-required-scope: governance:read
      security:
        - managementToken:
            - governance:read
        - session: []
      parameters:
        - name: node
          in: query
          description: Confine the view to this tenancy subtree (e.g. org:acme or org:acme/team:ml). A tenancy node id is a '/'-joined path (`org:acme`, `org:acme/team:ml`); the constraint matches the node itself and everything below it, and the '/' separator is what stops `org:acme` from also matching `org:acme-evil`. Absent ⇒ unconstrained.
          schema:
            type: string
        - name: posture
          in: query
          description: Keep only limits whose posture matches. Single-valued; absent ⇒ no constraint. A value outside this enum is refused `400 bad_request` naming the parameter and the legal values — never answered with an empty node list, which would be indistinguishable from an honest "nothing is in that posture".
          schema:
            type: string
            enum:
              - alert_only
              - enforcing
              - fail_closed
              - suspended
        - name: class
          in: query
          description: Keep only limits of this representation class — `spend` (dollar-denominated, reports Money) or `count` (tokens/requests/rate, reports raw counts). A value outside this enum is refused `400 bad_request` naming the parameter and the legal values.
          schema:
            type: string
            enum:
              - spend
              - count
        - name: limit_type
          in: query
          description: Keep only limits of this type. `budget_usd` is the sole spend-class type; the rest are count class. A value outside this enum is refused `400 bad_request` naming the parameter and the legal values.
          schema:
            type: string
            enum:
              - budget_usd
              - quota_tokens
              - quota_requests
              - rpm
              - tpm
              - concurrency
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceStatusResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/status" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/status",
                method="GET",
                data=None,
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/status`, {
              method: "GET",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/tenant-tokens:
    post:
      operationId: governanceTenantTokenMint
      summary: Mint a tenant-scoped viewer token (subtree-frozen; shown once).
      description: |-
        Issue a read-only credential that a tenant can use to see THEIR OWN subtree and nothing else — the mechanism behind giving a customer or an internal team visibility into their spend without giving them an account on this control plane.

        The token is FROZEN to one node subtree at mint. That freeze is not a filter applied at read time that a clever caller might widen: it is baked into the credential, and the read route refuses any node outside it with a 403.

        **THE TOKEN IS SHOWN ONCE.** The `token` field carries a live `tts_` credential, returned exactly once and never persisted (only its SHA-256 is stored), never logged, and never written to an audit row — the mint's audit row names the token's PUBLIC id and its frozen subtree, nothing more. `record` is the redacted register entry and has no field capable of holding the secret. Requires the privileged `keys:mint` scope.

        The request body is reflected into this document (`GovernanceTenantTokenMintRequest`) rather than described here, so the schema and the handler cannot drift apart. `expires_in` is a GO duration string, not an ISO 8601 one — see its field description.

        A 404 `feature_absent` means the share-token store is not open on this instance.

        Every branch of this endpoint audits exactly once, rejections included.
      tags:
        - governance
      x-role-floor: admin
      x-required-scope: keys:mint
      x-writes: true
      security:
        - managementToken:
            - keys:mint
        - session: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/GovernanceTenantTokenMintRequest'
            example:
              node: $TOKENTRIAGE_NODE
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceTenantTokenMintResponse'
        "404":
          description: 'Problem: feature_absent.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            # JSON-escape a shell value before it enters the body below. Without this a
            # password containing a backslash or a quote produces an invalid body (or, with
            # a crafted value, extra members). A control character is refused rather than
            # silently mis-sent.
            json_str() {
              case $1 in *[[:cntrl:]]*)
                printf 'json_str: refusing a value containing a control character\n' >&2; exit 1;;
              esac
              printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'
            }

            curl -sS --fail-with-body \
              -X POST "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/tenant-tokens" \
              -H "Authorization: Bearer $TOKENTRIAGE_ADMIN_TOKEN" \
              -H "Accept: application/json" \
              -H "Content-Type: application/json" \
              --data @- <<JSON
            {
              "node": "$(json_str "$TOKENTRIAGE_NODE")"
            }
            JSON
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_ADMIN_TOKEN"]
            NODE = os.environ["TOKENTRIAGE_NODE"]

            payload = {
              "node": NODE
            }

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/tenant-tokens",
                method="POST",
                data=json.dumps(payload).encode(),
                headers={
                    "Authorization": f"Bearer {TOKEN}",
                    "Accept": "application/json",
                    "Content-Type": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_ADMIN_TOKEN ?? "";
            const node: string = process.env.TOKENTRIAGE_NODE ?? "";

            const payload = {
              "node": node
            };

            const res: Response = await fetch(`${base}/tt/api/v1/governance/tenant-tokens`, {
              method: "POST",
              headers: {
                Authorization: `Bearer ${token}`,
                Accept: "application/json",
                "Content-Type": "application/json",
              },
              body: JSON.stringify(payload),
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
  /tt/api/v1/governance/tenant/{token}:
    get:
      operationId: governanceTenantRead
      summary: Read a tenant's own subtree via a tenant-scoped token (the token is the credential).
      description: |-
        The tenant-facing read: nodes, per-node limit status, keys (metadata only) and the recent refusal feed for the subtree the presented token is frozen to.

        This operation is PUBLIC at the authorization boundary because the token IS the credential — it is verified by the handler itself, and there is no session or management token involved. An unknown, revoked, expired, or wrong-kind token all return the SAME 404, so no probe can tell them apart — and so does a deployment whose share-token store is not open at all, deliberately: a distinct answer for that case would let an unauthenticated caller fingerprint the deployment before presenting any credential. This is the ONE governance operation that does not answer an absent dependency with `feature_absent` and a cure, because on a public route that cure is written for an operator and read by a stranger. The admin-authenticated mint still does.

        **THE WIDEN GUARD.** `node` drills DOWN within the token's frozen subtree. A node above it, beside it, or in another tenant entirely is refused 403 — deliberately NOT a 404, because a 404 would hide the attempt from the operator reading the audit trail. A blank `node` resolves to the frozen root. `scope_node` in the response always reports the freeze, so a tenant can see the boundary they are reading within.

        `read_only` is structurally always true: there is no write route on this surface.
      tags:
        - governance
      x-role-floor: public
      security: []
      parameters:
        - name: node
          in: query
          description: Drill into a sub-node WITHIN the token's frozen subtree. A node outside it is refused 403, never silently widened and never hidden behind a 404. A tenancy node id is a '/'-joined path (`org:acme`, `org:acme/team:ml`); the constraint matches the node itself and everything below it, and the '/' separator is what stops `org:acme` from also matching `org:acme-evil`. Absent ⇒ unconstrained.
          schema:
            type: string
        - name: limit
          in: query
          description: 'Cap the refusal-feed rows included in the view. DEFAULT 100; MAXIMUM 1000. A larger value is clamped to the maximum SILENTLY: this response carries no page block, so nothing on the wire reports the size the server actually applied. A value of 0, a negative number, or a non-integer is treated as ABSENT and takes the default.'
          schema:
            type: integer
            format: int64
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GovernanceTenantViewResponse'
        default:
          description: Any framework problem (bad_request, unauthenticated, forbidden, not_found, method_not_allowed, payload_too_large, rate_limited, internal) — application/problem+json with a registered type and request_id.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/Problem'
      x-code-samples:
        - lang: Shell
          label: curl
          source: |
            curl -sS --fail-with-body \
              -X GET "$TOKENTRIAGE_ADMIN_URL/tt/api/v1/governance/tenant/$TOKENTRIAGE_TOKEN" \
              -H "Accept: application/json"
        - lang: Python
          label: Python
          source: |
            # Standard library only — no pip install, which is what an air-gapped
            # deployment can actually run.
            import json
            import os
            import urllib.error
            import urllib.request

            BASE = os.environ["TOKENTRIAGE_ADMIN_URL"]
            TOKEN = os.environ["TOKENTRIAGE_TOKEN"]

            req = urllib.request.Request(
                f"{BASE}/tt/api/v1/governance/tenant/{TOKEN}",
                method="GET",
                data=None,
                headers={
                    "Accept": "application/json",
                },
            )

            try:
                with urllib.request.urlopen(req) as resp:
                    print(json.dumps(json.load(resp), indent=2))
            except urllib.error.HTTPError as err:
                # Every non-2xx is application/problem+json (RFC 9457). Switch on `code` —
                # it is stable contract, drawn from the closed registry at /tt/api/v1/problems.
                # `detail` is prose for humans and may change; never parse it.
                problem = json.loads(err.read() or b"{}")
                print(
                    f"{err.code} {problem.get('code')}: {problem.get('detail')}",
                    f"(request_id={problem.get('request_id')}"
                    + (f", required_scope={problem['required_scope']}" if 'required_scope' in problem else "")
                    + ")",
                )
                raise SystemExit(1)
        - lang: JavaScript
          label: TypeScript
          source: |
            // Node 18+ (global fetch), no dependencies. Node 22.6+ runs this file
            // directly as .mts; tsc compiles it unchanged.
            const base: string = process.env.TOKENTRIAGE_ADMIN_URL ?? "";
            const token: string = process.env.TOKENTRIAGE_TOKEN ?? "";

            const res: Response = await fetch(`${base}/tt/api/v1/governance/tenant/${token}`, {
              method: "GET",
              headers: {
                Accept: "application/json",
              },
            });

            if (!res.ok) {
              // Every non-2xx is application/problem+json (RFC 9457). Switch on `code` — it is
              // stable contract from the closed registry at /tt/api/v1/problems. `detail` is
              // prose for humans and may change; never parse it.
              const problem = (await res.json().catch(() => ({}))) as {
                code?: string;
                detail?: string;
                request_id?: string;
                required_scope?: string;
              };
              console.error(
                `${res.status} ${problem.code ?? res.statusText}: ${problem.detail ?? ""}`,
                { request_id: problem.request_id, required_scope: problem.required_scope },
              );
              process.exit(1);
            }
            console.log(JSON.stringify(await res.json(), null, 2));
    parameters:
      - name: token
        in: path
        required: true
        description: 'The tenant-scoped viewer token''s SECRET value (`tts_…`), as returned once by `governanceTenantTokenMint`. This route is PUBLIC at the authorization boundary because the token IS the credential — it is verified by the handler itself and carries its own frozen subtree scope. It is redacted from every audit row and log line BEFORE the write, it is never echoed in a response or a problem body, and an unknown, revoked, expired or wrong-kind token all return the SAME 404 so no probe can distinguish them. This parameter''s value is a LIVE CREDENTIAL: it is redacted before any audit or log write and never echoed in a problem body (CA5).'
        schema:
          type: string
        x-credential: true
components:
  schemas:
    BreakerReachability:
      type: object
      properties:
        consecutive_failures:
          type: integer
          format: int64
        err:
          type: string
        reachable:
          type: boolean
        since:
          type: string
        state:
          type: string
      required:
        - consecutive_failures
        - reachable
        - state
    DistributedBound:
      type: object
      properties:
        approximate:
          type: boolean
        convergence_lag:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        convergence_lag_rows:
          type: integer
          format: int64
        degraded:
          type: boolean
        note:
          type: string
        remote_burn:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        stale:
          type: boolean
      required:
        - approximate
        - degraded
        - note
        - stale
    EnforcementMode:
      type: object
      properties:
        bound:
          type: string
        exact:
          type: boolean
        live:
          type: integer
          format: int64
        share:
          type: integer
          format: int64
        strategy:
          type: string
      required:
        - exact
        - strategy
    ExportExcluded:
      type: object
      properties:
        unpriced:
          type: integer
          format: int64
      required:
        - unpriced
    ExportMeta:
      type: object
      properties:
        demo:
          type: boolean
        estimated_rows:
          type: integer
          format: int64
        excluded:
          $ref: '#/components/schemas/ExportExcluded'
        generated_at:
          type: string
        instance:
          type: string
        pricing_snapshots:
          type: array
          items:
            type: string
        query:
          anyOf:
            - $ref: '#/components/schemas/FilterNode'
            - type: "null"
        rows:
          type: integer
          format: int64
        v:
          type: integer
          format: int64
          description: The EXPORT SCHEMA version — stays 1, independent of the API epoch in info.version; it bumps only when the export format itself changes (plan §4 row 1).
        window:
          $ref: '#/components/schemas/Window'
      required:
        - demo
        - estimated_rows
        - excluded
        - generated_at
        - instance
        - pricing_snapshots
        - rows
        - v
        - window
    FieldError:
      type: object
      properties:
        issue:
          type: string
        pointer:
          type: string
      required:
        - issue
        - pointer
    FilterNode:
      type: object
      description: 'A filter AST node (internal/filter). HAND-PINNED as an opaque object — reflecting the recursive AST would document less than the grammar does. JSON form: {"and":[…]} · {"or":[…]} · {"not":{…}} · leaf {"f":"<field>","op":"<op>","v":<value>} with ops eq|neq|lt|lte|gt|gte|in|glob|exists; fields are the closed registry served in /capabilities.filter_fields. The equivalent q= text form is the frozen filter.ParseText grammar (plan Appendix B.3).'
    FleetCoverage:
      type: object
      properties:
        basis:
          type: string
        is_partial:
          type: boolean
        max_abs_skew_ms:
          type: integer
          format: int64
        replicas:
          type: array
          items:
            $ref: '#/components/schemas/ReplicaCoverage'
        replicas_answered:
          type: integer
          format: int64
        replicas_total:
          type: integer
          format: int64
        trace_linked:
          type: integer
          format: int64
      required:
        - basis
        - is_partial
        - max_abs_skew_ms
        - replicas
        - replicas_answered
        - replicas_total
    FleetGovernanceHealth:
      type: object
      properties:
        coverage:
          $ref: '#/components/schemas/FleetCoverage'
        kind:
          type: string
        replicas:
          type: array
          items:
            $ref: '#/components/schemas/ReplicaGovernanceHealth'
      required:
        - coverage
        - kind
        - replicas
    GovernanceConvergence:
      type: object
      properties:
        bound:
          $ref: '#/components/schemas/DistributedBound'
        cap:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        class:
          type: string
        cold_start:
          type: boolean
        degraded:
          type: boolean
        limit_id:
          type: string
        local_burn:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        node:
          type: string
        remote_burn:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        replicas:
          type: integer
          format: int64
        scope_label:
          type: string
        stale:
          type: boolean
      required:
        - bound
        - class
        - cold_start
        - degraded
        - limit_id
        - node
        - replicas
        - scope_label
        - stale
    GovernanceForecast:
      type: object
      properties:
        already_exhausted:
          type: boolean
        exhaust_at:
          type: string
        insufficient:
          type: boolean
        method:
          type: string
        rate_usd_per_hour:
          type:
            - number
            - "null"
      required:
        - already_exhausted
        - insufficient
        - method
    GovernanceHealthResponse:
      type: object
      properties:
        capability:
          $ref: '#/components/schemas/SharedStateDisclosure'
        convergence:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceConvergence'
        kind:
          type: string
        postgres:
          anyOf:
            - $ref: '#/components/schemas/PostgresReachability'
            - type: "null"
        redis:
          anyOf:
            - $ref: '#/components/schemas/BreakerReachability'
            - type: "null"
        suspensions:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceSuspension'
      required:
        - capability
        - convergence
        - kind
        - suspensions
    GovernanceKey:
      type: object
      properties:
        created_at:
          type: string
        expires_at:
          type: string
        kid:
          type: string
        label:
          type: string
        last_used:
          type: string
        node:
          type: string
        rotated_from:
          type: string
        scopes:
          $ref: '#/components/schemas/GovernanceKeyScopes'
        state:
          type: string
        stub:
          type: string
      required:
        - kid
        - node
        - scopes
        - state
        - stub
    GovernanceKeyResponse:
      type: object
      properties:
        key:
          $ref: '#/components/schemas/GovernanceKey'
      required:
        - key
    GovernanceKeyScopes:
      type: object
      properties:
        allow_passthrough:
          type: boolean
        endpoints:
          type: array
          items:
            type: string
        models:
          type: array
          items:
            type: string
        providers:
          type: array
          items:
            type: string
      required:
        - allow_passthrough
        - endpoints
        - models
        - providers
    GovernanceKeysResponse:
      type: object
      properties:
        keys:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceKey'
      required:
        - keys
    GovernanceLimit:
      type: object
      properties:
        burn:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        ceiling:
          type:
            - number
            - "null"
        class:
          type: string
        forecast:
          anyOf:
            - $ref: '#/components/schemas/GovernanceForecast'
            - type: "null"
        fraction:
          type: number
        limit:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        limit_id:
          type: string
        limit_type:
          type: string
        observed:
          type:
            - number
            - "null"
        overrun_bound:
          $ref: '#/components/schemas/GovernanceOverrunBound'
        period:
          type: string
        posture:
          type: string
        resets_at:
          type: string
        unit:
          type: string
        unpriced_excluded:
          type: integer
          format: int64
      required:
        - class
        - fraction
        - limit_id
        - limit_type
        - overrun_bound
        - posture
    GovernanceLimitDef:
      type: object
      properties:
        amount:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        amount_count:
          type:
            - integer
            - "null"
          format: int64
        attach:
          type: string
        class:
          type: string
        enforcement:
          anyOf:
            - $ref: '#/components/schemas/EnforcementMode'
            - type: "null"
        level:
          type: string
        limit_id:
          type: string
        limit_type:
          type: string
        node:
          type: string
        override_allowed:
          type: boolean
        override_max:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        override_max_count:
          type:
            - integer
            - "null"
          format: int64
        period:
          type: string
        posture:
          type: string
        unit:
          type: string
      required:
        - attach
        - class
        - limit_id
        - limit_type
        - override_allowed
        - posture
    GovernanceLimitsResponse:
      type: object
      properties:
        definitions:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceLimitDef'
        note:
          type: string
        overrides:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceOverride'
      required:
        - definitions
        - note
        - overrides
    GovernanceMintRequest:
      type: object
      properties:
        expires_at:
          type: string
        label:
          type: string
        node:
          type: string
        scopes:
          $ref: '#/components/schemas/GovernanceKeyScopes'
      required:
        - node
        - scopes
    GovernanceMintResponse:
      type: object
      description: 'The SHOWN-ONCE mint/rotate reveal: secret is streamed exactly once and never persisted, logged, or re-fetchable (the store holds only its SHA-256). The ONE governance schema that carries a secret; every read surface is secret-free.'
      properties:
        expires_at:
          type: string
        kid:
          type: string
        label:
          type: string
        node:
          type: string
        secret:
          type: string
        stub:
          type: string
        warning:
          type: string
      required:
        - kid
        - node
        - secret
        - stub
        - warning
    GovernanceNode:
      type: object
      properties:
        label:
          type: string
        limits:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceLimit'
        node:
          type: string
        posture:
          type: string
      required:
        - limits
        - node
        - posture
    GovernanceNodeCreateRequest:
      type: object
      properties:
        id:
          type: string
        kind:
          type: string
        label:
          type: string
        parent:
          type: string
      required:
        - id
        - kind
    GovernanceNodeDeleteResponse:
      type: object
      properties:
        deleted:
          type: boolean
        id:
          type: string
      required:
        - deleted
        - id
    GovernanceNodeResponse:
      type: object
      properties:
        id:
          type: string
      required:
        - id
    GovernanceNodesResponse:
      type: object
      properties:
        nodes:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceTreeNode'
      required:
        - nodes
    GovernanceOverride:
      type: object
      properties:
        amount:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        amount_count:
          type:
            - integer
            - "null"
          format: int64
        class:
          type: string
        limit_id:
          type: string
        node:
          type: string
        note:
          type: string
        pending_reload:
          type: boolean
        raw_amount:
          type: integer
          format: int64
          description: The exact stored int64 (nanodollars for budget_usd, a count otherwise) — for lossless round-trip back through the set endpoint.
      required:
        - limit_id
        - node
        - note
        - pending_reload
        - raw_amount
    GovernanceOverrideDeleteResponse:
      type: object
      properties:
        deleted:
          type: boolean
        limit_id:
          type: string
        node:
          type: string
        note:
          type: string
      required:
        - deleted
        - limit_id
        - node
        - note
    GovernanceOverrideRequest:
      type: object
      properties:
        amount:
          type: integer
          format: int64
          description: The limit's STORED unit — NANODOLLARS for budget_usd (integer, so money never rounds through a float; $1.00 == 1000000000), a raw count (tokens/requests) otherwise. amount <= 0 is refused (it would disable the cap).
        limit_id:
          type: string
        node:
          type: string
      required:
        - amount
        - limit_id
        - node
    GovernanceOverrideResponse:
      type: object
      properties:
        note:
          type: string
        override:
          $ref: '#/components/schemas/GovernanceOverride'
      required:
        - note
        - override
    GovernanceOverrunBound:
      type: object
      properties:
        approximate:
          type: boolean
        convergence_lag_rows:
          type: integer
          format: int64
        dropped_rows:
          type: integer
          format: int64
        estimate_skew_p95:
          type: number
        in_flight_reserved:
          type: integer
          format: int64
        note:
          type: string
        tee_lag_rows:
          type: integer
          format: int64
      required:
        - approximate
        - dropped_rows
        - note
        - tee_lag_rows
    GovernancePage:
      type: object
      properties:
        has_more:
          type: boolean
          description: 'Whether another page follows. Always present, including as `false` on the last page: an explicit false is a statement, where an absent member would be a silence a client has to guess at. ★ WHAT IT IS DERIVED FROM DIFFERS BY FEED, AND A CLIENT THAT ASSUMES ONE RULE FOR BOTH IS WRONG ON ONE OF THEM. On the FINITE feeds the two members AGREE: an absent `next_cursor` and `has_more: false` say the same thing there, whether the feed reaches that through `Page()` — which derives one from the other in a single expression — or builds the block itself and holds the same relation by hand. On the RING-BACKED `GET /events` feed it is computed independently of the cursor and means "more matching events exist RIGHT NOW, fetch again", while `next_cursor` means "resume from here whenever you next poll" — the split is deliberate, and without it a caught-up consumer had no duplicate-free way to poll again.'
        limit:
          type: integer
          format: int64
          description: The EFFECTIVE page size the server applied. A requested limit above the 1000 maximum is clamped, and this field is how a client observes that rather than having to infer it.
        next_cursor:
          type: string
          description: 'The opaque position to send as `cursor` for the NEXT page. ABSENT at the end of a FINITE feed — there absence IS the end-of-feed signal, never a present-and-empty string, so testing for the member''s PRESENCE is correct and testing for a non-empty value is not. ★ THE RING-BACKED FEED IS THE EXCEPTION AND IS DISCLOSED AS ONE: `GET /events` has no end, so its `next_cursor` is present on every response that has a position to report — including beside `has_more: false` and including on an empty page — and a consumer that stopped polling when the cursor went absent would stop polling the event log forever. Read `EventsResponse`''s own description before writing a loop against that operation. Echo it back VERBATIM; never construct or modify one, and never assume its interior is stable.'
        next_offset:
          type:
            - integer
            - "null"
          format: int64
          description: 'The offset of the next page, absent at the end of the feed. ★ THIS MEMBER IS NOT DEPRECATED — the `offset` REQUEST PARAMETER is, and only it (RFC 9745/8594; see that parameter for the dates). Nothing here is scheduled for removal: neither this operation, nor `limit`, nor this member. What the deprecation asks of you is to stop SENDING `offset`, and the migration is to send `cursor` and follow `next_cursor`, which is stable against rows arriving at the head of this newest-first feed. A response MEMBER cannot carry a conditional deprecation header, which is why the announcement lives on the request parameter; an earlier version of this note read that as the member being deprecated too, which would send an integrator to migrate off something that is not going away.'
        offset:
          type: integer
          format: int64
          description: The position this page was actually taken from. When the request supplied a `cursor`, this reports the offset the cursor RESOLVED to (not the `offset` parameter, which a cursor request ignores), so the body never claims a position the server did not use.
        returned:
          type: integer
          format: int64
        total:
          type: integer
          format: int64
          description: The row count within the requested subtree, SATURATED at this live view's 1000-row bound — it is not the true subtree total, and it stops growing at the cap. Use governanceRefusalsExport for an uncapped count.
      required:
        - has_more
        - limit
        - offset
        - returned
        - total
    GovernancePreviewAttach:
      type: object
      properties:
        level:
          type: string
        node:
          type: string
    GovernancePreviewExactLimit:
      type: object
      description: Wire shape of GovernancePreviewExactLimit (its MarshalJSON projects through this alias — pinned, not naively reflected).
      properties:
        burn:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        ceiling:
          type:
            - number
            - "null"
        class:
          type: string
        estimate:
          type: string
        fraction:
          type: number
        limit:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        limit_id:
          type: string
        limit_type:
          type: string
        node:
          type: string
        observed:
          type:
            - number
            - "null"
        period:
          type: string
        resets_at:
          type: string
        rows:
          type: integer
          format: int64
        unit:
          type: string
        unpriced_excluded:
          type: integer
          format: int64
        would_refuse:
          type: boolean
      required:
        - class
        - estimate
        - fraction
        - limit_id
        - limit_type
        - node
        - rows
        - would_refuse
    GovernancePreviewLimitSpec:
      type: object
      properties:
        algorithm:
          type: string
        amount:
          type: integer
          format: int64
        amount_usd:
          type:
            - number
            - "null"
        attach:
          $ref: '#/components/schemas/GovernancePreviewAttach'
        burst:
          type: integer
          format: int64
        id:
          type: string
        match:
          $ref: '#/components/schemas/GovernancePreviewMatch'
        period:
          type: string
        type:
          type: string
      required:
        - attach
        - id
        - type
    GovernancePreviewMatch:
      type: object
      properties:
        endpoint:
          type: array
          items:
            type: string
        model:
          type: array
          items:
            type: string
        provider:
          type: array
          items:
            type: string
    GovernancePreviewRateLimit:
      type: object
      description: Wire shape of GovernancePreviewRateLimit (its MarshalJSON projects through this alias — pinned, not naively reflected).
      properties:
        amount:
          type: integer
          format: int64
        approx_peak:
          type:
            - integer
            - "null"
          format: int64
        can_estimate:
          type: boolean
        estimate:
          type: string
        fraction:
          type:
            - number
            - "null"
        limit_id:
          type: string
        limit_type:
          type: string
        node:
          type: string
        rate_windows_approximated:
          type: boolean
        reason:
          type: string
        unit:
          type: string
        window:
          type: string
        would_refuse:
          type:
            - boolean
            - "null"
      required:
        - amount
        - can_estimate
        - estimate
        - limit_id
        - limit_type
        - node
        - rate_windows_approximated
    GovernancePreviewRefusal:
      type: object
      properties:
        burn:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        ceiling:
          type:
            - number
            - "null"
        class:
          type: string
        fraction:
          type: number
        limit:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        limit_id:
          type: string
        limit_type:
          type: string
        node:
          type: string
        observed:
          type:
            - number
            - "null"
      required:
        - class
        - fraction
        - limit_id
        - limit_type
        - node
    GovernancePreviewRequest:
      type: object
      properties:
        limits:
          type: array
          items:
            $ref: '#/components/schemas/GovernancePreviewLimitSpec'
        tenants:
          type: array
          items:
            $ref: '#/components/schemas/GovernancePreviewTenant'
      required:
        - limits
    GovernancePreviewResponse:
      type: object
      description: Wire shape of GovernancePreviewResponse (its MarshalJSON projects through this alias — pinned, not naively reflected).
      properties:
        approximations:
          type: array
          items:
            type: string
        exact:
          type: array
          items:
            $ref: '#/components/schemas/GovernancePreviewExactLimit'
        kind:
          type: string
        primary_instrument:
          type: string
        rate_class:
          type: array
          items:
            $ref: '#/components/schemas/GovernancePreviewRateLimit'
        rows_replayed:
          type: integer
          format: int64
        torn:
          type: integer
          format: int64
        truncated:
          type: boolean
        window:
          $ref: '#/components/schemas/Window'
        would_refuse_now:
          type: array
          items:
            $ref: '#/components/schemas/GovernancePreviewRefusal'
        would_refuse_now_note:
          type: string
      required:
        - approximations
        - exact
        - kind
        - primary_instrument
        - rate_class
        - rows_replayed
        - torn
        - truncated
        - window
        - would_refuse_now
        - would_refuse_now_note
    GovernancePreviewTenant:
      type: object
      properties:
        org:
          type: string
        teams:
          type: array
          items:
            type: string
        users:
          type: object
          additionalProperties:
            type: array
            items:
              type: string
      required:
        - org
    GovernanceReconcileResponse:
      type: object
      properties:
        adopted:
          type: boolean
        alert:
          type: string
        drift_count:
          type: integer
          format: int64
        kind:
          type: string
        rollup_source:
          type: string
        rows_folded:
          type: integer
          format: int64
        scopes:
          type: array
          items:
            $ref: '#/components/schemas/ReconcileDriftScope'
        torn:
          type: integer
          format: int64
      required:
        - adopted
        - alert
        - drift_count
        - kind
        - rollup_source
        - rows_folded
        - scopes
        - torn
    GovernanceRefusal:
      type: object
      properties:
        approximate:
          type: boolean
        http_status:
          type: integer
          format: int64
        kid:
          type: string
        limit_id:
          type: string
        limit_type:
          type: string
        node:
          type: string
        posture:
          type: string
        req_id:
          type: string
        ts:
          type: string
        type:
          type: string
      required:
        - approximate
        - http_status
        - req_id
        - ts
        - type
    GovernanceRefusalExportResponse:
      type: object
      properties:
        refusals:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceRefusal'
        tokentriage_export:
          $ref: '#/components/schemas/ExportMeta'
      required:
        - refusals
        - tokentriage_export
    GovernanceRefusalRef:
      type: object
      properties:
        feed:
          type: string
        limit_id:
          type: string
        node:
          type: string
        req_id:
          type: string
    GovernanceRefusalsResponse:
      type: object
      properties:
        page:
          anyOf:
            - $ref: '#/components/schemas/GovernancePage'
            - type: "null"
        refusals:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceRefusal'
      required:
        - refusals
    GovernanceReportExcluded:
      type: object
      properties:
        unattributed:
          type: integer
          format: int64
        unpriced:
          type: integer
          format: int64
      required:
        - unattributed
        - unpriced
    GovernanceRevokeResponse:
      type: object
      properties:
        kid:
          type: string
        state:
          type: string
      required:
        - kid
        - state
    GovernanceSelfServeBinding:
      type: object
      properties:
        role:
          type: string
        root:
          type: string
      required:
        - role
        - root
    GovernanceSelfServeResponse:
      type: object
      properties:
        bindings:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceSelfServeBinding'
        can_write:
          type: boolean
        keys:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceKey'
        node:
          type: string
        nodes:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceTreeNode'
        note:
          type: string
        refusals:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceRefusal'
        status:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceNode'
        user:
          type: string
      required:
        - bindings
        - can_write
        - keys
        - node
        - nodes
        - note
        - refusals
        - status
        - user
    GovernanceShadowLimit:
      type: object
      properties:
        limit_id:
          type: string
        rows_observed:
          type: integer
          format: int64
        would_refuse_count:
          type: integer
          format: int64
      required:
        - limit_id
        - rows_observed
        - would_refuse_count
    GovernanceShadowResponse:
      type: object
      properties:
        limits:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceShadowLimit'
        note:
          type: string
        shadow:
          type: boolean
      required:
        - limits
        - note
        - shadow
    GovernanceStatusResponse:
      type: object
      properties:
        nodes:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceNode'
      required:
        - nodes
    GovernanceSuspension:
      type: object
      properties:
        bound:
          $ref: '#/components/schemas/DistributedBound'
        cap:
          anyOf:
            - $ref: '#/components/schemas/Money'
            - type: "null"
        cap_amount:
          type:
            - integer
            - "null"
          format: int64
        class:
          type: string
        fail_posture:
          type: string
        limit_id:
          type: string
        limit_type:
          type: string
        node:
          type: string
        reason:
          type: string
      required:
        - bound
        - class
        - fail_posture
        - limit_id
        - limit_type
        - node
        - reason
    GovernanceTenantTokenMintRequest:
      type: object
      properties:
        expires_in:
          type: string
          description: 'A GO DURATION STRING, not an ISO 8601 duration and not a number of seconds: a decimal number with a unit suffix, e.g. `168h`, `30m`, `1h30m`. Valid units are ns, us, ms, s, m, h — there is no day or week unit, so a week is `168h`. ABSENT means the token never expires; a PRESENT value must be positive, so `0s` is refused 400 rather than silently minting an eternal credential.'
        node:
          type: string
      required:
        - node
    GovernanceTenantTokenMintResponse:
      type: object
      properties:
        audit_id:
          type: string
        record:
          $ref: '#/components/schemas/ShareToken'
        token:
          type: string
      required:
        - audit_id
        - record
        - token
    GovernanceTenantViewResponse:
      type: object
      properties:
        keys:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceKey'
        node:
          type: string
        nodes:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceTreeNode'
        read_only:
          type: boolean
        refusals:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceRefusal'
        scope_node:
          type: string
          description: The subtree root the presented token is FROZEN to — the boundary of everything in this response. `node` is the effective (possibly drilled-down) subtree, always this node or a descendant of it.
        status:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceNode'
      required:
        - keys
        - node
        - nodes
        - read_only
        - refusals
        - scope_node
        - status
    GovernanceTreeNode:
      type: object
      properties:
        id:
          type: string
        kind:
          type: string
        label:
          type: string
        parent:
          type: string
      required:
        - id
        - kind
    GovernanceUsageReport:
      type: object
      properties:
        excluded:
          $ref: '#/components/schemas/GovernanceReportExcluded'
        period:
          type: string
        rows:
          type: array
          items:
            $ref: '#/components/schemas/GovernanceUsageRow'
        tokentriage_export:
          $ref: '#/components/schemas/ExportMeta'
      required:
        - excluded
        - period
        - rows
        - tokentriage_export
    GovernanceUsageRow:
      type: object
      properties:
        bucket:
          type: string
        estimated_rows:
          type: integer
          format: int64
        input_tokens:
          type: integer
          format: int64
        node:
          type: string
        output_tokens:
          type: integer
          format: int64
        requests:
          type: integer
          format: int64
        spend:
          $ref: '#/components/schemas/Money'
        total_tokens:
          type: integer
          format: int64
        unpriced_rows:
          type: integer
          format: int64
      required:
        - bucket
        - estimated_rows
        - input_tokens
        - node
        - output_tokens
        - requests
        - spend
        - total_tokens
        - unpriced_rows
    IndexedThrough:
      type: object
      properties:
        file:
          type: string
        offset:
          type: integer
          format: int64
        ts:
          type: string
      required:
        - file
        - offset
        - ts
    LostEventsBound:
      type: object
      description: 'The gap disclosure carried by `cursor_expired`. It follows the shape `provenance.drop_bound` established for the metering gap: a bound that is labeled as a bound, and a `measured` flag that turns the unmeasured case into `null` instead of into a confident zero.'
      properties:
        at_most:
          type:
            - integer
            - "null"
          format: int64
          description: 'The UPPER BOUND on events you did not receive, or null when the gap could not be sized. It counts the feed positions between the cursor you presented and the position named by `oldest_seq`, so it is the exact loss for an unfiltered consumer and a ceiling for a filtered one — the rows are already evicted, so which of them a `types` or `since` filter would have selected is no longer knowable. The member is named for the reading it licenses: a ceiling, not a count.'
        measured:
          type: boolean
          description: Whether the gap could be sized against this feed. `false` ⇒ `at_most` is null and `reason` says why; the usual cause is a cursor minted by a different ring incarnation, whose sequences do not address the live ring, so the gap's size is unknowable rather than zero.
        reason:
          type: string
          description: Why the gap could not be sized — present exactly when `measured` is false. An unmeasured bound with no reason would be indistinguishable from a member the server forgot to fill in.
      required:
        - measured
    Money:
      type: object
      description: 'A typed dollar (§2.6). HAND-PINNED: Money.MarshalJSON nulls usd on an unpriced value — usd is null when unpriced, NEVER 0 — which reflection over the *float64 field cannot express. estimated/unpriced carry the provenance, so a raw float or a $0-for-unpriced is unrepresentable.'
      properties:
        estimated:
          type: boolean
        unpriced:
          type: boolean
          description: true ⇒ this value is excluded from dollar totals and usd is null
        usd:
          type:
            - number
            - "null"
          description: null when unpriced (never 0)
      required:
        - estimated
        - unpriced
        - usd
    PageResponse:
      type: object
      properties:
        has_more:
          type: boolean
          description: 'Whether another page follows. Always present, including as `false` on the last page: an explicit false is a statement, where an absent member would be a silence a client has to guess at. ★ WHAT IT IS DERIVED FROM DIFFERS BY FEED, AND A CLIENT THAT ASSUMES ONE RULE FOR BOTH IS WRONG ON ONE OF THEM. On the FINITE feeds the two members AGREE: an absent `next_cursor` and `has_more: false` say the same thing there, whether the feed reaches that through `Page()` — which derives one from the other in a single expression — or builds the block itself and holds the same relation by hand. On the RING-BACKED `GET /events` feed it is computed independently of the cursor and means "more matching events exist RIGHT NOW, fetch again", while `next_cursor` means "resume from here whenever you next poll" — the split is deliberate, and without it a caught-up consumer had no duplicate-free way to poll again.'
        next_cursor:
          type: string
          description: 'The opaque position to send as `cursor` for the NEXT page. ABSENT at the end of a FINITE feed — there absence IS the end-of-feed signal, never a present-and-empty string, so testing for the member''s PRESENCE is correct and testing for a non-empty value is not. ★ THE RING-BACKED FEED IS THE EXCEPTION AND IS DISCLOSED AS ONE: `GET /events` has no end, so its `next_cursor` is present on every response that has a position to report — including beside `has_more: false` and including on an empty page — and a consumer that stopped polling when the cursor went absent would stop polling the event log forever. Read `EventsResponse`''s own description before writing a loop against that operation. Echo it back VERBATIM; never construct or modify one, and never assume its interior is stable.'
      required:
        - has_more
    PostgresReachability:
      type: object
      properties:
        err:
          type: string
        pending_propagation:
          type: integer
          format: int64
        replica_ready:
          type: boolean
        revocation_bound:
          type: string
        since:
          type: string
        staleness_bound:
          type: string
        up:
          type: boolean
      required:
        - replica_ready
        - revocation_bound
        - up
    Problem:
      type: object
      description: 'The ONE error shape this API returns. Every non-2xx response on every operation is `application/problem+json` (RFC 9457) with this body — there is no second error envelope, no per-resource variant, and no bare string. **Switch on `code`, never on `detail`.** `code` is a stable identifier drawn from a CLOSED registry served at `/tt/api/v1/problems`; `detail` is occurrence-specific prose written for a human and may be reworded at any time without a contract change. `status` mirrors the HTTP status, so a client that has only the body still knows what happened. Extension members are closed and per-type: which ones an occurrence may carry is recorded in that type''s registry entry and documented on its `/tt/api/problems/<slug>` page. RFC 9457 requires clients to IGNORE members they do not recognize, which is what makes adding one an additive change. A problem discloses the real reason with the same honesty as a success shape — but never a secret and never the value of a credential path parameter.'
      properties:
        code:
          type: string
          description: 'The stable, machine-readable identifier for this problem type — the same slug as the tail of `type`, repeated here so a client can branch on one flat string and an operator can grep logs for it. **This is the member to switch on.** The vocabulary is CLOSED: every value is registered, enumerated at `/tt/api/v1/problems`, and never reused after retirement. Each operation''s response catalog lists the domain codes it can return; the framework codes (`bad_request`, `unauthenticated`, `forbidden`, `not_found`, `method_not_allowed`, `payload_too_large`, `rate_limited`, `internal`) are implicit on every operation and documented once at the document level.'
        current_generation:
          type: integer
          format: int64
          description: 'On `generation_conflict` (body-carried CAS) and `precondition_failed` (a failed `If-Match`): the config generation that actually won. Re-read the config at this generation, rebase your change, and retry — the write was refused, not partially applied.'
        detail:
          type: string
          description: Occurrence-specific explanation, written for a human and naming the actual failing input where one exists. **Never parse this.** It is the one member with no stability promise; its wording may change in any revision. Everything a program needs is in `code`, `status`, or a typed extension member.
        enable_hint:
          type: string
          description: 'On `feature_absent`: how to wire this feature up in THIS deployment. A 404 `feature_absent` is a permanent not-configured state, deliberately not a retried 503 — retrying will never cure it, and configuration will. This member says what to configure.'
        errors:
          type: array
          description: 'On `validation_failed`: the rejected inputs, reported together rather than one per round trip. Each entry carries an `issue`; its `pointer` is an RFC 6901 JSON pointer into the request body WHEN THE PRODUCER CAN DERIVE ONE, and an empty string when it cannot. An empty pointer is a deliberate answer, not a bug: a whole-document rejection has no field to point at, and a loader message that does not lead with a path would only yield a guess. Render an entry with no pointer against the document as a whole. The entries are also not one-per-field in general — a document-level refusal is a single entry however many fields it names.'
          items:
            $ref: '#/components/schemas/FieldError'
        legacy_kind:
          type: string
          description: TRANSITIONAL, and expected to disappear. During the epoch-1→2 error migration this preserved the pre-migration ad-hoc error kind so a client mid-migration could still recognize a failure it already handled. The ratchet that tracked it reached ZERO at MS-B, so nothing emits it today. Do not write new code against it; branch on `code`.
        lost_events:
          description: 'On `cursor_expired`: how much fell into the gap, sized by the server because the client is not able to size it — the cursor is opaque and the position it names is not a number a client is meant to read, so the subtraction that would produce this figure belongs to the side that decoded the cursor and holds the ring. It is an UPPER BOUND rather than a count, and `at_most` is named for that: the figure is the number of feed positions between your cursor and the tail, which is the exact loss for a consumer reading the whole feed and a ceiling for one reading it through a `types` or `since` filter, since the evicted rows are gone and which of them your filter would have selected is no longer recoverable. `measured: false` is a THIRD state beside a number and an absence: it means the gap cannot be sized against this feed at all — the usual cause is a cursor from a different ring incarnation, whose sequences do not address this ring — and it renders `at_most: null` rather than a zero, because a zero there would claim you missed nothing. A feed that makes no gap disclosure omits this member; read its absence as silence, not as zero.'
          anyOf:
            - $ref: '#/components/schemas/LostEventsBound'
            - type: "null"
        oldest_seq:
          type: integer
          format: int64
          description: 'On `cursor_expired`: the sequence to RESUME FROM. Everything from it onward is still available; the rows between your cursor and it are gone. Usually it is the oldest sequence still live in the bounded feed and your cursor pointed below it. When the ring is EMPTY — which is the state a rebuilt log lands in, and the one an epoch mismatch reports — there is no live oldest, and this member instead names the sequence the NEXT event will take. Treat it as a floor to resume at in both cases rather than as a claim that a row with that sequence exists. You are told this rather than being silently restarted at the tail — reconcile the gap from `POST /audit`, the underived truth, then resume from a fresh cursor. A silent restart would hide a hole you would otherwise discover much later, or never.'
        reactivate_url:
          type: string
          description: '`payment_required` / `trial_expired`: where a human goes to fix it. Omitted where the deployment has no self-serve billing surface — a link to a page that does not exist is worse than no link.'
        reasons:
          type: array
          description: 'On `promote_gate_refused`: the reasons the refusal rests on, quoted rather than paraphrased so the operator reads what was actually decided. When an evidence provider is wired — which the standard binary always does — they are the GATE''S OWN words, unsummarized. A server embedding this package without that seam gets a single server-authored line instead, saying the provider is not configured and the gate therefore cannot be satisfied; that one is the server''s sentence, not the gate''s.'
          items:
            type: string
        refusal:
          description: 'On a governance-mediated refusal: a REFERENCE to the refusal row that caused this — the request and firing limit plus the feed path to look it up. A reference, never the row itself, and never a secret; the refusal feed is the authoritative record and is where the full context lives. ★ NOTHING IN THIS BUILD POPULATES IT. The shape is declared and reserved on `forbidden`; the governance surfaces that would fill it serve their refusals through the refusal feed itself today. Handle its absence as the normal case.'
          anyOf:
            - $ref: '#/components/schemas/GovernanceRefusalRef'
            - type: "null"
        request_id:
          type: string
          description: The identifier for THIS request, identical to the `TT-Request-Id` response header every response carries. Server-minted and unguessable; quote it in a bug report and it locates the audit row and the log line for the same call. Present on every problem, including ones produced before any handler ran.
        required_role:
          type: string
          description: 'On `forbidden`: the session-ladder floor (`viewer`|`operator`|`admin`) the ROUTE requires. It is derived from the route''s floor rather than from the caller, so it is present on any non-public route''s 403 whatever kind of credential was refused — including a token refused for a missing SCOPE, which receives this member and `required_scope` together. Read it as "what this route demands", not as "what you were missing"; on a scope denial the second member is the one that names the gap.'
        required_scope:
          type: string
          description: 'On `forbidden`: the exact scope the CALLER lacks, also returned in the `TT-Required-Scope` response header. A 403 always names what was missing rather than leaving the caller to guess — mint a token carrying this scope, or ask an operator to. It is not confined to token principals: the scope fences are checked against whatever scope set the principal resolved to, so a SESSION whose role-derived scopes do not reach a privileged scope is refused with this member as well. Note the `*:read` / `*:write` wildcards never expand to a privileged scope; those must be named on the token explicitly.'
        restart_required:
          type: array
          description: 'On `restart_required`: the exact configuration paths that cannot be applied to a running process. The change was refused rather than half-applied, and this names precisely what would need a restart, so the decision belongs to the operator instead of to the server.'
          items:
            type: string
        retry_after_seconds:
          type: integer
          format: int64
          description: 'On `rate_limited` and `budget_exceeded`: how long to wait before retrying, in seconds, mirroring the `Retry-After` header. It is the only member on this API that tells you HOW LONG to wait, so an automatic backoff should key on it. It is not the only 4xx a client may retry: 409s such as `idempotency_in_flight` and `generation_conflict` invite a retry in their own descriptions and carry no wait, because the right delay there is the caller''s own settle time rather than a server-imposed one. `authority_conflict` is the counter-case: it refuses permanently until the owning configuration authority changes, so repeating the same write earns the same refusal. Read each problem type''s description before deciding.'
        status:
          type: integer
          format: int64
          description: The HTTP status code, mirrored into the body (RFC 9457 §3.1.2) so a client holding only the payload — a log line, a queued failure, a webhook delivery record — still knows what happened.
        subscription_status:
          type: string
          description: '`payment_required`: the subscription posture that refused the call — `delinquent` (payment lapsed; writes refused, reads still served) or `expired` (the paid period ended; reads refused too). Both answer 402 and they need different remedies, so switch on this and not on the prose.'
        successor:
          type: string
          description: 'On `gone`: the operation that replaced this sunset one. Were an operation retired, it would answer 410 naming its successor rather than a bare 404, so a client discovers the migration instead of concluding the path had not existed. ★ NOTHING IN THIS BUILD EMITS THIS MEMBER: no operation has been retired yet, so nothing produces a `gone` problem for a successor to hang on. That is a statement about this revision rather than a silence — the shape is declared now so a client written today already handles the first retirement, and a deprecation in flight announces itself on the operation with RFC 9745/8594 headers long before it becomes a 410.'
        title:
          type: string
          description: A short, human-readable summary, FIXED for a given `type` — it never varies between occurrences of the same problem. Safe to show in a UI; useless for branching (use `code`).
        trial_ends_at:
          type: string
          description: '`trial_expired`: the RFC 3339 instant the trial ended.'
        type:
          type: string
          description: A relative URI, `/tt/api/problems/<slug>`, identifying WHAT went wrong — stable across epochs and dereferenceable on this host, so an air-gapped deployment can read the explanation without egress. The slug tail is the stable identifier; resolve it against the served document's origin, and treat an unrecognized `type` as a server you do not understand rather than as a transient condition to retry.
        valid_through:
          type: string
          description: '`payment_required`: the RFC 3339 instant the paid period ran to. It is the fact a customer needs to know whether they are one failed charge or one forgotten renewal away from this.'
      required:
        - code
        - request_id
        - status
        - title
        - type
    ReconcileDriftScope:
      type: object
      properties:
        canonical:
          $ref: '#/components/schemas/Money'
        drift:
          $ref: '#/components/schemas/Money'
        drifted:
          type: boolean
        limit_id:
          type: string
        node:
          type: string
        orphan:
          type: boolean
        rollup:
          $ref: '#/components/schemas/Money'
        scope:
          type: string
      required:
        - canonical
        - drift
        - drifted
        - rollup
        - scope
    ReplicaCoverage:
      type: object
      properties:
        as_of:
          anyOf:
            - $ref: '#/components/schemas/IndexedThrough'
            - type: "null"
        error:
          type: string
        replica:
          type: string
        rows:
          type:
            - integer
            - "null"
          format: int64
        status:
          type: string
        transport:
          type: string
      required:
        - replica
        - status
    ReplicaGovernanceHealth:
      type: object
      properties:
        error:
          type: string
        health:
          anyOf:
            - $ref: '#/components/schemas/GovernanceHealthResponse'
            - type: "null"
        replica:
          type: string
        status:
          type: string
      required:
        - replica
        - status
    ShareScope:
      type: object
      description: 'The FROZEN scope a share token replays: exactly one of a saved-view reference, an inline query artifact, or a governance node subtree. It is immutable after mint — a bearer can never widen it.'
      properties:
        gov_node:
          type: string
        query:
          description: 'An opaque JSON value, carried verbatim. HAND-PINNED: json.RawMessage is a named []byte, so reflection would document its BACKING kind — an array of integers — rather than the JSON it holds. See the property description for what this particular value may contain. The versioned query artifact copied at mint, including resolved view content, tenant ceiling, absolute time bounds and scalar column projection. Legacy unversioned query artifacts and live-view references must be re-minted. Mutually exclusive with view_id and gov_node.'
        view_id:
          type: string
    ShareToken:
      type: object
      description: 'A share token''s REDACTED record: everything needed to manage the token and nothing that could reconstruct or confirm its secret. The store holds only the SHA-256 of the plaintext, and this shape has no field able to carry that digest.'
      properties:
        created_at:
          type: string
        created_by:
          type: string
        expired:
          type: boolean
          description: Derived against the server clock at read time, not stored. A token can be dead by revocation OR by expiry; both are rendered so an operator is never told a live-looking token is usable.
        expires_at:
          type:
            - string
            - "null"
        id:
          type: string
        revoked:
          type: boolean
        revoked_at:
          type:
            - string
            - "null"
        scope:
          $ref: '#/components/schemas/ShareScope'
      required:
        - created_at
        - expired
        - id
        - revoked
        - scope
    SharedStateDisclosure:
      type: object
      properties:
        note:
          type: string
        shared_state_enabled:
          type: boolean
        tiers:
          type: array
          items:
            type: string
      required:
        - note
        - shared_state_enabled
        - tiers
    Window:
      type: object
      properties:
        since:
          type: string
        tz:
          type: string
        until:
          type: string
      required:
        - since
        - until
  securitySchemes:
    managementToken:
      type: http
      scheme: bearer
      description: 'A tta_ scoped management token (§2.3): SHA-256-at-rest, shown exactly once at mint, rotatable with lineage, revocable immediately. Each operation''s security entry lists its required scope (also x-required-scope); the *:read / *:write wildcards expand over common scopes only — a privileged scope (config:apply, content:read, keys:mint, share:admin, tokens:mint, tokens:write, users:admin) must be named on the token. An insufficient scope is a 403 problem carrying required_scope and the TT-Required-Scope header.'
    session:
      type: apiKey
      in: cookie
      name: tt_session
      description: A dashboard session. Session principals are gated by the operation's x-role-floor (viewer|operator|admin); unsafe methods additionally require the session CSRF header. CSRF values are never issued to tta_/tts_ principals.
