openapi: 3.1.0
info:
  title: DVM-MPC Gateway API
  version: "1.0"
  description: |
    Authenticated server-to-server product API for a DVM-MPC deployment.

    The operation gateway is the public integration surface for custody platforms,
    treasury backends, and control-plane services. It collects approvals, obtains
    signed admission decisions from the Policy Authority, drives runtime node
    steps, and returns operation, custody, policy, and evidence views.

    Conventions: every 32-byte value (operation ids, hashes, roots, result_ref)
    is a lowercase 64-character hex string. Timestamps are *_at_ms epoch
    milliseconds. Errors are { "error": code, "message": text } with stable
    codes.
servers:
  - url: http://localhost:19200
    description: Operation gateway (development compose)
tags:
  - name: gateway-operations
    description: Operation gateway — signing operations
  - name: gateway-custody
    description: Operation gateway — custody keys and lifecycle
  - name: gateway-policy
    description: Operation gateway — policy evaluation
security:
  - bearerAuth: []
  - {}
paths:
  /v1/operations/sign:
    post:
      tags: [gateway-operations]
      summary: Submit a signing operation
      operationId: createSignOperation
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/SignOperationRequest" }
      responses:
        "200":
          description: Operation created or replayed by idempotency key.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/SignOperationResponse" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "429": { $ref: "#/components/responses/RateLimited" }
  /v1/operations/{operationId}:
    get:
      tags: [gateway-operations]
      summary: Read the operation view
      operationId: getOperation
      parameters: [{ $ref: "#/components/parameters/operationId" }]
      responses:
        "200":
          description: Current operation view.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/OperationView" }
        "404": { $ref: "#/components/responses/NotFound" }
  /v1/operations/{operationId}/approve:
    post:
      tags: [gateway-operations]
      summary: Record an approval; quorum triggers admission
      operationId: approveOperation
      parameters: [{ $ref: "#/components/parameters/operationId" }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/ApproveOperationRequest" }
      responses:
        "200":
          description: Updated operation view.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/OperationView" }
        "404": { $ref: "#/components/responses/NotFound" }
  /v1/operations/{operationId}/execute:
    post:
      tags: [gateway-operations]
      summary: Execute (sequences runtime policy-receipt and execute)
      operationId: executeOperation
      parameters: [{ $ref: "#/components/parameters/operationId" }]
      requestBody:
        content:
          application/json:
            schema: { $ref: "#/components/schemas/ExecuteOperationRequest" }
      responses:
        "200":
          description: Updated operation view (state executed).
          content:
            application/json:
              schema: { $ref: "#/components/schemas/OperationView" }
        "409": { $ref: "#/components/responses/Conflict" }
  /v1/operations/{operationId}/publish:
    post:
      tags: [gateway-operations]
      summary: Publish (drives the runtime publication gate)
      operationId: publishOperation
      parameters: [{ $ref: "#/components/parameters/operationId" }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/PublishOperationRequest" }
      responses:
        "200":
          description: Updated operation view (state published).
          content:
            application/json:
              schema: { $ref: "#/components/schemas/OperationView" }
        "409": { $ref: "#/components/responses/Conflict" }
  /v1/operations/{operationId}/node-recovery:
    post:
      tags: [gateway-operations]
      summary: Record a node-recovery effect (idempotent per effect_id)
      operationId: recordOperationNodeRecovery
      parameters: [{ $ref: "#/components/parameters/operationId" }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/NodeRecoveryRequest" }
      responses:
        "200":
          description: Committed recovery record and updated view.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/OperationNodeRecoveryResponse" }
  /v1/operations/{operationId}/audit-replay:
    get:
      tags: [gateway-operations]
      summary: Export the redacted audit log (scope audit.read; 512 KB cap)
      operationId: getOperationAuditReplay
      parameters: [{ $ref: "#/components/parameters/operationId" }]
      responses:
        "200":
          description: Redacted audit log.
          content:
            application/json:
              schema: { type: object }
        "403": { $ref: "#/components/responses/Forbidden" }
  /v1/policy/preview:
    post:
      tags: [gateway-policy]
      summary: Evaluate policy without creating an operation
      operationId: previewPolicy
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/PolicyPreviewRequest" }
      responses:
        "200":
          description: Policy decision preview.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/PolicyPreview" }
  /v1/custody/keys:
    post:
      tags: [gateway-custody]
      summary: Create a custody key (runs DKG)
      operationId: createCustodyKey
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/CustodyKeyCreateRequest" }
      responses:
        "200":
          description: Custody operation view for the key-create operation.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/CustodyOperationResponse" }
  /v1/custody/keys/{keyRef}:
    get:
      tags: [gateway-custody]
      summary: Read the custody key view
      operationId: getCustodyKey
      parameters: [{ $ref: "#/components/parameters/keyRef" }]
      responses:
        "200":
          description: Custody key view.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/CustodyKeyView" }
        "404": { $ref: "#/components/responses/NotFound" }
  /v1/custody/keys/{keyRef}/operations:
    get:
      tags: [gateway-custody]
      summary: List operations recorded against the key
      operationId: getCustodyKeyOperations
      parameters: [{ $ref: "#/components/parameters/keyRef" }]
      responses:
        "200":
          description: Custody operations for the key.
          content:
            application/json:
              schema:
                type: object
                properties:
                  operations:
                    type: array
                    items: { $ref: "#/components/schemas/CustodyOperationView" }
  /v1/custody/keys/{keyRef}/sign:
    post:
      tags: [gateway-custody]
      summary: Submit an asset action for signature
      operationId: createCustodySignOperation
      parameters: [{ $ref: "#/components/parameters/keyRef" }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/CustodySignRequest" }
      responses:
        "200":
          description: Custody operation view.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/CustodyOperationResponse" }
  /v1/custody/keys/{keyRef}/refresh:
    post:
      tags: [gateway-custody]
      summary: Refresh shares (same roster, same public key, new share epoch)
      operationId: createCustodyRefreshOperation
      parameters: [{ $ref: "#/components/parameters/keyRef" }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/CustodyKeyRefreshRequest" }
      responses:
        "200":
          description: Custody operation view.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/CustodyOperationResponse" }
  /v1/custody/keys/{keyRef}/reshare:
    post:
      tags: [gateway-custody]
      summary: Reshare to a new roster (same public key)
      operationId: createCustodyReshareOperation
      parameters: [{ $ref: "#/components/parameters/keyRef" }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/CustodyKeyReshareRequest" }
      responses:
        "200":
          description: Custody operation view.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/CustodyOperationResponse" }
  /v1/custody/keys/{keyRef}/rotate:
    post:
      tags: [gateway-custody]
      summary: Rotate to a new key pair (address migration)
      operationId: createCustodyRotateOperation
      parameters: [{ $ref: "#/components/parameters/keyRef" }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/CustodyKeyRotateRequest" }
      responses:
        "200":
          description: Custody operation view.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/CustodyOperationResponse" }
  /v1/custody/keys/{keyRef}/recover:
    post:
      tags: [gateway-custody]
      summary: Recover a participant with helper shares
      operationId: createCustodyRecoverOperation
      parameters: [{ $ref: "#/components/parameters/keyRef" }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/CustodyKeyRecoverRequest" }
      responses:
        "200":
          description: Custody operation view.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/CustodyOperationResponse" }
  /v1/custody/operations/{operationId}:
    get:
      tags: [gateway-custody]
      summary: Read a custody operation
      operationId: getCustodyOperation
      parameters: [{ $ref: "#/components/parameters/operationId" }]
      responses:
        "200":
          description: Custody operation view.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/CustodyOperationView" }
        "404": { $ref: "#/components/responses/NotFound" }
  /v1/custody/operations/{operationId}/approve:
    post:
      tags: [gateway-custody]
      summary: Approve a custody operation
      operationId: approveCustodyOperation
      parameters: [{ $ref: "#/components/parameters/operationId" }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/ApproveOperationRequest" }
      responses:
        "200":
          description: Updated custody operation view.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/CustodyOperationView" }
  /v1/custody/operations/{operationId}/execute:
    post:
      tags: [gateway-custody]
      summary: Execute a custody operation
      operationId: executeCustodyOperation
      parameters: [{ $ref: "#/components/parameters/operationId" }]
      requestBody:
        content:
          application/json:
            schema: { $ref: "#/components/schemas/ExecuteOperationRequest" }
      responses:
        "200":
          description: Updated custody operation view.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/CustodyOperationView" }
  /v1/custody/operations/{operationId}/node-recovery:
    post:
      tags: [gateway-custody]
      summary: Record a custody node-recovery effect
      operationId: recordCustodyOperationNodeRecovery
      parameters: [{ $ref: "#/components/parameters/operationId" }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/NodeRecoveryRequest" }
      responses:
        "200":
          description: Updated custody operation view.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/CustodyOperationView" }
  /v1/custody/operations/{operationId}/publish:
    post:
      tags: [gateway-custody]
      summary: Publish a custody operation result
      operationId: publishCustodyOperation
      parameters: [{ $ref: "#/components/parameters/operationId" }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/PublishOperationRequest" }
      responses:
        "200":
          description: Updated custody operation view.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/CustodyOperationView" }
  /v1/custody/operations/{operationId}/external-export:
    post:
      tags: [gateway-custody]
      summary: Record external rail settlement state
      operationId: recordCustodyExternalExport
      parameters: [{ $ref: "#/components/parameters/operationId" }]
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/CustodyExternalExportRequest" }
      responses:
        "200":
          description: Updated custody operation view.
          content:
            application/json:
              schema: { $ref: "#/components/schemas/CustodyOperationView" }
  /v1/custody/operations/{operationId}/evidence:
    get:
      tags: [gateway-custody]
      summary: Read the custody operation evidence view
      operationId: getCustodyOperationEvidence
      parameters: [{ $ref: "#/components/parameters/operationId" }]
      responses:
        "200":
          description: Evidence view.
          content:
            application/json:
              schema: { type: object }
  /v1/custody/operations/{operationId}/audit-replay:
    get:
      tags: [gateway-custody]
      summary: Export the redacted custody audit log (scope audit.read; 512 KB cap)
      operationId: getCustodyOperationAuditReplay
      parameters: [{ $ref: "#/components/parameters/operationId" }]
      responses:
        "200":
          description: Redacted audit log.
          content:
            application/json:
              schema: { type: object }
        "403": { $ref: "#/components/responses/Forbidden" }
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: Gateway auth modes jwks/introspection. trusted_headers mode uses x-client-id/x-subject-id/x-roles/x-scopes/x-tenant instead.
  parameters:
    operationId:
      name: operationId
      in: path
      required: true
      schema: { $ref: "#/components/schemas/Hex32" }
    keyRef:
      name: keyRef
      in: path
      required: true
      schema: { type: string }
  responses:
    BadRequest:
      description: invalid_json | invalid_operation_id | invalid_execute_request
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    Forbidden:
      description: audit_replay_forbidden | policy_rejected
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    NotFound:
      description: operation_not_found | custody_key_not_found
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    Conflict:
      description: operation_not_executed | result_ref_mismatch | publication_receipt_mismatch
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
    RateLimited:
      description: policy_admission_rate_limited
      content:
        application/json:
          schema: { $ref: "#/components/schemas/Error" }
  schemas:
    Hex32:
      type: string
      pattern: "^[0-9a-f]{64}$"
      description: 32-byte value as lowercase hex.
    Error:
      type: object
      properties:
        error: { type: string }
        message: { type: string }
    ClientBinding:
      type: object
      required: [client_id, subject_id, roles, scopes]
      properties:
        client_id: { type: string }
        subject_id: { type: string }
        roles: { type: array, items: { type: string } }
        scopes: { type: array, items: { type: string } }
        cert_sha256: { type: [string, "null"] }
        token_jti: { type: [string, "null"] }
        tenant: { type: [string, "null"] }
    PaymentIntent:
      type: object
      required: [from_account, to_account, amount_minor, currency]
      properties:
        from_account: { type: string }
        to_account: { type: string }
        amount_minor: { type: integer }
        currency: { type: string }
        reference: { type: string }
    SignOperationRequest:
      type: object
      required: [idempotency_key, payment, requested_participants, publication_mode, client]
      properties:
        idempotency_key: { type: string }
        key_ref: { type: [string, "null"] }
        payment: { $ref: "#/components/schemas/PaymentIntent" }
        bip32:
          type: [object, "null"]
          properties:
            network: { type: string }
            path: { type: string, example: "0/5" }
        requested_participants:
          type: array
          items: { type: integer }
          description: 0-based signer indices.
        threshold: { type: [integer, "null"] }
        epoch_fence_token_hex: { type: [string, "null"] }
        publication_mode: { type: string, enum: [manual, automatic] }
        client: { $ref: "#/components/schemas/ClientBinding" }
    SignOperationResponse:
      type: object
      properties:
        operation: { $ref: "#/components/schemas/OperationView" }
    OperationView:
      type: object
      properties:
        operation_id: { $ref: "#/components/schemas/Hex32" }
        state:
          type: string
          enum: [submitted, pending_approval, admitted, receipt_materialized, executing, executed, published, failed]
        intent: { type: object }
        approvals:
          type: object
          properties:
            approved: { type: integer }
            required: { type: integer }
            subjects: { type: array, items: { type: string } }
        policy: { $ref: "#/components/schemas/PolicyPreview" }
        runtime:
          type: object
          properties:
            phase: { type: string }
            record_root: { $ref: "#/components/schemas/Hex32" }
            ledger_root: { $ref: "#/components/schemas/Hex32" }
            admission_hash: { $ref: "#/components/schemas/Hex32" }
            policy_receipt_id: { type: string }
            permit_binding_id: { type: [string, "null"] }
            result_ref: { type: [string, "null"] }
            publication_receipt: { type: [string, "null"] }
        next_actions:
          type: array
          items: { type: string }
    ApproveOperationRequest:
      type: object
      required: [subject_id, roles, approved_at_ms]
      properties:
        subject_id: { type: string }
        roles: { type: array, items: { type: string } }
        approved_at_ms: { type: integer }
    ExecuteOperationRequest:
      type: object
      properties:
        requested_at_ms: { type: [integer, "null"] }
        signing_share_mul: { type: [object, "null"] }
        recovery_helper_shares: { type: [object, "null"] }
    PublishOperationRequest:
      type: object
      required: [scope, requested_at_ms]
      properties:
        scope: { type: string, example: signature }
        requested_at_ms: { type: integer }
        expected_result_ref: { type: [string, "null"] }
    NodeRecoveryRequest:
      type: object
      required: [effect_id, result_root]
      properties:
        effect_id: { type: string }
        result_root: { $ref: "#/components/schemas/Hex32" }
        secret_kernel_payload: { type: [object, "null"] }
    OperationNodeRecoveryResponse:
      type: object
      properties:
        recovery: { type: object }
        operation: { $ref: "#/components/schemas/OperationView" }
    PolicyPreviewRequest:
      type: object
      required: [intent]
      properties:
        intent: { $ref: "#/components/schemas/SignOperationRequest" }
        approvals:
          type: array
          items: { $ref: "#/components/schemas/ApproveOperationRequest" }
    PolicyPreview:
      type: object
      properties:
        decision: { type: string, enum: [ALLOW, DENY] }
        bundle_id: { type: string }
        bundle_hash: { type: string }
        reasons: { type: array, items: { type: string } }
        obligations: { type: array, items: { type: string } }
        facts: { type: array, items: { type: string } }
    CustodyKeyCreateRequest:
      type: object
      required: [key_ref, tenant_id, asset_id, purpose, threshold, participants, signature_profile, client]
      properties:
        key_ref: { type: string }
        tenant_id: { type: string }
        asset_id: { type: string }
        purpose: { type: string }
        threshold: { type: integer }
        participants: { type: array, items: { type: integer } }
        signature_profile: { type: string, example: ecdsa-secp256k1-dkls23-v1 }
        chains: { type: array, items: { type: string } }
        client: { $ref: "#/components/schemas/ClientBinding" }
    CustodyKeyView:
      type: object
      properties:
        key_ref: { type: string }
        state: { type: string }
        key_id: { $ref: "#/components/schemas/Hex32" }
        public_key_artifact_ref: { type: string }
        active_epoch: { type: string }
        threshold: { type: integer }
        participants: { type: array, items: { type: integer } }
        latest_operation_id: { $ref: "#/components/schemas/Hex32" }
    CustodySignRequest:
      type: object
      required: [idempotency_key, asset_id, action, chain_id, request_digest, typed_data_root, requested_participants, publication_mode, client]
      properties:
        idempotency_key: { type: string }
        asset_id: { type: string }
        action: { type: string, example: transfer }
        chain_id: { type: string, example: ethereum-mainnet }
        request_digest: { $ref: "#/components/schemas/Hex32" }
        typed_data_root: { $ref: "#/components/schemas/Hex32" }
        requested_participants: { type: array, items: { type: integer } }
        epoch_fence_token_hex: { type: [string, "null"] }
        publication_mode: { type: string, enum: [manual, automatic] }
        client: { $ref: "#/components/schemas/ClientBinding" }
        public_fields:
          type: object
          additionalProperties: { type: string }
    CustodyKeyRefreshRequest:
      type: object
      required: [target_epoch, reason, publication_mode, client]
      properties:
        idempotency_key: { type: [string, "null"] }
        target_epoch: { type: string }
        reason: { type: string }
        publication_mode: { type: string, enum: [manual, automatic] }
        client: { $ref: "#/components/schemas/ClientBinding" }
        metadata: { type: [object, "null"], additionalProperties: { type: string } }
    CustodyKeyReshareRequest:
      allOf:
        - $ref: "#/components/schemas/CustodyKeyRefreshRequest"
        - type: object
          required: [new_participants, threshold]
          properties:
            new_participants: { type: array, items: { type: integer } }
            threshold: { type: integer }
    CustodyKeyRotateRequest:
      allOf:
        - $ref: "#/components/schemas/CustodyKeyRefreshRequest"
        - type: object
          required: [new_key_ref, participants, threshold]
          properties:
            new_key_ref: { type: string }
            participants: { type: array, items: { type: integer } }
            threshold: { type: integer }
    CustodyKeyRecoverRequest:
      allOf:
        - $ref: "#/components/schemas/CustodyKeyRefreshRequest"
        - type: object
          required: [recovery_case_ref, recovering_participant, helper_participants, helper_share_cids]
          properties:
            recovery_case_ref: { type: string }
            recovering_participant: { type: integer }
            helper_participants: { type: array, items: { type: integer } }
            helper_share_cids: { type: array, items: { type: string } }
            expected_helper_admission_root: { type: [string, "null"] }
    CustodyOperationResponse:
      type: object
      properties:
        operation: { $ref: "#/components/schemas/CustodyOperationView" }
    CustodyOperationView:
      allOf:
        - $ref: "#/components/schemas/OperationView"
        - type: object
          properties:
            operation_type:
              type: string
              enum: [key_create, sign, refresh, reshare, rotate, recover]
            key_ref: { type: string }
            asset_id: { type: string }
            tenant_id: { type: string }
            created_at_ms: { type: integer }
            updated_at_ms: { type: integer }
            target_key_ref: { type: [string, "null"] }
            target_epoch_hex: { type: [string, "null"] }
            target_threshold: { type: [integer, "null"] }
            target_participants: { type: array, items: { type: integer } }
    CustodyExternalExportRequest:
      type: object
      required: [publication_receipt, rail, export_ref, state, external_id, exported_at_ms]
      properties:
        publication_receipt: { type: string }
        rail: { type: string }
        export_ref: { $ref: "#/components/schemas/Hex32" }
        state: { type: string, enum: [Submitted, Accepted, Settled] }
        external_id: { type: string }
        exported_at_ms: { type: integer }
    CallContext:
      type: object
      description: Bind context for node-level calls; see the Node API reference for field semantics.
      required: [execution_profile, step, operation_id, key_id, session_id, epoch_id, phase, slot, request_digest]
      properties:
        execution_profile:
          type: object
          properties:
            domain_id: { type: string }
            profile_id: { type: string }
            alg_id: { type: string }
            shape_id: { type: string }
            family_id: { type: string }
        step:
          type: object
          properties:
            op_class: { type: string }
            step_seq: { type: integer }
            admission_hash: { $ref: "#/components/schemas/Hex32" }
            intent_hash: { $ref: "#/components/schemas/Hex32" }
            prev_transition_hash: { $ref: "#/components/schemas/Hex32" }
        operation_id: { $ref: "#/components/schemas/Hex32" }
        key_id: { $ref: "#/components/schemas/Hex32" }
        session_id: { $ref: "#/components/schemas/Hex32" }
        epoch_id: { $ref: "#/components/schemas/Hex32" }
        lane: { type: [integer, "null"] }
        phase: { type: string }
        slot: { type: string }
        request_digest: { $ref: "#/components/schemas/Hex32" }
    NodeOperationSubmitRequest:
      type: object
      required: [intent, client_binding_root, submitted_at_ms]
      properties:
        intent:
          type: object
          properties:
            version: { type: integer }
            operation_id: { $ref: "#/components/schemas/Hex32" }
            operation_kind: { type: string, example: sign_digest }
            key_id: { $ref: "#/components/schemas/Hex32" }
            tenant_root: { type: [string, "null"] }
            session_id: { $ref: "#/components/schemas/Hex32" }
            epoch_id: { $ref: "#/components/schemas/Hex32" }
            request_digest: { $ref: "#/components/schemas/Hex32" }
            business_intent_root: { $ref: "#/components/schemas/Hex32" }
            public_fields_root: { $ref: "#/components/schemas/Hex32" }
            requested_participants: { type: array, items: { type: integer } }
            publication_mode: { type: string }
            signature_profile: { type: string }
            hash_profile: { type: string }
            canonicalization_profile: { type: string }
        client_binding_root: { $ref: "#/components/schemas/Hex32" }
        subject_root: { type: [string, "null"] }
        submitted_at_ms: { type: integer }
    NodeAdmissionDecisionRequest:
      type: object
      required: [decision, domain_manifest, authority_set, policy_bundle, decided_at_ms]
      properties:
        decision: { type: object, description: Signed AdmissionDecision from the Policy Authority. }
        domain_manifest: { type: object }
        authority_set: { type: object }
        policy_bundle: { type: object }
        decided_at_ms: { type: integer }
        now_ms: { type: [integer, "null"] }
