openapi: "3.0.3"
info:
  title: Gnomon Node REST API
  description: |
    V4.2 locked REST API for Gnomon Tier-1 and Tier-2 nodes.

    The current public EventAnchor endpoints expose the Polaris EventAnchor runtime.
    `/v1/event-anchors` is the only public EventAnchor resource path. Historical
    `/v1/anchors*` aliases have been removed because no external SDK
    clients depend on them.

    Cluster replication and quorum signing are private control-plane endpoints.
    They require node-level mTLS and the complete Cluster-auth header set.

    All responses are wrapped in a standard envelope:
    ```json
    {
      "data": <payload or null>,
      "error": null | { "code": "...", "message": "..." },
      "request_id": "<uuid>"
    }
    ```
    On success `error` is `null` and `data` carries the payload.
    On failure `data` is `null` and `error` carries a structured error object.
  version: "4.2.0"
  contact:
    name: Gnomon Team

servers:
  - url: https://gnomonsystem.cn/api
    description: Current production public Tier-2 gateway (Node A)
  - url: http://localhost:8080
    description: Local development (Tier-2)
  - url: http://localhost:50051
    description: Local development (Tier-1)

tags:
  - name: Anchors
    description: Polaris EventAnchor endpoints exposed only under `/v1/event-anchors*`.
  - name: NodeKeys
    description: Register, list, and revoke node signing public keys used by V2.3 verification.
  - name: TrustedIssuers
    description: Configure trusted L1 issuer public keys for NodeCertificate verification.
  - name: Cluster
    description: Signed peer-to-peer cluster replication and control-plane synchronization APIs.
  - name: Quorum
    description: Private quorum lease and signing APIs. Requires node-level mTLS and complete Cluster-auth.
  - name: Agents
    description: Register, rotate keys, offboard, and manage permissions for agents.
  - name: Audit
    description: Challenge-response audit protocol for cross-node verification.
  - name: Billing
    description: Usage-based billing records and revenue distribution (billing_routes).
  - name: Blockchain
    description: Batch attestation of anchors to an external blockchain via Merkle trees.
  - name: Distribution
    description: Deliver anchors to remote nodes using various distribution modes.
  - name: Interop
    description: Import/export anchors from external timestamping platforms.
  - name: Privacy
    description: Anonymize anchors with configurable privacy levels.
  - name: Routing
    description: Node selection, registration, and anti-monopoly enforcement.
  - name: Webhooks
    description: Register, list, and unregister webhook subscriptions.
  - name: Temporal
    description: "Tier-1: Temporal index allocation, precision time, and epoch consensus."
  - name: NodeManagement
    description: "Tier-1: Node registration, heartbeats, and listing."
  - name: Operations
    description: Health checks, readiness probes, metrics, node reports, and billing summaries.

security:
  - BearerAuth: []

paths:
  # ---------------------------------------------------------------------------
  # EventAnchors
  # ---------------------------------------------------------------------------
  /v1/event-anchors:
    post:
      operationId: createEventAnchor
      tags: [Anchors]
      summary: Create a single EventAnchor
      parameters:
        - $ref: "#/components/parameters/PolarisVersionHeader"
        - $ref: "#/components/parameters/PolarisNodeIdHeader"
        - $ref: "#/components/parameters/PolarisTimestampHeader"
        - $ref: "#/components/parameters/RequestIdHeader"
        - $ref: "#/components/parameters/IdempotencyKeyHeader"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateAnchorRequest"
            example:
              origin_fingerprint: "ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00"
              prev_anchor_id: null
              event_data_hash: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
              enable_vp: true
              enable_sp: true
              client_event_id: "evt-001"
              app_id_hash: "1111111111111111111111111111111111111111111111111111111111111111"
      responses:
        "201":
          description: EventAnchor created successfully
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseAnchorResponse"
              example:
                data:
                  anchor_id: "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"
                  temporal_index: "1"
                  origin_fingerprint: "ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00"
                  sequence_token: "1"
                  chain_proof: "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
                  verification_proof: "0000000000000000000000000000000000000000000000000000000000000000"
                  semantic_proof: "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
                  digital_signature: "304402..."
                  optional_flags: 3
                  protocol_version: 2
                  crypto_suite: "Chinese"
                  salt: "aabbccdd00112233aabbccdd00112233"
                  created_at: 1716000000.123
                  issuer_node_id: "0101010101010101010101010101010101010101010101010101010101010101"
                  degraded_mode: false
                  key_version: 1
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440000"
        "400":
          description: Invalid request (bad hex, wrong length)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
              example:
                data: null
                error:
                  code: "BAD_REQUEST"
                  message: "invalid hex for event_data_hash: Invalid character"
                request_id: "550e8400-e29b-41d4-a716-446655440001"
        "409":
          description: Chain broken or replay detected
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "422":
          description: Cryptographic error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "500":
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "503":
          description: Temporal indices exhausted
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/event-anchors/latest:
    get:
      operationId: getLatestEventAnchor
      tags: [Anchors]
      summary: Retrieve the latest EventAnchor for an origin fingerprint
      description: >
        Returns the most recent local anchor for the supplied origin fingerprint.
        Clients should call this before creating a non-genesis anchor and pass
        the returned anchor_id as prev_anchor_id.
      parameters:
        - name: origin_fingerprint
          in: query
          required: true
          description: Hex-encoded 32-byte origin fingerprint
          schema:
            type: string
            pattern: "^[0-9a-fA-F]{64}$"
          example: "ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00"
      responses:
        "200":
          description: Latest EventAnchor found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseAnchorResponse"
        "400":
          description: Invalid origin_fingerprint format
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "404":
          description: No EventAnchor found for the origin fingerprint
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "500":
          description: Storage error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/event-anchors/batch:
    post:
      operationId: batchCreateEventAnchors
      tags: [Anchors]
      summary: Create multiple EventAnchors in a single batch (max 1000)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: array
              items:
                $ref: "#/components/schemas/CreateAnchorRequest"
              maxItems: 1000
            example:
              - origin_fingerprint: "ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00"
                prev_anchor_id: null
                event_data_hash: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
                enable_vp: true
                enable_sp: true
              - origin_fingerprint: "ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00"
                prev_anchor_id: "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"
                event_data_hash: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
                enable_vp: true
                enable_sp: true
      responses:
        "200":
          description: Batch result (may contain partial errors)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseBatchCreateAnchorsResponse"
              example:
                data:
                  anchors:
                    - anchor_id: "a1b2c3..."
                      temporal_index: 1
                      origin_fingerprint: "ff00ff00..."
                      sequence_token: 1
                      chain_proof: "123456..."
                      verification_proof: "000000..."
                      semantic_proof: "abcdef..."
                      digital_signature: "304402..."
                      optional_flags: 3
                      protocol_version: 2
                      crypto_suite: "Chinese"
                      salt: "aabbccdd00112233aabbccdd00112233"
                      created_at: 1700000000000000000
                      issuer_node_id: "010101..."
                      degraded_mode: false
                      key_version: 1
                  errors:
                    - index: 1
                      error_code: "BAD_REQUEST"
                      error_message: "invalid hex for event_data_hash: Invalid character"
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440002"
        "400":
          description: Batch too large (> 1000 items)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
              example:
                data: null
                error:
                  code: "BATCH_TOO_LARGE"
                  message: "batch size 1001 exceeds maximum of 1000"
                request_id: "550e8400-e29b-41d4-a716-446655440003"

  /v1/event-anchors:verify:
    post:
      operationId: verifyEventAnchor
      tags: [Anchors]
      summary: Verify an existing EventAnchor
      parameters:
        - $ref: "#/components/parameters/PolarisVersionHeader"
        - $ref: "#/components/parameters/PolarisNodeIdHeader"
        - $ref: "#/components/parameters/PolarisTimestampHeader"
        - $ref: "#/components/parameters/RequestIdHeader"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/VerifyAnchorRequest"
            example:
              anchor_id: "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"
              verify_mode: "full"
              verify_depth: 3
              event_data_hash: null
              public_key: null
              attest_result: false
              app_id_hash: "1111111111111111111111111111111111111111111111111111111111111111"
      responses:
        "200":
          description: Verification result. Business invalid/unknown results are returned with HTTP 200.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseVerifyResponse"
              example:
                data:
                  anchor_id: "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"
                  result: "valid"
                  reason: null
                  warnings: []
                  temporal_index: 42
                  verify_mode: "full"
                  verify_depth: 3
                  verified_depth: 1
                  degraded_mode: false
                  issuer_node_id: "0101010101010101010101010101010101010101010101010101010101010101"
                  key_version: 1
                  verifier_node_id: "0202020202020202020202020202020202020202020202020202020202020202"
                  verifier_signature: null
                  verified_at: 1716000000456000000
                  protocol_version: 2
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440004"
        "400":
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "500":
          description: Storage error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/event-anchors:batchVerify:
    post:
      operationId: batchVerifyEventAnchors
      tags: [Anchors]
      summary: Verify multiple EventAnchors in request order
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/BatchVerifyAnchorsRequest"
            example:
              anchor_ids:
                - "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"
              verify_mode: "full"
              app_id_hash: "1111111111111111111111111111111111111111111111111111111111111111"
      responses:
        "200":
          description: Batch verification result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseBatchVerifyAnchorsResponse"
        "400":
          description: Invalid request or batch too large
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/event-anchors/sync:
    post:
      operationId: syncEventAnchors
      tags: [Anchors]
      summary: Sync offline-generated anchors to the node (max 1000)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/OfflineSyncRequest"
            example:
              anchors:
                - anchor_id: "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"
                  event_data_hash: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
                  public_key: "0411b0c4e5c9c3d..."
                  signature: "304402..."
                  temporal_index: 100
                  prev_anchor_id: null
                  origin_fingerprint: "ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00"
                  created_at_ns: 1700000000000000100
      responses:
        "200":
          description: Sync results
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseOfflineSyncResponse"
              example:
                data:
                  total: 2
                  accepted: 1
                  rejected: 1
                  errors:
                    - "[1] origin_fingerprint does not match public_key"
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440006"
        "400":
          description: Sync batch too large (> 1000)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
              example:
                data: null
                error:
                  code: "SYNC_TOO_LARGE"
                  message: "sync batch size 1001 exceeds maximum of 1000"
                request_id: "550e8400-e29b-41d4-a716-446655440007"

  /v1/event-anchors/{anchor_id}:
    get:
      operationId: getEventAnchor
      tags: [Anchors]
      summary: Retrieve an EventAnchor by ID
      parameters:
        - name: anchor_id
          in: path
          required: true
          description: Hex-encoded 32-byte anchor ID (64 hex characters)
          schema:
            type: string
            pattern: "^[0-9a-fA-F]{64}$"
          example: "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"
      responses:
        "200":
          description: Anchor found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseAnchorResponse"
        "400":
          description: Invalid anchor_id format
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "404":
          description: Anchor not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "500":
          description: Storage error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
    delete:
      operationId: deleteEventAnchor
      tags: [Anchors]
      summary: Delete an EventAnchor (GDPR right-to-be-forgotten)
      parameters:
        - name: anchor_id
          in: path
          required: true
          description: Hex-encoded 32-byte anchor ID
          schema:
            type: string
            pattern: "^[0-9a-fA-F]{64}$"
          example: "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"
        - name: depth
          in: query
          required: false
          description: Maximum number of anchors to return, including the requested anchor. Defaults to 100 and is capped at 100.
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 100
      responses:
        "200":
          description: Anchor deleted
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseDeleteAnchorResult"
              example:
                data:
                  anchor_id: "a1b2c3d4e5f6..."
                  status: "deleted"
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440008"
        "400":
          description: Invalid anchor_id format
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "404":
          description: Anchor not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "500":
          description: Storage error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/event-anchors/{anchor_id}/trace:
    get:
      operationId: traceEventAnchor
      tags: [Anchors]
      summary: Trace the chain of an EventAnchor back to genesis (max depth 100)
      parameters:
        - name: anchor_id
          in: path
          required: true
          description: Hex-encoded 32-byte anchor ID
          schema:
            type: string
            pattern: "^[0-9a-fA-F]{64}$"
          example: "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"
      responses:
        "200":
          description: Chain trace result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseTraceResponse"
              example:
                data:
                  chain:
                    - anchor_id: "a1b2c3..."
                      temporal_index: 3
                      origin_fingerprint: "ff00ff00..."
                      sequence_token: 3
                      chain_proof: "123456..."
                      verification_proof: "111111..."
                      semantic_proof: "abcdef..."
                      digital_signature: "304402..."
                      optional_flags: 3
                      protocol_version: 2
                      crypto_suite: "Chinese"
                      salt: "aabbccdd00112233aabbccdd00112233"
                      created_at: 1700000000000000003
                      issuer_node_id: "010101..."
                      degraded_mode: false
                      key_version: 1
                    - anchor_id: "b2c3d4..."
                      temporal_index: 2
                      origin_fingerprint: "ff00ff00..."
                      sequence_token: 2
                      chain_proof: "654321..."
                      verification_proof: "222222..."
                      semantic_proof: "fedcba..."
                      digital_signature: "304402..."
                      optional_flags: 3
                      protocol_version: 2
                      crypto_suite: "Chinese"
                      salt: "aabbccdd00112233aabbccdd00112233"
                      created_at: 1700000000000000002
                      issuer_node_id: "010101..."
                      degraded_mode: false
                      key_version: 1
                  total_depth: 2
                  chain_complete: true
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440009"
        "400":
          description: Invalid anchor_id format
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "500":
          description: Storage error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/node-keys:
    post:
      operationId: registerNodeKey
      tags: [NodeKeys]
      summary: Register a node signing public key
      description: |
        Registers a public key for `origin_fingerprint + key_version` so verify can resolve
        signatures without each request supplying `public_key`. For a rotated key, provide
        `prev_origin_fingerprint`, `prev_key_version`, and `prev_signature`.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RegisterNodeKeyRequest"
            example:
              public_key: "11b0c4e5c9c3d00111b0c4e5c9c3d00111b0c4e5c9c3d00111b0c4e5c9c3d001"
              key_version: 1
              valid_from: 0
              created_at: 1716000000000
      responses:
        "201":
          description: Key registered
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseNodeKeyVersionResponse"
        "400":
          description: Bad hex or invalid public key length
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "409":
          description: Key version already exists
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "422":
          description: Origin mismatch or invalid key-version chain
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/node-keys/{origin_fingerprint}:
    get:
      operationId: listNodeKeys
      tags: [NodeKeys]
      summary: List registered key versions for an origin
      parameters:
        - name: origin_fingerprint
          in: path
          required: true
          description: Hex-encoded 32-byte origin fingerprint
          schema:
            type: string
            pattern: "^[0-9a-fA-F]{64}$"
      responses:
        "200":
          description: Registered key versions
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseNodeKeyRegistryResponse"
        "400":
          description: Invalid origin_fingerprint
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/node-keys/{origin_fingerprint}/versions/{key_version}/revoke:
    post:
      operationId: revokeNodeKey
      tags: [NodeKeys]
      summary: Revoke a node signing key version at a TemporalIndex cutoff
      parameters:
        - name: origin_fingerprint
          in: path
          required: true
          description: Hex-encoded 32-byte origin fingerprint
          schema:
            type: string
            pattern: "^[0-9a-fA-F]{64}$"
        - name: key_version
          in: path
          required: true
          schema:
            type: integer
            format: uint32
            minimum: 1
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RevokeNodeKeyRequest"
            example:
              revocation_time: 5000
              revocation_reason: 1
      responses:
        "200":
          description: Key version revoked
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseNodeKeyVersionResponse"
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "404":
          description: Origin or key version not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/trusted-issuers:
    get:
      operationId: listTrustedIssuers
      tags: [TrustedIssuers]
      summary: List trusted L1 NodeCertificate issuer public keys
      responses:
        "200":
          description: Trusted issuers
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseTrustedIssuerListResponse"
    post:
      operationId: registerTrustedIssuer
      tags: [TrustedIssuers]
      summary: Persist a trusted L1 NodeCertificate issuer public key
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RegisterTrustedIssuerRequest"
            example:
              issuer_public_key: "03af..."
              label: "tier1-root-a"
              created_at: 1716000000000
      responses:
        "201":
          description: Trusted issuer registered
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseTrustedIssuerResponse"
        "400":
          description: Invalid hex
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "422":
          description: Invalid issuer public key
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/trusted-issuers/snapshot:
    get:
      operationId: exportTrustedIssuerSnapshot
      tags: [TrustedIssuers]
      summary: Export the full trusted issuer registry, including revoked and deleted tombstones
      responses:
        "200":
          description: Trusted issuer registry snapshot
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseTrustedIssuerListResponse"

  /v1/trusted-issuers/sync:
    post:
      operationId: syncTrustedIssuers
      tags: [TrustedIssuers]
      summary: Merge a trusted issuer registry snapshot from another node
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TrustedIssuerSyncRequest"
      responses:
        "200":
          description: Snapshot merged; invalid records are reported per item
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseTrustedIssuerSyncResponse"
        "400":
          description: Invalid request envelope
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/trusted-issuers/{issuer_node_id}/revoke:
    post:
      operationId: revokeTrustedIssuer
      tags: [TrustedIssuers]
      summary: Revoke a trusted L1 issuer while preserving an auditable tombstone
      parameters:
        - name: issuer_node_id
          in: path
          required: true
          schema:
            type: string
            pattern: "^[0-9a-fA-F]{64}$"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RevokeTrustedIssuerRequest"
      responses:
        "200":
          description: Trusted issuer revoked
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseTrustedIssuerResponse"
        "404":
          description: Trusted issuer not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/trusted-issuers/{issuer_node_id}/rotate:
    post:
      operationId: rotateTrustedIssuer
      tags: [TrustedIssuers]
      summary: Register a replacement L1 root and revoke the current issuer
      parameters:
        - name: issuer_node_id
          in: path
          required: true
          schema:
            type: string
            pattern: "^[0-9a-fA-F]{64}$"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RotateTrustedIssuerRequest"
      responses:
        "200":
          description: Trusted issuer rotated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseTrustedIssuerRotationResponse"
        "404":
          description: Trusted issuer not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "409":
          description: Replacement issuer is invalid or inactive
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/trusted-issuers/{issuer_node_id}:
    delete:
      operationId: deleteTrustedIssuer
      tags: [TrustedIssuers]
      summary: Soft-delete a trusted issuer tombstone from active trust
      parameters:
        - name: issuer_node_id
          in: path
          required: true
          schema:
            type: string
            pattern: "^[0-9a-fA-F]{64}$"
      responses:
        "200":
          description: Trusted issuer soft-deleted
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseTrustedIssuerResponse"
        "404":
          description: Trusted issuer not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/cluster/anchors/digest:
    get:
      operationId: getClusterAnchorDigest
      tags: [Cluster]
      summary: Return an authenticated peer digest for a temporal anchor range
      description: |
        Cluster API v1 requires the shared Cluster token plus a timestamp-bound
        SM2 request signature from a NodeId registered in the Tier-1 peer directory.
        The signature covers the exact method, path-and-query, timestamp, and
        canonical NodeId, timestamp, nonce, and exact path-and-query. The nonce
        is replay-protected by the receiving node.
      security:
        - ClusterTokenAuth: []
          ClusterNodeIdAuth: []
          ClusterTimestampAuth: []
          ClusterNonceAuth: []
          ClusterSignatureAuth: []
      parameters:
        - name: from
          in: query
          required: true
          schema:
            type: integer
            format: uint64
            minimum: 0
        - name: to
          in: query
          required: true
          schema:
            type: integer
            format: uint64
            minimum: 0
        - name: source_node_id
          in: query
          required: false
          schema:
            type: string
            pattern: "^[0-9a-fA-F]{64}$"
      responses:
        "200":
          description: Digest for the requested temporal range
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseClusterAnchorDigest"
        "401":
          description: Missing or invalid Cluster v1 authentication factors
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/cluster/trusted-issuers/snapshot:
    get:
      operationId: exportClusterTrustedIssuerSnapshot
      tags: [Cluster]
      summary: Export trusted issuer governance state for signed peer replication
      security:
        - ClusterTokenAuth: []
          ClusterNodeIdAuth: []
          ClusterTimestampAuth: []
          ClusterNonceAuth: []
          ClusterSignatureAuth: []
      responses:
        "200":
          description: Trusted issuer governance snapshot
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseTrustedIssuerClusterSnapshot"

  /v1/cluster/trusted-issuers/sync:
    post:
      operationId: importClusterTrustedIssuerSnapshot
      tags: [Cluster]
      summary: Merge trusted issuer governance state received from a cluster peer
      security:
        - ClusterTokenAuth: []
          ClusterNodeIdAuth: []
          ClusterTimestampAuth: []
          ClusterNonceAuth: []
          ClusterSignatureAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/TrustedIssuerClusterSnapshot"
      responses:
        "200":
          description: Trusted issuer governance snapshot merged
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseTrustedIssuerClusterSyncResponse"
        "401":
          description: Missing or invalid cluster peer credentials
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  # ---------------------------------------------------------------------------
  # Quorum — private control plane
  # ---------------------------------------------------------------------------
  /v1/quorum/lease:
    get:
      operationId: getQuorumLease
      tags: [Quorum]
      summary: Read the local quorum witness state
      description: Private control-plane endpoint. The response reports the local witness only; it does not prove a complete multi-signer quorum by itself.
      security:
        - ClusterTokenAuth: []
          ClusterNodeIdAuth: []
          ClusterTimestampAuth: []
          ClusterNonceAuth: []
          ClusterSignatureAuth: []
      responses:
        "200":
          description: Local quorum witness state
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseQuorumLeaseStatus"
        "401":
          description: Missing or invalid Cluster-auth factors
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/quorum/sign:
    post:
      operationId: signQuorumTicket
      tags: [Quorum]
      summary: Sign a constrained quorum ticket
      description: Private control-plane endpoint. Only the fixed quorum ticket profile is accepted; callers must provide all Cluster-auth factors.
      security:
        - ClusterTokenAuth: []
          ClusterNodeIdAuth: []
          ClusterTimestampAuth: []
          ClusterNonceAuth: []
          ClusterSignatureAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/QuorumSignRequest"
      responses:
        "200":
          description: Quorum ticket signature
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseQuorumSignResponse"
        "401":
          description: Missing or invalid Cluster-auth factors
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "409":
          description: Replayed quorum ticket nonce
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "422":
          description: Invalid or expired quorum ticket
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  # ---------------------------------------------------------------------------
  # Agents
  # ---------------------------------------------------------------------------
  /v1/agents/register:
    post:
      operationId: registerAgent
      tags: [Agents]
      summary: Register a new agent
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RegisterAgentRequest"
            example:
              public_key: "0411b0c4e5c9c3d..."
              endpoint: "https://agent.example.com:9090"
      responses:
        "200":
          description: Agent registered
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseRegisterAgentResult"
              example:
                data:
                  agent_id: "ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00"
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440012"
        "400":
          description: Invalid public_key hex
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/agents/{id}/rotate-key:
    post:
      operationId: rotateAgentKey
      tags: [Agents]
      summary: Rotate the public key of an agent
      parameters:
        - name: id
          in: path
          required: true
          description: Hex-encoded 32-byte agent ID
          schema:
            type: string
            pattern: "^[0-9a-fA-F]{64}$"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RotateAgentKeyRequest"
            example:
              new_public_key: "0422aabb..."
      responses:
        "200":
          description: Key rotated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseKeyRotatedResult"
              example:
                data:
                  agent_id: "ff00ff00..."
                  status: "key_rotated"
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440013"
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/agents/{id}/offboard:
    post:
      operationId: offboardAgent
      tags: [Agents]
      summary: Offboard (deactivate) an agent
      parameters:
        - name: id
          in: path
          required: true
          description: Hex-encoded 32-byte agent ID
          schema:
            type: string
            pattern: "^[0-9a-fA-F]{64}$"
      responses:
        "200":
          description: Agent offboarded
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseOffboardResult"
              example:
                data:
                  agent_id: "ff00ff00..."
                  status: "offboarded"
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440014"
        "400":
          description: Invalid agent ID
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/agents/{id}/permissions:
    get:
      operationId: getAgentPermissions
      tags: [Agents]
      summary: Get permissions for an agent
      parameters:
        - name: id
          in: path
          required: true
          description: Hex-encoded 32-byte agent ID
          schema:
            type: string
            pattern: "^[0-9a-fA-F]{64}$"
      responses:
        "200":
          description: Current agent permissions
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponsePermissionsResponse"
              example:
                data:
                  agent_id: "ff00ff00..."
                  permissions: ["ReadOnly", "Generate", "Verify"]
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440015"
        "400":
          description: Invalid agent ID
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "404":
          description: Agent not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
              example:
                data: null
                error:
                  code: "AGENT_NOT_FOUND"
                  message: "agent not found"
                request_id: "550e8400-e29b-41d4-a716-446655440016"
    post:
      operationId: updateAgentPermissions
      tags: [Agents]
      summary: Grant or revoke permissions for an agent
      parameters:
        - name: id
          in: path
          required: true
          description: Hex-encoded 32-byte agent ID
          schema:
            type: string
            pattern: "^[0-9a-fA-F]{64}$"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdatePermissionsRequest"
            example:
              grant: ["Admin"]
              revoke: ["Generate"]
      responses:
        "200":
          description: Permissions updated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponsePermissionsResponse"
              example:
                data:
                  agent_id: "ff00ff00..."
                  permissions: ["ReadOnly", "Verify", "Admin"]
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440017"
        "400":
          description: Unknown permission name
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "404":
          description: Agent not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/agents:
    get:
      operationId: listAgents
      tags: [Agents]
      summary: List all active agents
      responses:
        "200":
          description: List of active agents
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseAgentList"
              example:
                data:
                  - agent_id: "ff00ff00..."
                    public_key: "0411b0c4..."
                    endpoint: "https://agent.example.com:9090"
                    registered_at: 1700000000
                    status: "Active"
                    key_version: 1
                    permissions: ["ReadOnly", "Generate", "Verify"]
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440018"

  # ---------------------------------------------------------------------------
  # Iris / Grant minimal runtime
  # ---------------------------------------------------------------------------
  /v1/claim-anchors:
    post:
      operationId: createClaimAnchor
      tags: [Iris]
      summary: Create a ClaimAnchor runtime record
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateClaimAnchorRequest"
      responses:
        "201":
          description: ClaimAnchor created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseClaimAnchorResponse"
        "400":
          description: Invalid request or signature key material
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "409":
          description: Active claim already exists for entity + attribute type
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
    get:
      operationId: getCurrentClaimAnchor
      tags: [Iris]
      summary: Query the current active ClaimAnchor for an entity and attribute type
      parameters:
        - name: entity_fingerprint
          in: query
          required: true
          schema:
            type: string
            pattern: "^[0-9a-fA-F]{64}$"
        - name: attribute_type
          in: query
          required: true
          schema:
            type: integer
            format: uint32
      responses:
        "200":
          description: Current ClaimAnchor
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseClaimAnchorResponse"
        "404":
          description: ClaimAnchor not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/claim-anchors/prepare:
    post:
      operationId: prepareClaimAnchor
      tags: [Iris]
      summary: Reserve an EEA AnchorID for client-side ClaimAnchor signatures
      description: |
        Allocates the EEA anchor without accepting any private key. The client
        signs the returned subject_signing_payload and then signs the returned
        issuer_signing_payload_template after replacing its trailing 64-byte
        zero subject signature with the real subject signature. Submit both
        signatures to POST /v1/claim-anchors with prepared_anchor_id.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PrepareClaimAnchorRequest"
      responses:
        "200":
          description: Prepared ClaimAnchor signing context
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseClaimAnchorPrepareResponse"
        "409":
          description: Existing active claim or prepare mismatch
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/claim-anchors:verify:
    post:
      operationId: verifyClaimAnchor
      tags: [Iris]
      summary: Verify a ClaimAnchor runtime record
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ClaimVerifyRequest"
      responses:
        "200":
          description: Claim verification result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseClaimVerifyResponse"
        "404":
          description: ClaimAnchor not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/claim-anchors/{anchor_id}:
    get:
      operationId: getClaimAnchor
      tags: [Iris]
      summary: Get a ClaimAnchor by anchor_id
      parameters:
        - name: anchor_id
          in: path
          required: true
          schema:
            type: string
            pattern: "^[0-9a-fA-F]{64}$"
      responses:
        "200":
          description: ClaimAnchor found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseClaimAnchorResponse"
        "404":
          description: ClaimAnchor not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/claim-anchors/{anchor_id}/revoke:
    post:
      operationId: revokeClaimAnchor
      tags: [Iris]
      summary: Revoke a ClaimAnchor and emit a revocation anchor
      parameters:
        - name: anchor_id
          in: path
          required: true
          schema:
            type: string
            pattern: "^[0-9a-fA-F]{64}$"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ClaimRevokeRequest"
      responses:
        "201":
          description: Revocation anchor created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseLayerAnchorSummary"
        "404":
          description: ClaimAnchor not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "409":
          description: ClaimAnchor already revoked
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/grant-anchors:
    post:
      operationId: createGrantAnchor
      tags: [Grant]
      summary: Create a GrantAnchor runtime record
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateGrantAnchorRequest"
      responses:
        "201":
          description: GrantAnchor created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseGrantAnchorResponse"
        "400":
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "404":
          description: Referenced ClaimAnchor or parent GrantAnchor not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "409":
          description: Active grant already exists for this relation
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
    get:
      operationId: getCurrentGrantAnchor
      tags: [Grant]
      summary: Query the current active GrantAnchor for a relation
      parameters:
        - name: grantor_fingerprint
          in: query
          required: true
          schema:
            type: string
            pattern: "^[0-9a-fA-F]{64}$"
        - name: grantee_fingerprint
          in: query
          required: true
          schema:
            type: string
            pattern: "^[0-9a-fA-F]{64}$"
      responses:
        "200":
          description: Current GrantAnchor
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseGrantAnchorResponse"
        "404":
          description: GrantAnchor not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/grant-anchors/prepare:
    post:
      operationId: prepareGrantAnchor
      tags: [Grant]
      summary: Reserve an EEA AnchorID for client-side GrantAnchor signatures
      description: |
        Allocates the EEA anchor without accepting any private key. The client
        signs grantor_signing_payload and then signs grant_signing_payload_template
        after replacing its trailing 64-byte zero grantor signature with the real
        grantor signature. Submit both signatures to POST /v1/grant-anchors with
        prepared_anchor_id.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/PrepareGrantAnchorRequest"
      responses:
        "200":
          description: Prepared GrantAnchor signing context
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseGrantAnchorPrepareResponse"
        "409":
          description: Existing active grant or prepare mismatch
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/grant-anchors:verify:
    post:
      operationId: verifyGrantAnchor
      tags: [Grant]
      summary: Verify a GrantAnchor runtime record
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/GrantVerifyRequest"
      responses:
        "200":
          description: Grant verification result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseGrantVerifyResponse"
        "404":
          description: GrantAnchor not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/grant-anchors/{anchor_id}:
    get:
      operationId: getGrantAnchor
      tags: [Grant]
      summary: Get a GrantAnchor by anchor_id
      parameters:
        - name: anchor_id
          in: path
          required: true
          schema:
            type: string
            pattern: "^[0-9a-fA-F]{64}$"
      responses:
        "200":
          description: GrantAnchor found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseGrantAnchorResponse"
        "404":
          description: GrantAnchor not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/grant-anchors/{anchor_id}/revoke:
    post:
      operationId: revokeGrantAnchor
      tags: [Grant]
      summary: Revoke a GrantAnchor and emit a revocation anchor
      parameters:
        - name: anchor_id
          in: path
          required: true
          schema:
            type: string
            pattern: "^[0-9a-fA-F]{64}$"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/GrantRevokeRequest"
      responses:
        "201":
          description: Grant revocation anchor created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseLayerAnchorSummary"
        "404":
          description: GrantAnchor not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "409":
          description: GrantAnchor already revoked
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  # ---------------------------------------------------------------------------
  # Audit
  # ---------------------------------------------------------------------------
  /v1/audit/challenge:
    post:
      operationId: auditChallenge
      tags: [Audit]
      summary: Generate an audit challenge for a given anchor
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AuditChallengeRequest"
            example:
              anchor_id: "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"
              challenger_node_id: "0101010101010101010101010101010101010101010101010101010101010101"
      responses:
        "200":
          description: Challenge generated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseAuditChallengeResponse"
              example:
                data:
                  challenge_id: "aabbccddaabbccddaabbccddaabbccddaabbccddaabbccddaabbccddaabbccdd"
                  anchor_id: "a1b2c3d4..."
                  challenger_node_id: "01010101..."
                  timestamp_ns: 1700000000000000000
                  nonce: "deadbeef..."
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440019"
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/audit/respond:
    post:
      operationId: auditRespond
      tags: [Audit]
      summary: Respond to an audit challenge
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AuditRespondRequest"
            example:
              challenge_id: "aabbccddaabbccddaabbccddaabbccddaabbccddaabbccddaabbccddaabbccdd"
              anchor_id: "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"
      responses:
        "200":
          description: Audit response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseAuditRespondResponse"
              example:
                data:
                  challenge_id: "aabbccdd..."
                  anchor_found: true
                  anchor_valid: true
                  responder_node_id: "01010101..."
                  response_time_ns: 150000
                  signature: "cafebabe..."
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440020"
        "400":
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "500":
          description: Storage error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/audit/results:
    get:
      operationId: auditResults
      tags: [Audit]
      summary: List recent audit results (last 100)
      responses:
        "200":
          description: List of recent audit results
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseAuditResultList"
              example:
                data:
                  - challenge_id: "aabbccdd..."
                    passed: true
                    response_time_ns: 150000
                    failure_reason: null
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440021"

  # ---------------------------------------------------------------------------
  # Webhooks
  # ---------------------------------------------------------------------------
  /v1/webhooks:
    post:
      operationId: registerWebhook
      tags: [Webhooks]
      summary: Register a webhook subscription
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RegisterWebhookRequest"
            example:
              url: "https://example.com/webhook"
              secret: "my-shared-secret"
              events: ["anchor_generated", "agent_registered"]
      responses:
        "200":
          description: Webhook registered
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseWebhookRegistered"
              example:
                data:
                  id: 1
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440022"
    get:
      operationId: listWebhooks
      tags: [Webhooks]
      summary: List all registered webhooks
      responses:
        "200":
          description: List of webhooks
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseWebhookList"
              example:
                data:
                  - id: 1
                    url: "https://example.com/webhook"
                    secret_set: true
                    events: ["anchor_generated", "agent_registered"]
                  - id: 2
                    url: "https://other.example.com/hook"
                    secret_set: false
                    events: null
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440023"

  /v1/webhooks/{id}:
    delete:
      operationId: unregisterWebhook
      tags: [Webhooks]
      summary: Unregister a webhook
      parameters:
        - name: id
          in: path
          required: true
          description: Numeric webhook ID
          schema:
            type: integer
            format: uint64
          example: 2
      responses:
        "200":
          description: Webhook removed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseWebhookRemoved"
              example:
                data:
                  id: 1
                  status: "removed"
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440024"
        "400":
          description: Invalid webhook ID
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "404":
          description: Webhook not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
              example:
                data: null
                error:
                  code: "WEBHOOK_NOT_FOUND"
                  message: "webhook not found"
                request_id: "550e8400-e29b-41d4-a716-446655440025"

  # ---------------------------------------------------------------------------
  # Privacy
  # ---------------------------------------------------------------------------
  /v1/privacy/anonymize:
    post:
      operationId: anonymizeAnchor
      tags: [Privacy]
      summary: Anonymize an anchor with a configurable privacy level (1-5)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AnonymizeRequest"
            example:
              anchor_id: "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"
              privacy_level: 3
      responses:
        "200":
          description: Anonymized anchor
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseAnonymizeResponse"
              example:
                data:
                  anon_anchor_id: "deadbeef..."
                  obscured_temporal: "aabb1122..."
                  pseudonym_hash: "ccddee00..."
                  encrypted_proof: "ff001122..."
                  privacy_level: 3
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440026"
        "400":
          description: Invalid input (bad hex or privacy_level out of range)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
              example:
                data: null
                error:
                  code: "BAD_REQUEST"
                  message: "privacy_level must be between 1 and 5"
                request_id: "550e8400-e29b-41d4-a716-446655440027"
        "404":
          description: Anchor not found
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "500":
          description: Storage error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  # ---------------------------------------------------------------------------
  # Blockchain
  # ---------------------------------------------------------------------------
  /v1/blockchain/attest:
    post:
      operationId: attestAnchor
      tags: [Blockchain]
      summary: Queue an anchor for batch blockchain attestation
      description: >
        Adds an anchor to the attestation batch. When the batch reaches capacity,
        a Merkle root is automatically submitted to the blockchain. The response
        indicates whether the batch was flushed (merkle_root and tx_hash non-null)
        or the anchor was merely queued.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AttestRequest"
            example:
              anchor_id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      responses:
        "200":
          description: Anchor queued or batch submitted
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseAttestResponse"
              example:
                data:
                  queued: true
                  merkle_root: null
                  tx_hash: null
                  pending_count: 1
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440028"
        "400":
          description: Invalid anchor_id hex
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "500":
          description: Attestation failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/blockchain/flush:
    post:
      operationId: flushBatch
      tags: [Blockchain]
      summary: Manually flush the pending attestation batch to blockchain
      responses:
        "200":
          description: Batch flushed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseFlushResponse"
              example:
                data:
                  merkle_root: "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
                  tx_hash: "0xdeadbeef..."
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440029"
        "400":
          description: No pending anchors
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
              example:
                data: null
                error:
                  code: "EMPTY_BATCH"
                  message: "no pending anchors to flush"
                request_id: "550e8400-e29b-41d4-a716-446655440030"
        "500":
          description: Flush failed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/blockchain/pending:
    get:
      operationId: pendingCount
      tags: [Blockchain]
      summary: Get the number of pending anchors in the attestation batch
      responses:
        "200":
          description: Pending count
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponsePendingCount"
              example:
                data:
                  pending_count: 5
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440031"

  /v1/blockchain/merkle-root:
    post:
      operationId: computeMerkleRoot
      tags: [Blockchain]
      summary: Compute a Merkle root from a list of anchor IDs
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/MerkleRootRequest"
            example:
              anchor_ids:
                - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
                - "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
                - "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
      responses:
        "200":
          description: Merkle root computed
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseMerkleRootResponse"
              example:
                data:
                  root: "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
                  leaf_count: 3
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440032"
        "400":
          description: Empty anchor_ids list or invalid hex
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  # ---------------------------------------------------------------------------
  # Interop
  # ---------------------------------------------------------------------------
  /v1/interop/platforms:
    get:
      operationId: listPlatforms
      tags: [Interop]
      summary: List registered interop platforms
      responses:
        "200":
          description: Platform list
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponsePlatformList"
              example:
                data:
                  platforms: ["rfc3161", "ethereum"]
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440033"

  /v1/interop/import:
    post:
      operationId: importAnchor
      tags: [Interop]
      summary: Import an anchor from an external platform
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ImportAnchorRequest"
            example:
              platform_id: "rfc3161"
              external_id: "ext-001"
              data: "5500550055005500550055005500550055005500550055005500550055005500"
              metadata:
                source: "external-tsa"
      responses:
        "200":
          description: Anchor imported
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseImportAnchorResponse"
              example:
                data:
                  anchor_id: "aabb1122..."
                  temporal_index: 42
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440034"
        "400":
          description: Invalid hex data
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "422":
          description: Import failed (unknown platform, conversion error)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
              example:
                data: null
                error:
                  code: "IMPORT_FAILED"
                  message: "unsupported platform: unknown-platform"
                request_id: "550e8400-e29b-41d4-a716-446655440035"

  /v1/interop/export:
    post:
      operationId: exportAnchor
      tags: [Interop]
      summary: Export an anchor to an external platform format
      description: >
        Currently returns 501 Not Implemented. Requires integration with Tier2State
        for anchor storage access.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ExportAnchorRequest"
            example:
              anchor_id: "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"
              platform_id: "rfc3161"
      responses:
        "501":
          description: Not yet implemented
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
              example:
                data: null
                error:
                  code: "NOT_IMPLEMENTED"
                  message: "export requires anchor storage access -- integrate with Tier2State"
                request_id: "550e8400-e29b-41d4-a716-446655440036"

  # ---------------------------------------------------------------------------
  # Routing
  # ---------------------------------------------------------------------------
  /v1/routing/select:
    post:
      operationId: selectNode
      tags: [Routing]
      summary: Select a node using a routing strategy (auto/manual/priority)
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/SelectNodeRequest"
            example:
              strategy: "auto"
              weight_quality: 1.0
              weight_cost: 1.0
              weight_latency: 1.0
      responses:
        "200":
          description: Selected node (null if no nodes match)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseSelectedNode"
              example:
                data:
                  node_id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440037"
        "400":
          description: Unknown strategy or missing required field
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/routing/nodes:
    get:
      operationId: listRoutingNodes
      tags: [Routing]
      summary: List all nodes in the routing table
      responses:
        "200":
          description: Node list
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseRoutingNodeList"
              example:
                data:
                  nodes:
                    - node_id: "aaaa..."
                      healthy: true
                      cost_per_anchor: 10
                      quality_score: 1.0
                      traffic_share: 0.25
                      location: "us-east-1"
                      capabilities: ["finance", "high-throughput"]
                  count: 1
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440038"
    post:
      operationId: registerRoutingNode
      tags: [Routing]
      summary: Register a node in the routing table
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RoutingRegisterNodeRequest"
            example:
              node_id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
              cost_per_anchor: 10
              location: "us-east-1"
              capabilities: ["finance", "high-throughput"]
      responses:
        "200":
          description: Node registered
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseRegistered"
              example:
                data:
                  registered: true
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440039"
        "400":
          description: Invalid node_id hex
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/routing/anti-monopoly:
    post:
      operationId: checkAntiMonopoly
      tags: [Routing]
      summary: Run anti-monopoly check and demote over-share nodes
      responses:
        "200":
          description: Anti-monopoly check result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseAntiMonopolyResult"
              example:
                data:
                  demoted_nodes: ["aaaa...", "bbbb..."]
                  count: 2
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440040"

  # ---------------------------------------------------------------------------
  # Distribution
  # ---------------------------------------------------------------------------
  /v1/distribution/deliver:
    post:
      operationId: deliverAnchor
      tags: [Distribution]
      summary: Deliver an anchor to remote nodes
      description: >
        Supports three distribution modes: `point_to_point` (single target),
        `batch` (multiple targets), and `cascade` (chain of forwarding nodes).
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DeliverRequest"
            example:
              anchor_id: "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
              mode: "point_to_point"
              target: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
      responses:
        "200":
          description: Delivery result
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseDeliverResponse"
              example:
                data:
                  anchor_id: "cccccc..."
                  delivered: ["bbbbbb..."]
                  failed: []
                  mode: "point_to_point"
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440041"
        "400":
          description: Invalid input or unknown mode
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/distribution/log:
    get:
      operationId: deliveryLog
      tags: [Distribution]
      summary: Get the delivery log of all distributed anchors
      responses:
        "200":
          description: Delivery log
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseDeliveryLog"
              example:
                data:
                  deliveries:
                    - anchor_id: "cccccc..."
                      delivered_to: ["bbbbbb...", "dddddd..."]
                  count: 1
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440042"

  # ---------------------------------------------------------------------------
  # Billing (billing_routes)
  # ---------------------------------------------------------------------------
  /v1/billing/record:
    post:
      operationId: recordUsage
      tags: [Billing]
      summary: Record a billing usage event
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RecordUsageRequest"
            example:
              anchor_id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
              tier: "finance"
              node_id: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
              user_id: "user-1"
      responses:
        "200":
          description: Usage recorded
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseRecordUsageResponse"
              example:
                data:
                  anchor_id: "aaaaaa..."
                  tier: "finance"
                  cost: 200
                  timestamp: 1700000000
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440043"
        "400":
          description: Invalid tier or bad hex
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
              example:
                data: null
                error:
                  code: "BAD_REQUEST"
                  message: "tier must be general/industry/finance"
                request_id: "550e8400-e29b-41d4-a716-446655440044"

  /v1/billing/records:
    get:
      operationId: listBillingRecords
      tags: [Billing]
      summary: List all billing records
      responses:
        "200":
          description: Billing records
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseBillingRecordList"
              example:
                data:
                  records:
                    - anchor_id: "aaaaaa..."
                      tier: "Finance"
                      node_id: "bbbbbb..."
                      user_id: "user-1"
                      cost: 200
                      quality_met: true
                      timestamp: 1700000000
                  count: 1
                  total_cost: 200
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440045"

  /v1/billing/revenue:
    post:
      operationId: calculateRevenue
      tags: [Billing]
      summary: Calculate revenue distribution from current billing records
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RevenueRequest"
            example:
              tier1_share: 0.30
              tier2_share: 0.40
              platform_reserve: 0.20
              anti_monopoly_fund: 0.10
      responses:
        "200":
          description: Revenue distribution
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseRevenueResult"
              example:
                data:
                  revenue:
                    tier1: 63
                    tier2: 84
                    platform: 42
                    anti_monopoly: 21
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440046"
        "422":
          description: Invalid distribution (shares do not sum to 1.0)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
              example:
                data: null
                error:
                  code: "BILLING_ERROR"
                  message: "shares must sum to 1.0"
                request_id: "550e8400-e29b-41d4-a716-446655440047"

  # ---------------------------------------------------------------------------
  # Temporal (Tier-1)
  # ---------------------------------------------------------------------------
  /v1/temporal/allocate:
    post:
      operationId: allocateBatch
      tags: [Temporal]
      summary: "Tier-1: Allocate a batch of temporal indices for a node"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/AllocateBatchRequest"
            example:
              node_id: "aabbccdd"
              batch_size: 100
      responses:
        "200":
          description: Batch allocated
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseAllocateBatchResponse"
              example:
                data:
                  start_index: 0
                  count: 100
                  ttl_seconds: 3600
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440048"
        "400":
          description: Invalid node_id hex or batch_size out of range (1-100000)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
              example:
                data: null
                error:
                  code: "BAD_REQUEST"
                  message: "batch_size must be between 1 and 100000"
                request_id: "550e8400-e29b-41d4-a716-446655440049"

  /v1/temporal/precision-time:
    get:
      operationId: precisionTime
      tags: [Temporal]
      summary: "Tier-1: Get high-precision current time from the node"
      responses:
        "200":
          description: Precision time
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponsePrecisionTimeResponse"
              example:
                data:
                  timestamp_ns: 1700000000000000000
                  confidence_interval_ns: 1000000
                  clock_source: "system_clock"
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440050"

  /v1/keypkg/sync:
    post:
      operationId: syncKeyPackage
      tags: [Temporal]
      summary: "Tier-1: Issue a signed KeyPackage and bound ChildTHK for a registered Tier-2 node"
      description: "Internal Tier-1 endpoint. It is used by Tier-2 nodes or L1 clients to fetch a signed KeyPackage together with the ChildTHK it binds to. It is not exposed through the public Tier-2 Node A gateway."
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/KeyPackageSyncRequest"
            example:
              node_id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
              current_key_version: 3
              requested_ti_span: "1000000"
      responses:
        "200":
          description: Signed KeyPackage and ChildTHK issued
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseKeyPackageSyncResponse"
              example:
                data:
                  key_package:
                    t_epoch: "1000"
                    valid_until: "4600"
                    grace_period_seconds: 300
                    issuer_node_id: "4a9c000000000000000000000000000000000000000000000000000000000000"
                    root_public_key_hash: "7b9c000000000000000000000000000000000000000000000000000000000000"
                    key_version: 4
                    issued_at: "1716000000000"
                    child_thk_commitment: "9f9c000000000000000000000000000000000000000000000000000000000000"
                    target_node_fp: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
                    package_signature: "304402..."
                  child_thk:
                    parent_root_thk_id: "1f1f000000000000000000000000000000000000000000000000000000000000"
                    t_utc: "1716000000000000000"
                    ti_range_start: "1000"
                    ti_range_end: "4600"
                    sub_sequence: 1
                    target_node_fp: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
                    nonce: "00112233445566778899aabbccddeeff"
                    child_commitment: "9f9c000000000000000000000000000000000000000000000000000000000000"
                    child_thk_signature: "304402..."
                  root_thk:
                    time_commitment: "1f1f000000000000000000000000000000000000000000000000000000000000"
                    t_epoch: "1000"
                    t_utc: "1716000000000000000"
                    time_window: "3600000000"
                    valid_until: "3600001000"
                    grace_period: 300
                    issuer_node_id: "4a9c000000000000000000000000000000000000000000000000000000000000"
                    key_version: 1
                    prev_commitment: "0000000000000000000000000000000000000000000000000000000000000000"
                    signature: "304402..."
                  op_certificate:
                    op_cert_id: "ab12000000000000000000000000000000000000000000000000000000000000"
                    issuer_root_fp: "4a9c000000000000000000000000000000000000000000000000000000000000"
                    op_pubkey: "03af..."
                    valid_from: "1000"
                    valid_until: "3600001000"
                    allowed_ops: 1
                    key_version_op: 1
                    root_sig: "304402..."
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440050"
        "400":
          description: Invalid node_id or request shape
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/child-thk/request:
    post:
      operationId: requestChildTHK
      tags: [Temporal]
      summary: "Tier-1: Issue a ChildTHK for a registered Tier-2 node"
      description: "Internal Tier-1 endpoint. It is used when a Tier-2 node needs a fresh TI allocation segment before wrapping it into a KeyPackage."
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ChildTHKRequest"
            example:
              node_id: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
              requested_ti_span: "1000000"
      responses:
        "200":
          description: ChildTHK issued
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseChildTHKResponse"
              example:
                data:
                  child_thk:
                    parent_root_thk_id: "1f1f000000000000000000000000000000000000000000000000000000000000"
                    t_utc: "1716000000000000000"
                    ti_range_start: "1000"
                    ti_range_end: "4600"
                    sub_sequence: 1
                    target_node_fp: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
                    nonce: "00112233445566778899aabbccddeeff"
                    child_commitment: "9f9c000000000000000000000000000000000000000000000000000000000000"
                    child_thk_signature: "304402..."
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440051"
        "400":
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "404":
          description: Node is not registered with Tier-1
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "422":
          description: ChildTHK could not be issued
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/prime/thk/current:
    get:
      operationId: getCurrentRootTHK
      tags: [Temporal]
      summary: "Tier-1: Get the current RootTHK and active OpCertificate"
      description: "Internal Tier-1 endpoint used for control-plane inspection and future L2 root-chain synchronization."
      responses:
        "200":
          description: Current RootTHK and OpCertificate returned
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseCurrentRootTHKResponse"
              example:
                data:
                  root_thk:
                    time_commitment: "1f1f000000000000000000000000000000000000000000000000000000000000"
                    t_epoch: "1000"
                    t_utc: "1716000000000000000"
                    time_window: "3600000000"
                    valid_until: "3600001000"
                    grace_period: 300
                    issuer_node_id: "4a9c000000000000000000000000000000000000000000000000000000000000"
                    key_version: 1
                    prev_commitment: "0000000000000000000000000000000000000000000000000000000000000000"
                    signature: "304402..."
                  op_certificate:
                    op_cert_id: "ab12000000000000000000000000000000000000000000000000000000000000"
                    issuer_root_fp: "4a9c000000000000000000000000000000000000000000000000000000000000"
                    op_pubkey: "03af..."
                    valid_from: "1000"
                    valid_until: "3600001000"
                    allowed_ops: 1
                    key_version_op: 1
                    root_sig: "304402..."
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440052"
        "404":
          description: Node is not registered with Tier-1
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "422":
          description: KeyPackage could not be issued
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/prime/thk:
    get:
      operationId: getRootTHKRange
      tags: [Temporal]
      summary: "Tier-1: Query RootTHK history and overlapping OpCertificates by TI range"
      description: "Internal Tier-1 endpoint used for minimal traceable root-chain history lookup. Returns RootTHK and OpCertificate entries overlapping the requested TI range."
      parameters:
        - in: query
          name: from_ti
          required: false
          schema:
            type: integer
            format: uint64
          description: Inclusive lower TI bound. Defaults to 0.
        - in: query
          name: to_ti
          required: false
          schema:
            type: integer
            format: uint64
          description: Exclusive upper TI bound. Defaults to uint64 max.
      responses:
        "200":
          description: Matching RootTHK and OpCertificate entries returned
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseRootTHKRangeResponse"
              example:
                data:
                  from_ti: "0"
                  to_ti: "3600001001"
                  root_thks:
                    - time_commitment: "1f1f000000000000000000000000000000000000000000000000000000000000"
                      t_epoch: "1000"
                      t_utc: "1716000000000000000"
                      time_window: "3600000000"
                      valid_until: "3600001000"
                      grace_period: 300
                      issuer_node_id: "4a9c000000000000000000000000000000000000000000000000000000000000"
                      key_version: 1
                      prev_commitment: "0000000000000000000000000000000000000000000000000000000000000000"
                      signature: "304402..."
                  op_certificates:
                    - op_cert_id: "ab12000000000000000000000000000000000000000000000000000000000000"
                      issuer_root_fp: "4a9c000000000000000000000000000000000000000000000000000000000000"
                      op_pubkey: "03af..."
                      valid_from: "1000"
                      valid_until: "3600001000"
                      allowed_ops: 1
                      key_version_op: 1
                      root_sig: "304402..."
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440053"
        "400":
          description: Invalid TI range
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/temporal/epoch:
    get:
      operationId: getEpoch
      tags: [Temporal]
      summary: "Tier-1: Get the current epoch and its confirmation status"
      responses:
        "200":
          description: Current epoch
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseGetEpochResponse"
              example:
                data:
                  current_epoch: 5
                  is_confirmed: true
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440051"

  /v1/temporal/epoch/propose:
    post:
      operationId: proposeEpoch
      tags: [Temporal]
      summary: "Tier-1: Propose advancing to a new epoch"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ProposeEpochRequest"
            example:
              new_epoch: 6
              node_id: "0101010101010101010101010101010101010101010101010101010101010101"
      responses:
        "200":
          description: Proposal accepted
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseProposeEpochResponse"
              example:
                data:
                  accepted: true
                  current_epoch: 6
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440052"
        "400":
          description: Invalid node_id hex or length
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
        "409":
          description: Epoch proposal rejected (e.g., lower than current)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
              example:
                data: null
                error:
                  code: "EPOCH_PROPOSAL_REJECTED"
                  message: "proposed epoch 0 is not greater than current epoch 1"
                request_id: "550e8400-e29b-41d4-a716-446655440053"

  /v1/temporal/epoch/vote:
    post:
      operationId: voteEpoch
      tags: [Temporal]
      summary: "Tier-1: Vote on a pending epoch proposal"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/VoteEpochRequest"
            example:
              epoch: 6
              voter_id: "0202020202020202020202020202020202020202020202020202020202020202"
      responses:
        "200":
          description: Vote result (pending, confirmed, or rejected)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseVoteEpochResponse"
              example:
                data:
                  result: "confirmed"
                  epoch: 6
                  reason: null
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440054"
        "400":
          description: Invalid voter_id hex or length
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  # ---------------------------------------------------------------------------
  # Node Management (Tier-1)
  # ---------------------------------------------------------------------------
  /v1/nodes:
    get:
      operationId: listNodes
      tags: [NodeManagement]
      summary: "Tier-1: List all registered nodes"
      responses:
        "200":
          description: Registered node list
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseNodeInfoList"
              example:
                data:
                  - node_id: "aabb"
                    public_key: "1122"
                    endpoint: "http://node-a:8080"
                    registered_at: 1700000000
                    last_heartbeat: 1700000100
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440055"

  /v1/nodes/register:
    post:
      operationId: registerNode
      tags: [NodeManagement]
      summary: "Tier-1: Register a new node"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/RegisterNodeRequest"
            example:
              node_id: "e8a2bb38a12369ce230e1c0ad15e185f82492b5045fb05c572c72b53d5bd9f12"
              public_key: "af566366fcfc5b08c2c625df6369e6e30aa6a8169c061157f0cbdf868054e065ae91047a949cb8104d9b20901301bad510f2f21d286505589fe0834d373b191d"
              endpoint: "https://gnomonsystem.cn/api"
              node_tier: 2
              operator_id: "1111111111111111111111111111111111111111111111111111111111111111"
              region_code: "CN01"
              valid_from: 0
              valid_until: 31536000000000000
              key_version: 1
      responses:
        "200":
          description: Node registered
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseRegisterNodeResponse"
              example:
                data:
                  accepted: true
                  node_cert: "7b226e6f64655f66696e6765727072696e74223a5b..."
                  node_fingerprint: "c1b7..."
                  issuer_node_id: "4a9c..."
                  issuer_public_key: "03af..."
                  valid_from: "0"
                  valid_until: "31536000000000000"
                  key_version: 1
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440056"
        "400":
          description: Invalid hex
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  /v1/nodes/heartbeat:
    post:
      operationId: heartbeat
      tags: [NodeManagement]
      summary: "Tier-1: Send a heartbeat from a node"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/HeartbeatRequest"
            example:
              node_id: "aabbccdd"
      responses:
        "200":
          description: Heartbeat acknowledged
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseHeartbeatResponse"
              example:
                data:
                  acknowledged: true
                  server_time_ns: 1700000000000000000
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440057"
        "400":
          description: Invalid node_id hex
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"

  # ---------------------------------------------------------------------------
  # Operations (Tier-1 & Tier-2 shared)
  # ---------------------------------------------------------------------------
  /healthz:
    get:
      operationId: healthz
      tags: [Operations]
      summary: Health check
      security: []
      responses:
        "200":
          description: Node is healthy
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseHealthz"
              example:
                data:
                  status: "healthy"
                  service: "tier2-node"
                  node_id: "0101010101010101010101010101010101010101010101010101010101010101"
                  origin_fingerprint: "aabbccddeeff00112233445566778899aabbccddeeff00112233445566778899"
                  crypto_suite: "Chinese"
                  pool_remaining: 9500
                  prime_materials_installed: true
                  root_thk_sync_cursor: 1710000000000
                  root_thk_last_sync_attempt_at: 1700000010
                  root_thk_last_sync_at: 1700000010
                  root_thk_last_synced_key_version: 7
                  root_thk_last_sync_error: null
                  prime_root_thk_sync:
                    status: "ok"
                    sync_cursor: 1710000000000
                    last_attempt_at: 1700000010
                    last_success_at: 1700000010
                    last_synced_key_version: 7
                    current_root_key_version: 7
                    last_error: null
                    root_thk_history_count: 2
                    op_certificate_history_count: 2
                  timestamp: 1700000000
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440058"

  /readyz:
    get:
      operationId: readyz
      tags: [Operations]
      summary: Readiness probe
      security: []
      responses:
        "200":
          description: Node is ready
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseReadyz"
              example:
                data:
                  ready: true
                  service: "tier2-node"
                  pool_remaining: 9500
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440059"
        "503":
          description: Node is not ready
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
              example:
                data: null
                error:
                  code: "NOT_READY"
                  message: "service is not ready"
                request_id: "550e8400-e29b-41d4-a716-446655440060"

  /metrics:
    get:
      operationId: metrics
      tags: [Operations]
      summary: Prometheus metrics endpoint
      security: []
      responses:
        "200":
          description: Prometheus text format metrics
          content:
            text/plain:
              schema:
                type: string

  /v1/reports/node:
    get:
      operationId: nodeReport
      tags: [Operations]
      summary: Get a point-in-time node operation report
      responses:
        "200":
          description: Node report
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseNodeReport"
              example:
                data:
                  node_id: "0101010101010101010101010101010101010101010101010101010101010101"
                  uptime_seconds: 86400
                  anchors_generated: 1500
                  anchors_verified: 300
                  anchors_failed: 2
                  storage_size: 1500
                  pool_remaining: 8500
                  active_agents: 3
                  active_origins: 12
                  degradation_level: 0
                  report_timestamp: 1700000000
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440061"

  /v1/billing:
    get:
      operationId: billingPeriod
      tags: [Operations]
      summary: Get a billing summary for a time period (Tier-2 only)
      parameters:
        - name: start
          in: query
          required: true
          description: Period start (unix epoch seconds)
          schema:
            type: integer
            format: uint64
          example: 1700000000
        - name: end
          in: query
          required: true
          description: Period end (unix epoch seconds)
          schema:
            type: integer
            format: uint64
          example: 1700086400
      responses:
        "200":
          description: Billing summary
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseBillingRecord"
              example:
                data:
                  period_start: 1700000000
                  period_end: 1700086400
                  total_operations: 1800
                  generate_count: 1500
                  verify_count: 300
                  storage_bytes_used: 10485760
                error: null
                request_id: "550e8400-e29b-41d4-a716-446655440062"
        "400":
          description: Invalid range (start > end)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ApiResponseError"
              example:
                data: null
                error:
                  code: "BAD_REQUEST"
                  message: "start must be <= end"
                request_id: "550e8400-e29b-41d4-a716-446655440063"

# =============================================================================
# Components
# =============================================================================
components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: Bearer token authentication
    ClusterTokenAuth:
      type: apiKey
      in: header
      name: x-cluster-token
      description: Shared Cluster authorization token. Never include it in a URL or log.
    ClusterNodeIdAuth:
      type: apiKey
      in: header
      name: x-cluster-node-id
      description: Lowercase NodeId registered in the Tier-1 peer directory.
    ClusterTimestampAuth:
      type: apiKey
      in: header
      name: x-cluster-timestamp
      description: Decimal Unix seconds, signed as part of the Cluster v1 payload.
    ClusterNonceAuth:
      type: apiKey
      in: header
      name: x-cluster-nonce
      description: Caller-generated, replay-protected Cluster request nonce.
    ClusterSignatureAuth:
      type: apiKey
      in: header
      name: x-cluster-signature
      description: Lowercase hexadecimal SM2 signature over the canonical Cluster v1 payload.

  parameters:
    PolarisVersionHeader:
      name: X-Polaris-Version
      in: header
      required: true
      description: Polaris API version. Current production value is 2.3.
      schema:
        type: string
        enum: ["2.3"]
      example: "2.3"
    PolarisNodeIdHeader:
      name: X-Polaris-Node-ID
      in: header
      required: true
      description: Hex-encoded 32-byte Polaris origin fingerprint. For create, this must match request body origin_fingerprint and the node signing key.
      schema:
        type: string
        pattern: "^[0-9a-fA-F]{64}$"
      example: "ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00"
    PolarisTimestampHeader:
      name: X-Polaris-Timestamp
      in: header
      required: true
      description: Client timestamp as an integer string for the Polaris API. SDKs send nanoseconds since Unix epoch.
      schema:
        type: string
        pattern: "^[0-9]+$"
      example: "1716000000000000000"
    RequestIdHeader:
      name: X-Request-ID
      in: header
      required: true
      description: Caller-generated request correlation ID.
      schema:
        type: string
      example: "550e8400-e29b-41d4-a716-446655440000"
    IdempotencyKeyHeader:
      name: Idempotency-Key
      in: header
      required: true
      description: Required for anchor creation. Reusing a key with the same body returns the same anchor; reusing it with a different body returns 409.
      schema:
        type: string
      example: "evt-001"

  schemas:
    # -------------------------------------------------------------------------
    # Envelope and Error
    # -------------------------------------------------------------------------
    ApiError:
      type: object
      required: [code, message]
      properties:
        code:
          type: string
          description: Machine-readable error code
          example: "BAD_REQUEST"
        message:
          type: string
          description: Human-readable error message
          example: "invalid anchor_id hex"

    ApiResponseError:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          nullable: true
          example: null
        error:
          $ref: "#/components/schemas/ApiError"
        request_id:
          type: string
          format: uuid
          example: "550e8400-e29b-41d4-a716-446655440000"

    QuorumLeaseStatus:
      type: object
      required: [enabled, environment, mode, status, required_signers, local_signers, mint_allowed]
      properties:
        enabled:
          type: boolean
        environment:
          type: string
          enum: [disabled, test-only, prod]
        role:
          type: string
          nullable: true
        mode:
          type: string
          enum: [disabled, observe, enforce]
        status:
          type: string
        required_signers:
          type: integer
          minimum: 1
        local_signers:
          type: integer
          minimum: 0
        mint_allowed:
          type: boolean
        signer_id:
          type: string
          nullable: true
          pattern: "^[0-9a-f]{64}$"
        qsk_pub:
          type: string
          nullable: true
          pattern: "^[0-9a-f]{128}$"
        key_version:
          type: string
          nullable: true
        owner_node_id:
          type: string
          nullable: true
          pattern: "^[0-9a-f]{64}$"
        anchor_scope_start:
          type: integer
          format: uint64
          nullable: true
        anchor_scope_end_exclusive:
          type: integer
          format: uint64
          nullable: true
        lease_ttl_secs:
          type: integer
          format: uint64
          nullable: true
        last_lease_at:
          type: integer
          format: uint64
          nullable: true

    QuorumSignRequest:
      type: object
      additionalProperties: false
      required: [ticket_id, op, target, params_hash, issued_at, expires_at, nonce, scope_start, scope_end_exclusive]
      properties:
        ticket_id:
          type: string
          maxLength: 256
        op:
          type: string
          enum: [lease.renew, node.restart, runtime.tune, key.rotate, ti_range.change, window.watermark.change, peer.membership.change, worm.switch]
        target:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        params_hash:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        issued_at:
          type: integer
          format: uint64
        expires_at:
          type: integer
          format: uint64
        nonce:
          type: string
          maxLength: 256
        scope_start:
          type: integer
          format: uint64
        scope_end_exclusive:
          type: integer
          format: uint64

    QuorumSignResponse:
      type: object
      required: [ticket_id, signer_id, qsk_pub, key_version, environment, signature_profile, canonical_hex, sm3_digest, signature, expires_at]
      properties:
        ticket_id:
          type: string
        signer_id:
          type: string
          pattern: "^[0-9a-f]{64}$"
        qsk_pub:
          type: string
          pattern: "^[0-9a-f]{128}$"
        key_version:
          type: string
        environment:
          type: string
        signature_profile:
          type: string
        canonical_hex:
          type: string
        sm3_digest:
          type: string
          pattern: "^[0-9a-f]{64}$"
        signature:
          type: string
          pattern: "^[0-9a-f]{128}$"
        expires_at:
          type: integer
          format: uint64

    ApiResponseQuorumLeaseStatus:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/QuorumLeaseStatus"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseQuorumSignResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/QuorumSignResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ClusterAnchorDigest:
      type: object
      required: [from_temporal, to_temporal, anchor_count, merkle_root, last_temporal]
      properties:
        from_temporal:
          type: integer
          format: uint64
        to_temporal:
          type: integer
          format: uint64
        anchor_count:
          type: integer
          format: uint64
        merkle_root:
          type: string
          pattern: "^[0-9a-f]{64}$"
        last_temporal:
          type: integer
          format: uint64
          nullable: true

    ApiResponseClusterAnchorDigest:
      type: object
      required: [data, error]
      properties:
        data:
          $ref: "#/components/schemas/ClusterAnchorDigest"
        error:
          nullable: true
          example: null

    # -------------------------------------------------------------------------
    # Anchor schemas
    # -------------------------------------------------------------------------
    AnchorResponse:
      type: object
      required:
        - anchor_id
        - temporal_index
        - origin_fingerprint
        - sequence_token
        - chain_proof
        - extensions
        - digital_signature
        - optional_flags
        - protocol_version
        - crypto_suite
        - salt
        - created_at
        - issuer_node_id
        - degraded_mode
        - key_version
      properties:
        anchor_id:
          type: string
          description: "Hex-encoded 32-byte anchor identifier: H(\"Polaris.Anchor.v1\" || BE64(temporal_index) || origin_fingerprint || BE64(sequence_token) || salt16)"
          example: "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2"
        temporal_index:
          type: string
          description: Monotonically increasing temporal index
          example: "42"
        origin_fingerprint:
          type: string
          description: Hex-encoded 32-byte origin fingerprint
          example: "ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00"
        sequence_token:
          type: string
          description: Node-local monotonic sequence token, starting at 1
          example: "1"
        chain_proof:
          type: string
          description: Hex-encoded 32-byte chain proof
        verification_proof:
          type: string
          nullable: true
          description: "Optional hex-encoded 32-byte verification proof. Genesis anchors use 0x00*32; non-genesis anchors use H(prev_anchor_id)."
        semantic_proof:
          type: string
          nullable: true
          description: "Optional hex-encoded 32-byte P2 semantic proof. compatibility mode uses H(anchor_id || event_data_hash); structured mode uses H(eea_anchor_id || iris_ref || scroll_ref || grant_ref || bond_ref || yield_ref || event_data_hash), a canonical 224-byte input."
        semantic_proof_mode:
          type: string
          nullable: true
          enum: [compatibility, structured]
          description: Null when semantic_proof is absent; compatibility for legacy event-data binding, structured for full SemanticAnchorInput binding.
        semantic_anchor:
          nullable: true
          allOf:
            - $ref: "#/components/schemas/SemanticAnchorInput"
        extensions:
          type: array
          items:
            $ref: "#/components/schemas/AnchorExtensionEnvelope"
          description: "P2 extension envelopes preserved in canonical order. Unknown non-critical extensions are preserved; unknown critical extensions are rejected."
        digital_signature:
          type: string
          description: Hex-encoded signature bytes
        optional_flags:
          type: integer
          format: uint32
          description: "V2.3 bitset: bit0=VerificationProof, bit1=SemanticProof, bit2=ExtensionHeader. bit2 is set when extensions is non-empty and the extension canonical bytes are covered by the anchor signature payload."
        protocol_version:
          type: integer
          format: uint32
          example: 2
        crypto_suite:
          type: string
          description: "Fixed production crypto suite identifier: Chinese (SM2 signature + SM3 hash)."
          enum: [Chinese]
          example: "Chinese"
        salt:
          type: string
          description: Hex-encoded 16-byte random salt
        created_at:
          type: number
          format: double
          description: Creation timestamp in Unix seconds
          example: 1716000000.123
        issuer_node_id:
          type: string
          description: Hex-encoded 32-byte issuer node identifier
        degraded_mode:
          type: boolean
          description: Whether the anchor was created in degraded mode
          example: false
        key_version:
          type: integer
          format: uint32
          example: 1

    SemanticAnchorRefsRequest:
      type: object
      properties:
        iris_ref:
          type: string
          nullable: true
          description: Hex-encoded 32-byte Iris reference; omitted means 0x00*32.
        scroll_ref:
          type: string
          nullable: true
          description: Hex-encoded 32-byte Scroll reference; omitted means 0x00*32.
        grant_ref:
          type: string
          nullable: true
          description: Hex-encoded 32-byte Grant reference; omitted means 0x00*32.
        bond_ref:
          type: string
          nullable: true
          description: Hex-encoded 32-byte Bond reference; omitted means 0x00*32.
        yield_ref:
          type: string
          nullable: true
          description: Hex-encoded 32-byte Yield reference; omitted means 0x00*32.
        event_data_hash:
          type: string
          nullable: true
          description: Hex-encoded 32-byte event data hash; may be omitted when top-level event_data_hash is present.

    SemanticAnchorInput:
      type: object
      required: [eea_anchor_id, iris_ref, scroll_ref, grant_ref, bond_ref, yield_ref, event_data_hash]
      properties:
        eea_anchor_id:
          type: string
          description: Hex-encoded 32-byte Polaris EventAnchor ID.
        iris_ref:
          type: string
          description: Hex-encoded 32-byte Iris reference.
        scroll_ref:
          type: string
          description: Hex-encoded 32-byte Scroll reference.
        grant_ref:
          type: string
          description: Hex-encoded 32-byte Grant reference.
        bond_ref:
          type: string
          description: Hex-encoded 32-byte Bond reference.
        yield_ref:
          type: string
          description: Hex-encoded 32-byte Yield reference.
        event_data_hash:
          type: string
          description: Hex-encoded 32-byte event data hash.

    AnchorExtensionEnvelope:
      type: object
      required: [extension_type, version, critical_flag, payload]
      properties:
        extension_type:
          type: integer
          format: uint32
          description: "Registered types include Iris=0x0102, Scroll=0x0401, Grant=0x0501, Bond=0x0601, Yield=0x0701."
        version:
          type: integer
          format: uint32
          minimum: 0
          maximum: 255
        critical_flag:
          type: boolean
          description: Unknown critical extension types are rejected; unknown non-critical types are preserved.
        payload:
          type: string
          description: Hex-encoded extension payload. Canonical bytes are type(2B BE) || version(1B) || critical(1B) || length(2B BE) || payload.

    LayerAnchorSummary:
      type: object
      required: [anchor_id, temporal_index, origin_fingerprint, key_version, degraded_mode, created_at]
      properties:
        anchor_id:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        temporal_index:
          type: string
        origin_fingerprint:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        key_version:
          type: integer
          format: uint32
        degraded_mode:
          type: boolean
        created_at:
          type: integer
          format: uint64

    CreateClaimAnchorRequest:
      type: object
      required:
        - origin_fingerprint
        - entity_fingerprint
        - entity_type_prefix
        - attribute_type
        - attribute_hash
        - subject_public_key
        - issuer_public_key
        - subject_signature
        - issuer_signature
      properties:
        origin_fingerprint:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        prev_anchor_id:
          type: string
          nullable: true
          pattern: "^[0-9a-fA-F]{64}$"
        entity_fingerprint:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        entity_type_prefix:
          type: integer
          format: uint8
          enum: [1, 2, 3, 5, 32]
          description: V4.1 EntityPrefix discriminator. Unknown prefixes are rejected.
        entity_fingerprint_preimage:
          type: string
          nullable: true
          pattern: "^[0-9a-fA-F]+$"
          description: Optional transient raw preimage. When present, SM3(preimage) must equal entity_fingerprint and its first byte must equal entity_type_prefix.
        attribute_type:
          type: integer
          format: uint32
        attribute_hash:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        valid_until:
          type: integer
          format: uint64
          nullable: true
        previous_claim_anchor:
          type: string
          nullable: true
          pattern: "^[0-9a-fA-F]{64}$"
        subject_public_key:
          type: string
          description: Hex-encoded 64-byte raw SM2 public key (`x || y`).
          pattern: "^[0-9a-fA-F]{128}$"
        issuer_public_key:
          type: string
          description: Hex-encoded 64-byte raw SM2 public key (`x || y`).
          pattern: "^[0-9a-fA-F]{128}$"
        subject_signature:
          type: string
          description: Hex-encoded 64-byte SM2 signature from subject private key over SubjectSigningPayload.
          pattern: "^[0-9a-fA-F]{128}$"
        issuer_signature:
          type: string
          description: Hex-encoded 64-byte SM2 signature from issuer private key over IssuerSigningPayload.
          pattern: "^[0-9a-fA-F]{128}$"
        prepared_anchor_id:
          type: string
          nullable: true
          pattern: "^[0-9a-fA-F]{64}$"
          description: AnchorID returned by POST /v1/claim-anchors/prepare.

    PrepareClaimAnchorRequest:
      type: object
      required:
        - origin_fingerprint
        - entity_fingerprint
        - entity_type_prefix
        - attribute_type
        - attribute_hash
        - subject_public_key
        - issuer_public_key
      properties:
        origin_fingerprint:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        prev_anchor_id:
          type: string
          nullable: true
          pattern: "^[0-9a-fA-F]{64}$"
        entity_fingerprint:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        entity_type_prefix:
          type: integer
          format: uint8
          enum: [1, 2, 3, 5, 32]
          description: V4.1 EntityPrefix discriminator. Unknown prefixes are rejected.
        entity_fingerprint_preimage:
          type: string
          nullable: true
          pattern: "^[0-9a-fA-F]+$"
          description: Optional transient raw preimage verified against entity_fingerprint.
        attribute_type:
          type: integer
          format: uint32
        attribute_hash:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        valid_until:
          type: integer
          format: uint64
          nullable: true
        previous_claim_anchor:
          type: string
          nullable: true
          pattern: "^[0-9a-fA-F]{64}$"
        subject_public_key:
          type: string
          pattern: "^[0-9a-fA-F]{128}$"
        issuer_public_key:
          type: string
          pattern: "^[0-9a-fA-F]{128}$"

    ClaimAnchorPrepareResponse:
      type: object
      required: [prepared_anchor_id, anchor, entity_fingerprint, entity_type_prefix, attribute_type, attribute_hash, valid_until, issuer_fingerprint, previous_claim_anchor, subject_public_key, issuer_public_key, subject_signing_payload, issuer_signing_payload_template, expires_in_seconds]
      properties:
        prepared_anchor_id:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        anchor:
          $ref: "#/components/schemas/LayerAnchorSummary"
        entity_fingerprint:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        entity_type_prefix:
          type: integer
          format: uint8
          enum: [1, 2, 3, 5, 32]
        attribute_type:
          type: integer
          format: uint32
        attribute_hash:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        valid_until:
          type: integer
          format: uint64
        issuer_fingerprint:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        previous_claim_anchor:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        subject_public_key:
          type: string
          pattern: "^[0-9a-fA-F]{128}$"
        issuer_public_key:
          type: string
          pattern: "^[0-9a-fA-F]{128}$"
        subject_signing_payload:
          type: string
          pattern: "^[0-9a-fA-F]{378}$"
        issuer_signing_payload_template:
          type: string
          pattern: "^[0-9a-fA-F]{506}$"
        expires_in_seconds:
          type: integer
          format: uint64

    ClaimAnchorResponse:
      type: object
      required:
        - anchor
        - entity_fingerprint
        - attribute_type
        - attribute_hash
        - valid_until
        - previous_claim_anchor
        - subject_public_key
        - issuer_public_key
        - revoked
      properties:
        anchor:
          $ref: "#/components/schemas/LayerAnchorSummary"
        entity_fingerprint:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        attribute_type:
          type: integer
          format: uint32
        attribute_hash:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        valid_until:
          type: integer
          format: uint64
        previous_claim_anchor:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        subject_public_key:
          type: string
          pattern: "^[0-9a-fA-F]{128}$"
        issuer_public_key:
          type: string
          pattern: "^[0-9a-fA-F]{128}$"
        revoked:
          type: boolean

    ClaimVerifyRequest:
      type: object
      required: [anchor_id]
      properties:
        anchor_id:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        check_ti:
          type: integer
          format: uint64
          nullable: true

    ClaimVerifyResponse:
      type: object
      required:
        - anchor_id
        - check_ti
        - event_anchor_valid
        - subject_signature_valid
        - iris_signature_valid
        - issuer_fingerprint_valid
        - valid_until_ok
        - revoked
        - result
        - reason
      properties:
        anchor_id:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        check_ti:
          type: integer
          format: uint64
        event_anchor_valid:
          type: boolean
        subject_signature_valid:
          type: boolean
        iris_signature_valid:
          type: boolean
        issuer_fingerprint_valid:
          type: boolean
        valid_until_ok:
          type: boolean
        revoked:
          type: boolean
        revoked_at_ti:
          type: integer
          format: uint64
          nullable: true
        error_code:
          type: string
          nullable: true
        warnings:
          type: array
          items:
            type: string
        result:
          type: string
          enum: [valid, invalid]
        reason:
          type: string

    ClaimRevokeRequest:
      type: object
      properties:
        revocation_reason:
          type: integer
          format: uint32
          minimum: 0
          maximum: 255

    CreateGrantAnchorRequest:
      type: object
      required:
        - origin_fingerprint
        - grantor_claim_ref
        - grantee_claim_ref
        - scope_hash
        - delegation_depth
        - grantor_public_key
        - witness_public_key
        - grantor_signature
        - witness_signature
      properties:
        origin_fingerprint:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        prev_anchor_id:
          type: string
          nullable: true
          pattern: "^[0-9a-fA-F]{64}$"
        grantor_claim_ref:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        grantee_claim_ref:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        scope_hash:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        valid_until:
          type: integer
          format: uint64
          nullable: true
        delegation_depth:
          type: integer
          format: uint32
          minimum: 0
          maximum: 255
        delegation_flags:
          type: integer
          format: uint32
          minimum: 0
          maximum: 255
        previous_grant_anchor:
          type: string
          nullable: true
          pattern: "^[0-9a-fA-F]{64}$"
        parent_grant_anchor:
          type: string
          nullable: true
          pattern: "^[0-9a-fA-F]{64}$"
        grantor_public_key:
          type: string
          description: Hex-encoded 64-byte raw SM2 public key (`x || y`).
          pattern: "^[0-9a-fA-F]{128}$"
        witness_public_key:
          type: string
          description: Hex-encoded 64-byte raw SM2 public key (`x || y`).
          pattern: "^[0-9a-fA-F]{128}$"
        grantor_signature:
          type: string
          description: Hex-encoded 64-byte SM2 signature from grantor private key over GrantorSigningPayload.
          pattern: "^[0-9a-fA-F]{128}$"
        witness_signature:
          type: string
          description: Hex-encoded 64-byte SM2 signature from witness private key over GrantSigningPayload.
          pattern: "^[0-9a-fA-F]{128}$"
        prepared_anchor_id:
          type: string
          nullable: true
          pattern: "^[0-9a-fA-F]{64}$"
          description: AnchorID returned by POST /v1/grant-anchors/prepare.

    PrepareGrantAnchorRequest:
      type: object
      required: [origin_fingerprint, grantor_claim_ref, grantee_claim_ref, scope_hash, delegation_depth, grantor_public_key, witness_public_key]
      properties:
        origin_fingerprint:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        prev_anchor_id:
          type: string
          nullable: true
          pattern: "^[0-9a-fA-F]{64}$"
        grantor_claim_ref:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        grantee_claim_ref:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        scope_hash:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        valid_until:
          type: integer
          format: uint64
          nullable: true
        delegation_depth:
          type: integer
          format: uint32
          minimum: 0
          maximum: 255
        delegation_flags:
          type: integer
          format: uint32
          minimum: 0
          maximum: 255
        previous_grant_anchor:
          type: string
          nullable: true
          pattern: "^[0-9a-fA-F]{64}$"
        parent_grant_anchor:
          type: string
          nullable: true
          pattern: "^[0-9a-fA-F]{64}$"
        grantor_public_key:
          type: string
          pattern: "^[0-9a-fA-F]{128}$"
        witness_public_key:
          type: string
          pattern: "^[0-9a-fA-F]{128}$"

    GrantAnchorPrepareResponse:
      type: object
      required: [prepared_anchor_id, anchor, grantor_fingerprint, grantee_fingerprint, grantor_claim_ref, grantee_claim_ref, scope_hash, valid_until, delegation_depth, delegation_flags, previous_grant_anchor, grantor_public_key, witness_public_key, grantor_signing_payload, grant_signing_payload_template, expires_in_seconds]
      properties:
        prepared_anchor_id:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        anchor:
          $ref: "#/components/schemas/LayerAnchorSummary"
        grantor_fingerprint:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        grantee_fingerprint:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        grantor_claim_ref:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        grantee_claim_ref:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        scope_hash:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        valid_until:
          type: integer
          format: uint64
        delegation_depth:
          type: integer
          format: uint32
        delegation_flags:
          type: integer
          format: uint32
        previous_grant_anchor:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        parent_grant_anchor:
          type: string
          nullable: true
        grantor_public_key:
          type: string
          pattern: "^[0-9a-fA-F]{128}$"
        witness_public_key:
          type: string
          pattern: "^[0-9a-fA-F]{128}$"
        grantor_signing_payload:
          type: string
          pattern: "^[0-9a-fA-F]{506}$"
        grant_signing_payload_template:
          type: string
          pattern: "^[0-9a-fA-F]{634}$"
        expires_in_seconds:
          type: integer
          format: uint64

    GrantAnchorResponse:
      type: object
      required:
        - anchor
        - grantor_fingerprint
        - grantee_fingerprint
        - grantor_claim_ref
        - grantee_claim_ref
        - scope_hash
        - valid_until
        - delegation_depth
        - delegation_flags
        - previous_grant_anchor
      properties:
        anchor:
          $ref: "#/components/schemas/LayerAnchorSummary"
        grantor_fingerprint:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        grantee_fingerprint:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        grantor_claim_ref:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        grantee_claim_ref:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        scope_hash:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        valid_until:
          type: integer
          format: uint64
        delegation_depth:
          type: integer
          format: uint32
        delegation_flags:
          type: integer
          format: uint32
        previous_grant_anchor:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        parent_grant_anchor:
          type: string
          nullable: true

    GrantVerifyRequest:
      type: object
      required: [anchor_id]
      properties:
        anchor_id:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        check_ti:
          type: integer
          format: uint64
          nullable: true
        operation:
          type: object
          nullable: true
          additionalProperties: true
        scope_def:
          type: object
          nullable: true
          additionalProperties: true

    GrantVerifyResponse:
      type: object
      required:
        - anchor_id
        - check_ti
        - event_anchor_valid
        - grantor_signature_valid
        - grant_signature_valid
        - valid_until_ok
        - delegation_chain_valid
        - revoked
        - current_grantor_claim_valid
        - current_grantee_claim_valid
        - grantor_claim_valid_at_anchor_ti
        - grantee_claim_valid_at_anchor_ti
        - result
        - reason
      properties:
        anchor_id:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        check_ti:
          type: integer
          format: uint64
        event_anchor_valid:
          type: boolean
        grantor_signature_valid:
          type: boolean
        grant_signature_valid:
          type: boolean
        valid_until_ok:
          type: boolean
        delegation_chain_valid:
          type: boolean
        revoked:
          type: boolean
        revoked_at_ti:
          type: integer
          format: uint64
          nullable: true
        error_code:
          type: string
          nullable: true
        warnings:
          type: array
          items:
            type: string
        current_grantor_claim_valid:
          type: boolean
        current_grantee_claim_valid:
          type: boolean
        grantor_claim_valid_at_anchor_ti:
          type: boolean
        grantee_claim_valid_at_anchor_ti:
          type: boolean
        result:
          type: string
          enum: [valid, invalid]
        reason:
          type: string

    GrantRevokeRequest:
      type: object
      properties:
        revocation_reason:
          type: integer
          format: uint32
          minimum: 0
          maximum: 255

    CreateAnchorRequest:
      type: object
      required: [origin_fingerprint]
      properties:
        origin_fingerprint:
          type: string
          description: Hex-encoded 32-byte origin fingerprint; must match the X-Polaris-Node-ID header and the node signing key.
        prev_anchor_id:
          type: string
          nullable: true
          description: Hex-encoded previous anchor ID; null only for genesis
        event_data_hash:
          type: string
          nullable: true
          description: Hex-encoded 32-byte event data hash; null means empty-content event
          example: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
        semantic_anchor:
          nullable: true
          allOf:
            - $ref: "#/components/schemas/SemanticAnchorRefsRequest"
          description: Structured SemanticAnchor refs. When present, event_data_hash is required either here or at the request top level; the server fills eea_anchor_id after AnchorID allocation.
        extensions:
          type: array
          default: []
          items:
            $ref: "#/components/schemas/AnchorExtensionEnvelope"
          description: Extension envelopes to sign, store, return, and replicate. Unknown critical extension_type is rejected; unknown non-critical extension_type is preserved.
        enable_vp:
          type: boolean
          default: false
        enable_sp:
          type: boolean
          default: false
        client_event_id:
          type: string
          nullable: true
        app_id_hash:
          type: string
          nullable: true

    VerifyAnchorRequest:
      type: object
      required: [anchor_id]
      properties:
        anchor_id:
          type: string
          description: Hex-encoded 32-byte anchor ID to verify
        verify_mode:
          type: string
          enum: [fast, full]
          default: full
        verify_depth:
          type: integer
          enum: [1, 2, 3]
          nullable: true
          description: "Verification strength: 1=minimal signature+AnchorID, 2=standard plus VP/ChainProof, 3=full plus SemanticProof/event_data_hash and local chain window."
        event_data_hash:
          type: string
          nullable: true
          description: Hex-encoded 32-byte event data hash; required to verify compatibility SemanticProof. Structured SemanticAnchor proofs can be verified from stored semantic_anchor, or from semantic_anchor supplied in this request.
        semantic_anchor:
          nullable: true
          allOf:
            - $ref: "#/components/schemas/SemanticAnchorRefsRequest"
          description: Optional structured SemanticAnchor refs. If supplied for a structured anchor, they must match the stored SemanticAnchorInput.
        public_key:
          type: string
          nullable: true
          description: Optional hex-encoded 64-byte raw SM2 public key override (`x || y`).
          pattern: "^[0-9a-fA-F]{128}$"
        attest_result:
          type: boolean
          default: false
        app_id_hash:
          type: string
          nullable: true
          description: Hex-encoded 32-byte application identity hash

    VerifyResponse:
      type: object
      required: [anchor_id, result, warnings, verify_depth, verified_depth, degraded_mode, key_version, verifier_node_id, verified_at]
      properties:
        anchor_id:
          type: string
        result:
          type: string
          enum: [valid, invalid, unknown]
        reason:
          type: string
          nullable: true
        warnings:
          type: array
          items:
            type: string
        temporal_index:
          type: integer
          format: uint64
          nullable: true
        verify_mode:
          type: string
          enum: [fast, full]
        verify_depth:
          type: integer
          enum: [1, 2, 3]
        verified_depth:
          type: integer
          minimum: 0
          description: Number of local chain anchors inspected by this verification result.
        degraded_mode:
          type: boolean
        issuer_node_id:
          type: string
          nullable: true
        key_version:
          type: integer
          format: uint32
        verifier_node_id:
          type: string
        verifier_signature:
          type: string
          nullable: true
        verified_at:
          type: integer
          format: uint64
        protocol_version:
          type: integer
          format: uint32

    BatchVerifyAnchorsRequest:
      type: object
      required: [anchor_ids]
      properties:
        anchor_ids:
          type: array
          maxItems: 1000
          items:
            type: string
        verify_mode:
          type: string
          enum: [fast, full]
          default: full
        verify_depth:
          type: integer
          enum: [1, 2, 3]
          nullable: true
          description: Verification strength applied to every item in the batch.
        app_id_hash:
          type: string
          nullable: true

    BatchVerifyAnchorsResponse:
      type: object
      required: [results]
      properties:
        results:
          type: array
          items:
            $ref: "#/components/schemas/VerifyResponse"

    RegisterNodeKeyRequest:
      type: object
      required: [public_key]
      properties:
        origin_fingerprint:
          type: string
          nullable: true
          description: Optional hex-encoded 32-byte origin. If omitted, computed as hash(public_key).
          pattern: "^[0-9a-fA-F]{64}$"
        public_key:
          type: string
          description: Hex-encoded 64-byte raw SM2 signing public key (`x || y`, no PEM/DER/SEC1 prefix).
          pattern: "^[0-9a-fA-F]{128}$"
        key_version:
          type: integer
          format: uint32
          default: 1
          minimum: 1
        prev_origin_fingerprint:
          type: string
          nullable: true
          description: Previous key origin when rotating to a new origin fingerprint.
          pattern: "^[0-9a-fA-F]{64}$"
        prev_key_version:
          type: integer
          format: uint32
          nullable: true
          description: Previous key version that signed this rotation.
        prev_signature:
          type: string
          nullable: true
          description: Hex signature by previous private key over hash(new_public_key).
        valid_from:
          type: integer
          format: uint64
          default: 0
          description: TemporalIndex from which the key version is valid.
        valid_until:
          type: integer
          format: uint64
          nullable: true
          description: Exclusive TemporalIndex upper bound. Null means no local expiry.
        created_at:
          type: integer
          format: uint64
          nullable: true
          description: Unix milliseconds.
        node_certificate:
          type: string
          nullable: true
          description: Hex-encoded L1-issued NodeCertificate. When present, the registry validates it against public_key, origin_fingerprint, key_version, and validity bounds.
        issuer_public_key:
          type: string
          nullable: true
          description: Hex-encoded 64-byte raw SM2 L1 issuer public key used to verify node_certificate. It must already be registered in /v1/trusted-issuers.
          pattern: "^[0-9a-fA-F]{128}$"

    RevokeNodeKeyRequest:
      type: object
      required: [revocation_time, revocation_reason]
      properties:
        revocation_time:
          type: integer
          format: uint64
          description: TemporalIndex cutoff. Anchors at or after this TI are invalid.
        revocation_reason:
          type: integer
          format: uint32
          minimum: 1
          maximum: 255
          description: "1=compromise, 2=expiry, 3=node exit, 4=policy upgrade"

    NodeKeyVersionResponse:
      type: object
      required:
        - origin_fingerprint
        - key_version
        - public_key
        - valid_from
        - created_at
        - revoked
      properties:
        origin_fingerprint:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        key_version:
          type: integer
          format: uint32
        public_key:
          type: string
          description: Hex-encoded 64-byte raw SM2 signing public key (`x || y`).
          pattern: "^[0-9a-fA-F]{128}$"
        prev_origin_fingerprint:
          type: string
          nullable: true
          pattern: "^[0-9a-fA-F]{64}$"
        prev_key_version:
          type: integer
          format: uint32
          nullable: true
        prev_signature:
          type: string
          nullable: true
        valid_from:
          type: string
          description: TemporalIndex as decimal string.
        valid_until:
          type: string
          nullable: true
          description: Exclusive TemporalIndex upper bound as decimal string.
        created_at:
          type: integer
          format: uint64
        revoked:
          type: boolean
        revocation_time:
          type: string
          nullable: true
          description: Revocation cutoff TemporalIndex as decimal string.
        revocation_reason:
          type: integer
          format: uint32
          nullable: true
        node_certificate:
          type: string
          nullable: true
          description: Hex-encoded L1-issued NodeCertificate, when this key version was registered with one.
        issuer_public_key:
          type: string
          nullable: true
          description: Hex-encoded 64-byte raw SM2 L1 issuer public key associated with node_certificate.
          pattern: "^[0-9a-fA-F]{128}$"

    NodeKeyRegistryResponse:
      type: object
      required: [origin_fingerprint, versions]
      properties:
        origin_fingerprint:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        versions:
          type: array
          items:
            $ref: "#/components/schemas/NodeKeyVersionResponse"

    RegisterTrustedIssuerRequest:
      type: object
      required: [issuer_public_key]
      properties:
        issuer_public_key:
          type: string
          description: Hex-encoded 64-byte raw SM2 L1 issuer public key. The issuer_node_id is computed as SM3(issuer_public_key).
          pattern: "^[0-9a-fA-F]{128}$"
        label:
          type: string
          nullable: true
          description: Optional operator-readable label for this trusted issuer.
        created_at:
          type: integer
          format: uint64
          nullable: true
          description: Unix milliseconds. Defaults to server time.
        allowed_ops:
          type: array
          nullable: true
          items:
            type: integer
            format: uint16
          description: V4.1 Issuer ACL. Values are permitted AttributeType/operation identifiers; 65535 is the legacy unrestricted wildcard.

    RevokeTrustedIssuerRequest:
      type: object
      required: [revocation_reason]
      properties:
        revoked_at:
          type: integer
          format: uint64
          nullable: true
          description: Unix milliseconds. Defaults to server time.
        revocation_reason:
          type: integer
          format: uint8
          description: Compact protocol reason code for the revocation.
        replacement_issuer_node_id:
          type: string
          nullable: true
          pattern: "^[0-9a-fA-F]{64}$"
          description: Optional active replacement issuer NodeId.

    RotateTrustedIssuerRequest:
      type: object
      required: [replacement_issuer_public_key]
      properties:
        replacement_issuer_public_key:
          type: string
          description: Hex-encoded 64-byte raw SM2 replacement L1 issuer public key.
          pattern: "^[0-9a-fA-F]{128}$"
        label:
          type: string
          nullable: true
        created_at:
          type: integer
          format: uint64
          nullable: true
          description: Unix milliseconds for replacement issuer creation. Defaults to server time.
        revoked_at:
          type: integer
          format: uint64
          nullable: true
          description: Unix milliseconds for old issuer revocation. Defaults to created_at.
        revocation_reason:
          type: integer
          format: uint8
          nullable: true
          description: Compact protocol reason code. Defaults to 1.
        allowed_ops:
          type: array
          nullable: true
          items:
            type: integer
            format: uint16
          description: V4.1 Issuer ACL for the replacement issuer.

    TrustedIssuerSyncItem:
      type: object
      required: [issuer_node_id, issuer_public_key, created_at]
      properties:
        issuer_node_id:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        issuer_public_key:
          type: string
          description: Hex-encoded 64-byte raw SM2 L1 issuer public key.
          pattern: "^[0-9a-fA-F]{128}$"
        label:
          type: string
          nullable: true
        created_at:
          type: integer
          format: uint64
        updated_at:
          type: integer
          format: uint64
          nullable: true
          description: Last governance update time. Defaults to max(created_at, revoked_at, deleted_at).
        revoked_at:
          type: integer
          format: uint64
          nullable: true
        revocation_reason:
          type: integer
          format: uint8
          nullable: true
        deleted_at:
          type: integer
          format: uint64
          nullable: true
        replacement_issuer_node_id:
          type: string
          nullable: true
          pattern: "^[0-9a-fA-F]{64}$"
        allowed_ops:
          type: array
          nullable: true
          items:
            type: integer
            format: uint16
          description: V4.1 Issuer ACL. Missing legacy values are treated as unrestricted.

    TrustedIssuerSyncRequest:
      type: object
      required: [issuers]
      properties:
        issuers:
          type: array
          items:
            $ref: "#/components/schemas/TrustedIssuerSyncItem"

    TrustedIssuerClusterSnapshot:
      type: object
      required: [issuers]
      properties:
        issuers:
          type: array
          description: Complete trusted issuer governance snapshot, including active entries and tombstones.
          items:
            $ref: "#/components/schemas/TrustedIssuerSyncItem"

    TrustedIssuerClusterSyncResponse:
      type: object
      required: [total, imported, skipped, rejected, conflicts, errors]
      properties:
        total:
          type: integer
        imported:
          type: integer
          description: Records that changed local state.
        skipped:
          type: integer
          description: Records already present or older than local state.
        rejected:
          type: integer
          description: Records rejected by validation or persistence.
        conflicts:
          type: integer
          description: Stale peer records that were not allowed to overwrite newer local governance state.
        errors:
          type: array
          items:
            type: string

    TrustedIssuerResponse:
      type: object
      required: [issuer_node_id, issuer_public_key, status, created_at, updated_at, allowed_ops]
      properties:
        issuer_node_id:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
          description: Hex-encoded NodeId derived from issuer_public_key.
        issuer_public_key:
          type: string
          description: Hex-encoded 64-byte raw SM2 L1 issuer public key.
          pattern: "^[0-9a-fA-F]{128}$"
        label:
          type: string
          nullable: true
        status:
          type: string
          enum: [active, revoked, deleted]
        created_at:
          type: integer
          format: uint64
        updated_at:
          type: integer
          format: uint64
        revoked_at:
          type: integer
          format: uint64
          nullable: true
        revocation_reason:
          type: integer
          format: uint8
          nullable: true
        deleted_at:
          type: integer
          format: uint64
          nullable: true
        replacement_issuer_node_id:
          type: string
          nullable: true
          pattern: "^[0-9a-fA-F]{64}$"
        allowed_ops:
          type: array
          items:
            type: integer
            format: uint16

    TrustedIssuerListResponse:
      type: object
      required: [issuers]
      properties:
        issuers:
          type: array
          items:
            $ref: "#/components/schemas/TrustedIssuerResponse"

    TrustedIssuerRotationResponse:
      type: object
      required: [revoked_issuer, replacement_issuer]
      properties:
        revoked_issuer:
          $ref: "#/components/schemas/TrustedIssuerResponse"
        replacement_issuer:
          $ref: "#/components/schemas/TrustedIssuerResponse"

    TrustedIssuerSyncError:
      type: object
      required: [issuer_node_id, error]
      properties:
        issuer_node_id:
          type: string
          nullable: true
          pattern: "^[0-9a-fA-F]{64}$"
        error:
          type: string

    TrustedIssuerSyncResponse:
      type: object
      required: [accepted, rejected, errors]
      properties:
        accepted:
          type: integer
        rejected:
          type: integer
        errors:
          type: array
          items:
            $ref: "#/components/schemas/TrustedIssuerSyncError"

    TraceResponse:
      type: object
      required: [chain, total_depth, chain_complete]
      properties:
        chain:
          type: array
          items:
            $ref: "#/components/schemas/AnchorResponse"
        total_depth:
          type: integer
          example: 3
        chain_complete:
          type: boolean
          example: true

    BatchCreateAnchorsResponse:
      type: object
      required: [anchors, errors]
      properties:
        anchors:
          type: array
          items:
            $ref: "#/components/schemas/AnchorResponse"
        errors:
          type: array
          items:
            $ref: "#/components/schemas/BatchItemError"

    BatchItemError:
      type: object
      required: [index, error_code, error_message]
      properties:
        index:
          type: integer
          example: 1
        error_code:
          type: string
          example: "BAD_REQUEST"
        error_message:
          type: string
          example: "invalid hex for event_data_hash: Invalid character"

    OfflineSyncRequest:
      type: object
      required: [anchors]
      properties:
        anchors:
          type: array
          maxItems: 1000
          items:
            $ref: "#/components/schemas/OfflineAnchorPayload"

    OfflineAnchorPayload:
      type: object
      required:
        - anchor_id
        - event_data_hash
        - public_key
        - signature
        - temporal_index
        - origin_fingerprint
        - created_at_ns
      properties:
        anchor_id:
          type: string
          description: Hex-encoded 32-byte anchor ID
        event_data_hash:
          type: string
          description: Hex-encoded 32-byte event data hash
        public_key:
          type: string
          description: Hex-encoded 64-byte raw SM2 public key (`x || y`).
          pattern: "^[0-9a-fA-F]{128}$"
        signature:
          type: string
          description: Hex-encoded 64-byte SM2 signature (`r || s`).
          pattern: "^[0-9a-fA-F]{128}$"
        temporal_index:
          type: integer
          format: uint64
        prev_anchor_id:
          type: string
          nullable: true
          description: Hex-encoded 32-byte previous anchor ID (null for genesis)
        origin_fingerprint:
          type: string
          description: Hex-encoded 32-byte origin fingerprint
        created_at_ns:
          type: integer
          format: uint64
          description: Creation timestamp in nanoseconds

    OfflineSyncResponse:
      type: object
      required: [total, accepted, rejected, errors]
      properties:
        total:
          type: integer
          example: 5
        accepted:
          type: integer
          example: 4
        rejected:
          type: integer
          example: 1
        errors:
          type: array
          items:
            type: string
          example: ["[2] origin_fingerprint does not match public_key"]

    # -------------------------------------------------------------------------
    # Agent schemas
    # -------------------------------------------------------------------------
    RegisterAgentRequest:
      type: object
      required: [public_key, endpoint]
      properties:
        public_key:
          type: string
          description: Hex-encoded 64-byte raw SM2 agent public key (`x || y`).
          pattern: "^[0-9a-fA-F]{128}$"
        endpoint:
          type: string
          description: Network endpoint of the agent
          example: "https://agent.example.com:9090"

    AgentResponse:
      type: object
      required: [agent_id, public_key, endpoint, registered_at, status, key_version, permissions]
      properties:
        agent_id:
          type: string
          description: Hex-encoded 32-byte agent identifier
        public_key:
          type: string
          description: Hex-encoded 64-byte raw SM2 agent public key (`x || y`).
          pattern: "^[0-9a-fA-F]{128}$"
        endpoint:
          type: string
        registered_at:
          type: integer
          format: uint64
        status:
          type: string
          enum: [Active, Offboarded]
          example: "Active"
        key_version:
          type: integer
          format: uint32
          example: 1
        permissions:
          type: array
          items:
            type: string
            enum: [ReadOnly, Generate, Verify, Admin]
          example: ["ReadOnly", "Generate", "Verify"]

    RotateAgentKeyRequest:
      type: object
      required: [new_public_key]
      properties:
        new_public_key:
          type: string
          description: Hex-encoded 64-byte raw SM2 agent public key (`x || y`).
          pattern: "^[0-9a-fA-F]{128}$"

    UpdatePermissionsRequest:
      type: object
      properties:
        grant:
          type: array
          items:
            type: string
            enum: [ReadOnly, Generate, Verify, Admin]
          default: []
        revoke:
          type: array
          items:
            type: string
            enum: [ReadOnly, Generate, Verify, Admin]
          default: []

    PermissionsResponse:
      type: object
      required: [agent_id, permissions]
      properties:
        agent_id:
          type: string
        permissions:
          type: array
          items:
            type: string
            enum: [ReadOnly, Generate, Verify, Admin]

    # -------------------------------------------------------------------------
    # Audit schemas
    # -------------------------------------------------------------------------
    AuditChallengeRequest:
      type: object
      required: [anchor_id, challenger_node_id]
      properties:
        anchor_id:
          type: string
          description: Hex-encoded 32-byte anchor ID to challenge
        challenger_node_id:
          type: string
          description: Hex-encoded 32-byte challenger node ID

    AuditChallengeResponse:
      type: object
      required: [challenge_id, anchor_id, challenger_node_id, timestamp_ns, nonce]
      properties:
        challenge_id:
          type: string
          description: Hex-encoded 32-byte challenge ID
        anchor_id:
          type: string
        challenger_node_id:
          type: string
        timestamp_ns:
          type: integer
          format: uint64
        nonce:
          type: string
          description: Hex-encoded random nonce

    AuditRespondRequest:
      type: object
      required: [challenge_id, anchor_id]
      properties:
        challenge_id:
          type: string
          description: Hex-encoded 32-byte challenge ID
        anchor_id:
          type: string
          description: Hex-encoded 32-byte anchor ID

    AuditRespondResponse:
      type: object
      required: [challenge_id, anchor_found, anchor_valid, responder_node_id, response_time_ns, signature]
      properties:
        challenge_id:
          type: string
        anchor_found:
          type: boolean
          example: true
        anchor_valid:
          type: boolean
          example: true
        responder_node_id:
          type: string
        response_time_ns:
          type: integer
          format: uint64
          example: 150000
        signature:
          type: string
          description: Hex-encoded HMAC/signature proving response authenticity

    AuditResultResponse:
      type: object
      required: [challenge_id, passed, response_time_ns]
      properties:
        challenge_id:
          type: string
        passed:
          type: boolean
          example: true
        response_time_ns:
          type: integer
          format: uint64
          example: 150000
        failure_reason:
          type: string
          nullable: true
          example: null

    # -------------------------------------------------------------------------
    # Webhook schemas
    # -------------------------------------------------------------------------
    RegisterWebhookRequest:
      type: object
      required: [url]
      properties:
        url:
          type: string
          format: uri
          description: URL to POST event payloads to
          example: "https://example.com/webhook"
        secret:
          type: string
          nullable: true
          description: Optional shared secret for HMAC-SHA256 payload signing
          example: "my-shared-secret"
        events:
          type: array
          nullable: true
          items:
            type: string
            enum: [anchor_generated, anchor_verified, agent_registered, agent_offboarded]
          description: Event types to subscribe to. Null or empty subscribes to all.
          example: ["anchor_generated", "agent_registered"]

    WebhookResponse:
      type: object
      required: [id, url, secret_set]
      properties:
        id:
          type: integer
          format: uint64
          example: 1
        url:
          type: string
          example: "https://example.com/webhook"
        secret_set:
          type: boolean
          example: true
        events:
          type: array
          nullable: true
          items:
            type: string
            enum: [anchor_generated, anchor_verified, agent_registered, agent_offboarded]

    # -------------------------------------------------------------------------
    # Privacy schemas
    # -------------------------------------------------------------------------
    AnonymizeRequest:
      type: object
      required: [anchor_id]
      properties:
        anchor_id:
          type: string
          description: Hex-encoded 32-byte anchor ID
        privacy_level:
          type: integer
          minimum: 1
          maximum: 5
          default: 3
          description: Privacy level (1 = minimal, 5 = maximum)

    AnonymizeResponse:
      type: object
      required: [anon_anchor_id, obscured_temporal, pseudonym_hash, encrypted_proof, privacy_level]
      properties:
        anon_anchor_id:
          type: string
          description: Hex-encoded anonymized anchor ID
        obscured_temporal:
          type: string
          description: Hex-encoded obscured temporal value
        pseudonym_hash:
          type: string
          description: Hex-encoded pseudonym hash
        encrypted_proof:
          type: string
          description: Hex-encoded encrypted proof
        privacy_level:
          type: integer
          example: 3

    # -------------------------------------------------------------------------
    # Blockchain schemas
    # -------------------------------------------------------------------------
    AttestRequest:
      type: object
      required: [anchor_id]
      properties:
        anchor_id:
          type: string
          description: Hex-encoded 32-byte anchor ID

    AttestResponse:
      type: object
      required: [queued, pending_count]
      properties:
        queued:
          type: boolean
          example: true
        merkle_root:
          type: string
          nullable: true
          description: Hex-encoded 32-byte Merkle root (non-null when batch was flushed)
        tx_hash:
          type: string
          nullable: true
          description: Blockchain transaction hash (non-null when batch was flushed)
        pending_count:
          type: integer
          example: 3

    FlushResponse:
      type: object
      required: [merkle_root, tx_hash]
      properties:
        merkle_root:
          type: string
          description: Hex-encoded 32-byte Merkle root
        tx_hash:
          type: string
          description: Blockchain transaction hash

    MerkleRootRequest:
      type: object
      required: [anchor_ids]
      properties:
        anchor_ids:
          type: array
          minItems: 1
          items:
            type: string
          description: List of hex-encoded 32-byte anchor IDs

    MerkleRootResponse:
      type: object
      required: [root, leaf_count]
      properties:
        root:
          type: string
          description: Hex-encoded 32-byte Merkle root
          example: "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
        leaf_count:
          type: integer
          example: 3

    # -------------------------------------------------------------------------
    # Interop schemas
    # -------------------------------------------------------------------------
    ImportAnchorRequest:
      type: object
      required: [platform_id, external_id, data]
      properties:
        platform_id:
          type: string
          description: Registered platform identifier
          example: "rfc3161"
        external_id:
          type: string
          description: External anchor identifier
          example: "ext-001"
        data:
          type: string
          description: Hex-encoded anchor data
        metadata:
          type: object
          additionalProperties:
            type: string
          default: {}
          example:
            source: "external-tsa"

    ImportAnchorResponse:
      type: object
      required: [anchor_id, temporal_index]
      properties:
        anchor_id:
          type: string
          description: Hex-encoded Gnomon anchor ID
        temporal_index:
          type: integer
          format: uint64

    ExportAnchorRequest:
      type: object
      required: [anchor_id, platform_id]
      properties:
        anchor_id:
          type: string
          description: Hex-encoded 32-byte Gnomon anchor ID
        platform_id:
          type: string
          description: Target platform identifier

    ExportAnchorResponse:
      type: object
      required: [platform_id, external_id, data, metadata]
      properties:
        platform_id:
          type: string
        external_id:
          type: string
        data:
          type: string
        metadata:
          type: object
          additionalProperties:
            type: string

    # -------------------------------------------------------------------------
    # Routing schemas
    # -------------------------------------------------------------------------
    SelectNodeRequest:
      type: object
      required: [strategy]
      properties:
        strategy:
          type: string
          enum: [auto, manual, priority]
          description: Routing strategy to use
        target_node_id:
          type: string
          nullable: true
          description: Required for 'manual' strategy -- hex-encoded 32-byte node ID
        priority_list:
          type: array
          nullable: true
          items:
            type: string
          description: Required for 'priority' strategy -- list of hex-encoded node IDs
        weight_quality:
          type: number
          format: double
          default: 1.0
          description: Weight for quality score (auto strategy)
        weight_cost:
          type: number
          format: double
          default: 1.0
          description: Weight for cost (auto strategy)
        weight_latency:
          type: number
          format: double
          default: 1.0
          description: Weight for latency (auto strategy)

    RoutingNodeInfo:
      type: object
      required: [node_id, healthy, cost_per_anchor, quality_score, traffic_share, location, capabilities]
      properties:
        node_id:
          type: string
        healthy:
          type: boolean
          example: true
        cost_per_anchor:
          type: integer
          format: uint64
          example: 10
        quality_score:
          type: number
          format: double
          example: 1.0
        traffic_share:
          type: number
          format: double
          example: 0.25
        location:
          type: string
          example: "us-east-1"
        capabilities:
          type: array
          items:
            type: string
          example: ["finance", "high-throughput"]

    RoutingRegisterNodeRequest:
      type: object
      required: [node_id, cost_per_anchor, location]
      properties:
        node_id:
          type: string
          description: Hex-encoded 32-byte `SM3(public_key)` node ID
        cost_per_anchor:
          type: integer
          format: uint64
          example: 10
        location:
          type: string
          example: "us-east-1"
        capabilities:
          type: array
          items:
            type: string
          default: []
          example: ["finance", "high-throughput"]

    # -------------------------------------------------------------------------
    # Distribution schemas
    # -------------------------------------------------------------------------
    DeliverRequest:
      type: object
      required: [anchor_id, mode]
      properties:
        anchor_id:
          type: string
          description: Hex-encoded 32-byte anchor ID
        mode:
          type: string
          enum: [point_to_point, batch, cascade]
          description: Distribution mode
        target:
          type: string
          nullable: true
          description: Required for 'point_to_point' -- hex-encoded 32-byte target node ID
        targets:
          type: array
          nullable: true
          items:
            type: string
          description: Required for 'batch' -- list of hex-encoded node IDs
        batch_size:
          type: integer
          nullable: true
          description: Optional batch size hint for 'batch' mode
        chain:
          type: array
          nullable: true
          items:
            type: string
          description: Required for 'cascade' -- ordered list of hex-encoded node IDs

    DeliverResponse:
      type: object
      required: [anchor_id, delivered, failed, mode]
      properties:
        anchor_id:
          type: string
        delivered:
          type: array
          items:
            type: string
          description: Hex-encoded node IDs that received the anchor
        failed:
          type: array
          items:
            type: string
          description: Hex-encoded node IDs that failed to receive
        mode:
          type: string

    # -------------------------------------------------------------------------
    # Billing schemas (billing_routes)
    # -------------------------------------------------------------------------
    RecordUsageRequest:
      type: object
      required: [anchor_id, tier, node_id, user_id]
      properties:
        anchor_id:
          type: string
          description: Hex-encoded 32-byte anchor ID
        tier:
          type: string
          enum: [general, industry, finance]
          description: Service tier
        node_id:
          type: string
          description: Hex-encoded 32-byte node ID
        user_id:
          type: string
          description: User identifier

    RecordUsageResponse:
      type: object
      required: [anchor_id, tier, cost, timestamp]
      properties:
        anchor_id:
          type: string
        tier:
          type: string
        cost:
          type: integer
          format: uint64
          example: 200
        timestamp:
          type: integer
          format: uint64
          example: 1700000000

    RevenueRequest:
      type: object
      required: [tier1_share, tier2_share, platform_reserve, anti_monopoly_fund]
      properties:
        tier1_share:
          type: number
          format: double
          example: 0.30
        tier2_share:
          type: number
          format: double
          example: 0.40
        platform_reserve:
          type: number
          format: double
          example: 0.20
        anti_monopoly_fund:
          type: number
          format: double
          example: 0.10

    # -------------------------------------------------------------------------
    # Temporal (Tier-1) schemas
    # -------------------------------------------------------------------------
    AllocateBatchRequest:
      type: object
      required: [node_id, batch_size]
      properties:
        node_id:
          type: string
          description: Hex-encoded node ID
        batch_size:
          type: integer
          format: uint32
          minimum: 1
          maximum: 100000

    AllocateBatchResponse:
      type: object
      required: [start_index, count, ttl_seconds]
      properties:
        start_index:
          type: integer
          format: uint64
          example: 0
        count:
          type: integer
          format: uint32
          example: 100
        ttl_seconds:
          type: integer
          format: uint64
          example: 3600

    KeyPackageSyncRequest:
      type: object
      required: [node_id, current_key_version]
      properties:
        node_id:
          type: string
          description: Hex-encoded 32-byte Tier-2 node ID.
          pattern: "^[0-9a-fA-F]{64}$"
        requested_ti_span:
          type: string
          description: Optional requested ChildTHK TI span as a uint64 decimal string.
        current_key_version:
          type: integer
          format: uint32
          description: Latest KeyPackage version currently installed by the Tier-2 node.
        requested_at_ti:
          type: string
          description: Optional Tier-2 current TI as a uint64 decimal string.
        node_cert_sig:
          type: string
          description: Optional hex-encoded NodeCertificate signature or proof blob.
        prev_kp_id:
          type: string
          nullable: true
          description: Optional 32-byte hex commitment identifying the previous KeyPackage during renewal.
          pattern: "^[0-9a-fA-F]{64}$"
        ti_consumed:
          type: integer
          format: uint32
          nullable: true
          description: Optional number of TI values consumed from the previous KeyPackage.
        last_eea_anchor_id:
          type: string
          nullable: true
          description: Optional 32-byte hex AnchorID of the last EEA emitted under the previous KeyPackage. Zero means none consumed.
          pattern: "^[0-9a-fA-F]{64}$"
        last_eea_ti:
          type: string
          nullable: true
          description: Optional uint64 decimal string of the last EEA TI under the previous KeyPackage.

    KeyPackageSyncResponse:
      type: object
      required: [key_package, child_thk, root_thk, op_certificate]
      properties:
        key_package:
          $ref: "#/components/schemas/KeyPackage"
        child_thk:
          $ref: "#/components/schemas/ChildTHK"
        root_thk:
          $ref: "#/components/schemas/RootTHK"
        op_certificate:
          $ref: "#/components/schemas/OpCertificate"

    ChildTHKRequest:
      type: object
      required: [node_id, requested_ti_span]
      properties:
        node_id:
          type: string
          description: Hex-encoded 32-byte Tier-2 node ID.
          pattern: "^[0-9a-fA-F]{64}$"
        requested_ti_span:
          type: string
          description: Requested TI span as a uint64 decimal string.
        requested_at_ti:
          type: string
          description: Optional Tier-2 current TI as a uint64 decimal string.
        node_cert_sig:
          type: string
          description: Optional hex-encoded NodeCertificate signature or proof blob.

    ChildTHKResponse:
      type: object
      required: [child_thk]
      properties:
        child_thk:
          $ref: "#/components/schemas/ChildTHK"

    CurrentRootTHKResponse:
      type: object
      required: [root_thk, op_certificate]
      properties:
        root_thk:
          $ref: "#/components/schemas/RootTHK"
        op_certificate:
          $ref: "#/components/schemas/OpCertificate"

    RootTHKRangeResponse:
      type: object
      required: [from_ti, to_ti, root_thks, op_certificates]
      properties:
        from_ti:
          type: string
        to_ti:
          type: string
        root_thks:
          type: array
          items:
            $ref: "#/components/schemas/RootTHK"
        op_certificates:
          type: array
          items:
            $ref: "#/components/schemas/OpCertificate"

    KeyPackage:
      type: object
      required:
        - t_epoch
        - valid_until
        - grace_period_seconds
        - issuer_node_id
        - root_public_key_hash
        - key_version
        - issued_at
        - child_thk_commitment
        - target_node_fp
        - package_signature
      properties:
        t_epoch:
          type: string
          description: Authorized TemporalIndex window start as a uint64 decimal string.
        valid_until:
          type: string
          description: KeyPackage expiry / renewal signal as a uint64 decimal string. In current runtime it matches the bound ChildTHK ti_range_end.
        grace_period_seconds:
          type: integer
          format: uint32
        issuer_node_id:
          type: string
          description: Hex-encoded L1 issuer NodeId.
          pattern: "^[0-9a-fA-F]{64}$"
        root_public_key_hash:
          type: string
          description: Hex-encoded hash of the L1 issuer public key.
          pattern: "^[0-9a-fA-F]{64}$"
        key_version:
          type: integer
          format: uint32
        issued_at:
          type: string
          description: Unix milliseconds as a uint64 decimal string.
        child_thk_commitment:
          type: string
          description: Hex-encoded 32-byte ChildTHK commitment bound by the package signature.
          pattern: "^[0-9a-fA-F]{64}$"
        target_node_fp:
          type: string
          description: Hex-encoded 32-byte Tier-2 fingerprint this package is bound to.
          pattern: "^[0-9a-fA-F]{64}$"
        package_signature:
          type: string
          description: Hex-encoded L1 signature over H(KeyPackage signing payload).

    ChildTHK:
      type: object
      required:
        - parent_root_thk_id
        - t_utc
        - ti_range_start
        - ti_range_end
        - sub_sequence
        - target_node_fp
        - nonce
        - child_commitment
        - child_thk_signature
      properties:
        parent_root_thk_id:
          type: string
          description: Hex-encoded 32-byte parent root THK commitment.
          pattern: "^[0-9a-fA-F]{64}$"
        t_utc:
          type: string
          description: ChildTHK issue time in UTC nanoseconds as a uint64 decimal string.
        ti_range_start:
          type: string
          description: Inclusive TI range start as a uint64 decimal string.
        ti_range_end:
          type: string
          description: Exclusive TI range end as a uint64 decimal string.
        sub_sequence:
          type: integer
          format: uint32
          description: Sequence number under the parent root THK.
        target_node_fp:
          type: string
          description: Hex-encoded 32-byte target Tier-2 fingerprint.
          pattern: "^[0-9a-fA-F]{64}$"
        nonce:
          type: string
          description: Hex-encoded 16-byte ChildTHK nonce.
          pattern: "^[0-9a-fA-F]{32}$"
        child_commitment:
          type: string
          description: Hex-encoded 32-byte ChildTHK commitment.
          pattern: "^[0-9a-fA-F]{64}$"
        child_thk_signature:
          type: string
          description: Hex-encoded signature over the ChildTHK signing payload.

    RootTHK:
      type: object
      required:
        - time_commitment
        - t_epoch
        - t_utc
        - time_window
        - valid_until
        - grace_period
        - issuer_node_id
        - key_version
        - prev_commitment
        - signature
      properties:
        time_commitment:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        t_epoch:
          type: string
        t_utc:
          type: string
        time_window:
          type: string
        valid_until:
          type: string
        grace_period:
          type: integer
          format: uint32
        issuer_node_id:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        key_version:
          type: integer
          format: uint32
        prev_commitment:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        signature:
          type: string
          description: Hex-encoded RootTHK signature.

    OpCertificate:
      type: object
      required:
        - op_cert_id
        - issuer_root_fp
        - op_pubkey
        - valid_from
        - valid_until
        - allowed_ops
        - key_version_op
        - root_sig
      properties:
        op_cert_id:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        issuer_root_fp:
          type: string
          pattern: "^[0-9a-fA-F]{64}$"
        op_pubkey:
          type: string
          description: Hex-encoded SM2 operation public key.
        valid_from:
          type: string
        valid_until:
          type: string
        allowed_ops:
          type: integer
          format: uint32
        key_version_op:
          type: integer
          format: uint32
        root_sig:
          type: string
          description: Hex-encoded root signature over the OpCertificate payload.

    PrecisionTimeResponse:
      type: object
      required: [timestamp_ns, confidence_interval_ns, clock_source]
      properties:
        timestamp_ns:
          type: integer
          format: uint64
          example: 1700000000000000000
        confidence_interval_ns:
          type: integer
          format: uint32
          example: 1000000
        clock_source:
          type: string
          example: "system_clock"

    ProposeEpochRequest:
      type: object
      required: [new_epoch, node_id]
      properties:
        new_epoch:
          type: integer
          format: uint64
        node_id:
          type: string
          description: Hex-encoded 32-byte node ID

    ProposeEpochResponse:
      type: object
      required: [accepted, current_epoch]
      properties:
        accepted:
          type: boolean
          example: true
        current_epoch:
          type: integer
          format: uint64
          example: 6

    VoteEpochRequest:
      type: object
      required: [epoch, voter_id]
      properties:
        epoch:
          type: integer
          format: uint64
        voter_id:
          type: string
          description: Hex-encoded 32-byte voter node ID

    VoteEpochResponse:
      type: object
      required: [result, epoch]
      properties:
        result:
          type: string
          enum: [pending, confirmed, rejected]
          example: "confirmed"
        epoch:
          type: integer
          format: uint64
          example: 6
        reason:
          type: string
          nullable: true
          example: null

    GetEpochResponse:
      type: object
      required: [current_epoch, is_confirmed]
      properties:
        current_epoch:
          type: integer
          format: uint64
          example: 5
        is_confirmed:
          type: boolean
          example: true

    # -------------------------------------------------------------------------
    # Node Management (Tier-1) schemas
    # -------------------------------------------------------------------------
    RegisterNodeRequest:
      type: object
      required: [node_id, public_key, endpoint]
      properties:
        node_id:
          type: string
          description: Hex-encoded 32-byte node ID
          pattern: "^[0-9a-fA-F]{64}$"
        public_key:
          type: string
          description: Hex-encoded 64-byte raw SM2 node signing public key (`x || y`, no PEM/DER/SEC1 prefix)
          pattern: "^[0-9a-fA-F]{128}$"
        endpoint:
          type: string
          description: Network endpoint URL
          example: "https://gnomonsystem.cn/api"
        node_tier:
          type: integer
          format: uint32
          default: 2
          minimum: 1
        operator_id:
          type: string
          nullable: true
          description: Hex-encoded 32-byte operator fingerprint. Defaults to all-zero for development registration.
          pattern: "^[0-9a-fA-F]{64}$"
        region_code:
          type: string
          nullable: true
          description: 4 ASCII bytes such as CN01, or 8 hex chars.
        valid_from:
          type: integer
          format: uint64
          nullable: true
          description: NodeCertificate valid-from TemporalIndex. Defaults to 0.
        valid_until:
          type: integer
          format: uint64
          nullable: true
          description: Exclusive NodeCertificate valid-until TemporalIndex.
        key_version:
          type: integer
          format: uint32
          nullable: true
          default: 1

    RegisterNodeResponse:
      type: object
      required:
        - accepted
        - node_cert
        - node_fingerprint
        - issuer_node_id
        - issuer_public_key
        - valid_from
        - valid_until
        - key_version
      properties:
        accepted:
          type: boolean
          example: true
        node_cert:
          type: string
          description: Hex-encoded node certificate
        node_fingerprint:
          type: string
          description: Hex-encoded OriginFingerprint derived from public_key.
          pattern: "^[0-9a-fA-F]{64}$"
        issuer_node_id:
          type: string
          description: Hex-encoded L1 issuer NodeId derived from issuer_public_key.
          pattern: "^[0-9a-fA-F]{64}$"
        issuer_public_key:
          type: string
          description: Hex-encoded 64-byte raw SM2 L1 issuer public key needed for certificate verification.
          pattern: "^[0-9a-fA-F]{128}$"
        valid_from:
          type: string
          description: NodeCertificate valid-from TemporalIndex as decimal string.
        valid_until:
          type: string
          description: NodeCertificate exclusive valid-until TemporalIndex as decimal string.
        key_version:
          type: integer
          format: uint32

    HeartbeatRequest:
      type: object
      required: [node_id]
      properties:
        node_id:
          type: string
          description: Hex-encoded node ID

    HeartbeatResponse:
      type: object
      required: [acknowledged, server_time_ns]
      properties:
        acknowledged:
          type: boolean
          example: true
        server_time_ns:
          type: integer
          format: uint64
          example: 1700000000000000000

    NodeInfoResponse:
      type: object
      required: [node_id, public_key, endpoint, registered_at, last_heartbeat]
      properties:
        node_id:
          type: string
        public_key:
          type: string
        endpoint:
          type: string
        registered_at:
          type: integer
          format: uint64
        last_heartbeat:
          type: integer
          format: uint64

    # -------------------------------------------------------------------------
    # Operations schemas
    # -------------------------------------------------------------------------
    NodeReport:
      type: object
      required:
        - node_id
        - uptime_seconds
        - anchors_generated
        - anchors_verified
        - anchors_failed
        - storage_size
        - pool_remaining
        - active_agents
        - active_origins
        - degradation_level
        - report_timestamp
      properties:
        node_id:
          type: string
        uptime_seconds:
          type: integer
          format: uint64
          example: 86400
        anchors_generated:
          type: integer
          format: uint64
          example: 1500
        anchors_verified:
          type: integer
          format: uint64
          example: 300
        anchors_failed:
          type: integer
          format: uint64
          example: 2
        storage_size:
          type: integer
          format: uint64
          example: 1500
        pool_remaining:
          type: integer
          format: uint64
          example: 8500
        active_agents:
          type: integer
          format: uint64
          example: 3
        active_origins:
          type: integer
          format: uint64
          example: 12
        degradation_level:
          type: integer
          minimum: 0
          maximum: 3
          description: "0=Normal, 1=GracePeriod, 2=Suspended, 3=Recovering"
          example: 0
        prime_root_thk_sync:
          $ref: "#/components/schemas/PrimeRootTHKSyncStatus"
        report_timestamp:
          type: integer
          format: uint64
          example: 1700000000

    PrimeRootTHKSyncStatus:
      type: object
      required:
        - status
        - sync_cursor
        - root_thk_history_count
        - op_certificate_history_count
      properties:
        status:
          type: string
          description: "High-level RootTHK catch-up state: never_synced, ok, or error."
          example: "ok"
        sync_cursor:
          type: integer
          format: uint64
          description: "Inclusive cursor used for the next incremental /v1/prime/thk range request."
          example: 1710000000000
        last_attempt_at:
          type: integer
          format: uint64
          nullable: true
          description: "Unix epoch seconds of the most recent RootTHK sync attempt."
        last_success_at:
          type: integer
          format: uint64
          nullable: true
          description: "Unix epoch seconds of the most recent successful RootTHK sync."
        last_synced_key_version:
          type: integer
          format: uint32
          nullable: true
          description: "Most recent root_thk.key_version installed by the incremental sync loop."
        current_root_key_version:
          type: integer
          format: uint32
          nullable: true
          description: "Current RootTHK key version held in memory by the node."
        last_error:
          type: string
          nullable: true
          description: "Most recent RootTHK sync failure reason."
        root_thk_history_count:
          type: integer
          format: uint64
          example: 3
        op_certificate_history_count:
          type: integer
          format: uint64
          example: 3

    BillingRecord:
      type: object
      required: [period_start, period_end, total_operations, generate_count, verify_count, storage_bytes_used]
      properties:
        period_start:
          type: integer
          format: uint64
          example: 1700000000
        period_end:
          type: integer
          format: uint64
          example: 1700086400
        total_operations:
          type: integer
          format: uint64
          example: 1800
        generate_count:
          type: integer
          format: uint64
          example: 1500
        verify_count:
          type: integer
          format: uint64
          example: 300
        storage_bytes_used:
          type: integer
          format: uint64
          example: 10485760

    # -------------------------------------------------------------------------
    # Envelope wrappers (for specific response types)
    # -------------------------------------------------------------------------
    ApiResponseAnchorResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/AnchorResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseBatchCreateAnchorsResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/BatchCreateAnchorsResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseVerifyResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/VerifyResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseBatchVerifyAnchorsResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/BatchVerifyAnchorsResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseLayerAnchorSummary:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/LayerAnchorSummary"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseClaimAnchorResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/ClaimAnchorResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseClaimAnchorPrepareResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/ClaimAnchorPrepareResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseClaimVerifyResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/ClaimVerifyResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseGrantAnchorResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/GrantAnchorResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseGrantAnchorPrepareResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/GrantAnchorPrepareResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseGrantVerifyResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/GrantVerifyResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseNodeKeyVersionResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/NodeKeyVersionResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseNodeKeyRegistryResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/NodeKeyRegistryResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseTrustedIssuerResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/TrustedIssuerResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseTrustedIssuerListResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/TrustedIssuerListResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseTrustedIssuerRotationResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/TrustedIssuerRotationResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseTrustedIssuerSyncResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/TrustedIssuerSyncResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseTrustedIssuerClusterSnapshot:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/TrustedIssuerClusterSnapshot"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseTrustedIssuerClusterSyncResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/TrustedIssuerClusterSyncResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseOfflineSyncResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/OfflineSyncResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseTraceResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/TraceResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseDeleteAnchorResult:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          type: object
          properties:
            anchor_id:
              type: string
            status:
              type: string
              example: "deleted"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseRegisterAgentResult:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          type: object
          properties:
            agent_id:
              type: string
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseKeyRotatedResult:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          type: object
          properties:
            agent_id:
              type: string
            status:
              type: string
              example: "key_rotated"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseOffboardResult:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          type: object
          properties:
            agent_id:
              type: string
            status:
              type: string
              example: "offboarded"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponsePermissionsResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/PermissionsResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseAgentList:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/AgentResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseAuditChallengeResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/AuditChallengeResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseAuditRespondResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/AuditRespondResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseAuditResultList:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/AuditResultResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseWebhookRegistered:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          type: object
          properties:
            id:
              type: integer
              format: uint64
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseWebhookList:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/WebhookResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseWebhookRemoved:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          type: object
          properties:
            id:
              type: integer
              format: uint64
            status:
              type: string
              example: "removed"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseAnonymizeResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/AnonymizeResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseAttestResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/AttestResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseFlushResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/FlushResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponsePendingCount:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          type: object
          properties:
            pending_count:
              type: integer
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseMerkleRootResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/MerkleRootResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponsePlatformList:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          type: object
          properties:
            platforms:
              type: array
              items:
                type: string
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseImportAnchorResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/ImportAnchorResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseSelectedNode:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          type: object
          properties:
            node_id:
              type: string
              nullable: true
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseRoutingNodeList:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          type: object
          properties:
            nodes:
              type: array
              items:
                $ref: "#/components/schemas/RoutingNodeInfo"
            count:
              type: integer
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseRegistered:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          type: object
          properties:
            registered:
              type: boolean
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseAntiMonopolyResult:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          type: object
          properties:
            demoted_nodes:
              type: array
              items:
                type: string
            count:
              type: integer
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseDeliverResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/DeliverResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseDeliveryLog:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          type: object
          properties:
            deliveries:
              type: array
              items:
                type: object
                properties:
                  anchor_id:
                    type: string
                  delivered_to:
                    type: array
                    items:
                      type: string
            count:
              type: integer
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseRecordUsageResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/RecordUsageResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseBillingRecordList:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          type: object
          properties:
            records:
              type: array
              items:
                type: object
                properties:
                  anchor_id:
                    type: string
                  tier:
                    type: string
                  node_id:
                    type: string
                  user_id:
                    type: string
                  cost:
                    type: integer
                    format: uint64
                  quality_met:
                    type: boolean
                  timestamp:
                    type: integer
                    format: uint64
            count:
              type: integer
            total_cost:
              type: integer
              format: uint64
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseRevenueResult:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          type: object
          properties:
            revenue:
              type: object
              properties:
                tier1:
                  type: integer
                  format: uint64
                tier2:
                  type: integer
                  format: uint64
                platform:
                  type: integer
                  format: uint64
                anti_monopoly:
                  type: integer
                  format: uint64
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseAllocateBatchResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/AllocateBatchResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseKeyPackageSyncResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/KeyPackageSyncResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseChildTHKResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/ChildTHKResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseCurrentRootTHKResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/CurrentRootTHKResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseRootTHKRangeResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/RootTHKRangeResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponsePrecisionTimeResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/PrecisionTimeResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseGetEpochResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/GetEpochResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseProposeEpochResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/ProposeEpochResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseVoteEpochResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/VoteEpochResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseNodeInfoList:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/NodeInfoResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseRegisterNodeResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/RegisterNodeResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseHeartbeatResponse:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/HeartbeatResponse"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseHealthz:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          type: object
          properties:
            status:
              type: string
              example: "healthy"
            service:
              type: string
              example: "tier2-node"
            node_id:
              type: string
            origin_fingerprint:
              type: string
            crypto_suite:
              type: string
              enum: [Chinese]
            pool_remaining:
              type: integer
              format: uint64
            prime_materials_installed:
              type: boolean
            current_key_package_version:
              type: integer
              format: uint32
              nullable: true
            root_thk_sync_cursor:
              type: integer
              format: uint64
            root_thk_last_sync_attempt_at:
              type: integer
              format: uint64
              nullable: true
            root_thk_last_sync_at:
              type: integer
              format: uint64
              nullable: true
            root_thk_last_synced_key_version:
              type: integer
              format: uint32
              nullable: true
            root_thk_last_sync_error:
              type: string
              nullable: true
            prime_root_thk_sync:
              $ref: "#/components/schemas/PrimeRootTHKSyncStatus"
            timestamp:
              type: integer
              format: uint64
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseReadyz:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          type: object
          properties:
            ready:
              type: boolean
              example: true
            service:
              type: string
              example: "tier2-node"
            pool_remaining:
              type: integer
              format: uint64
            registered_nodes:
              type: integer
              description: Present on Tier-1 readyz responses
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseNodeReport:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/NodeReport"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid

    ApiResponseBillingRecord:
      type: object
      required: [data, error, request_id]
      properties:
        data:
          $ref: "#/components/schemas/BillingRecord"
        error:
          nullable: true
          example: null
        request_id:
          type: string
          format: uuid
