openapi: 3.1.0
info:
  title: ZERO Operator Runtime API
  version: 1.0.0-beta.1
  description: |
    Operator-authenticated `/v2/*` routes for live Runtime state. Distinct
    from the public-operator API: these surfaces require an operator
    token and return live engine state (runtime safety, data-plane
    divergence, setup lifecycles, Genesis evidence bundles).

    All routes are read-only. Mutating actions live under
    `/api/deployments/{id}/*` (separate OpenAPI surface).

    JSON schemas for response bodies are published as static contracts
    under `/contracts/zero.*.v1.schema.json` and referenced via
    `x-json-schemas` below.

    **Deployment status (as of v1.0.0-beta.1):**

    - `/v2/runtime-safety` — **shipping** on `main`
    - `/v2/data-plane-divergence` — **shipping** on `main`
    - `/v2/data-plane-divergence/history` — ships via PR #301
    - `/v2/setup-lifecycles` — ships via PR #303
    - `/v2/genesis/evidence` + `/v2/genesis/evidence/{cycle_id}` —
      ship via PR #300

    Schema files for non-shipped routes land with their respective PRs.
    Clients consuming this contract before all PRs merge should check
    response status — paths in this section may 404 until then.
externalDocs:
  description: Operator-facing JSON Schema artifacts.
  url: https://getzero.dev/contracts/
x-json-schemas:
  runtime_safety: https://getzero.dev/contracts/zero.runtime_safety.v1.schema.json
  hl_data_plane: https://getzero.dev/contracts/zero.hl_data_plane.v1.schema.json
  setup_lifecycle: https://getzero.dev/contracts/zero.setup_lifecycle.v1.schema.json
  genesis_evidence: https://getzero.dev/contracts/zero.genesis.evidence.v1.schema.json
servers:
  - url: https://getzero.dev
    description: Production app
  - url: http://127.0.0.1:8420
    description: Local engine
components:
  securitySchemes:
    operatorBearer:
      type: http
      scheme: bearer
      description: Operator JWT/token from `auth.register_token(...)` (see `zero/auth.py`).
  schemas:
    RuntimeSafetyState:
      type: object
      description: Aggregated engine safety signals — see /contracts/zero.runtime_safety.v1.schema.json.
      required: [schema, overall_status, overall_reasons]
      properties:
        schema:
          type: string
          const: zero.runtime_safety.v1
        checked_at:
          type: string
          format: date-time
        overall_status:
          type: string
          enum: [ok, degraded, halted]
        overall_reasons:
          type: array
          items: {type: string}
        account: {type: object}
        risk: {type: object}
        circuit_breaker: {type: object}
        market_data: {type: object}
        immune: {type: object}
        data_plane:
          type: object
          description: Latest divergence record + age_s freshness stamp.
        heartbeats: {type: object, additionalProperties: {type: object}}
        asset_index:
          type: object
          description: '{asset_id (str): coin_name} — engine''s HL universe map.'
          additionalProperties: {type: string}
    DataPlaneDivergenceRecord:
      type: object
      description: One snapshot from the divergence producer.
      required: [schema, result, primary]
      properties:
        schema:
          type: string
          const: zero.hl_data_plane_divergence.v1
        checked_at: {type: string, format: date-time}
        primary: {type: object}
        fallback: {oneOf: [{type: object}, {type: 'null'}]}
        result:
          type: object
          properties:
            status:
              type: string
              enum: [match, minor_drift, major_drift, unchecked]
            comparisons: {type: array}
        reason: {type: [string, 'null']}
        per_coin:
          type: object
          description: Present only when the fallback fetch succeeded.
          properties:
            portfolio_status:
              type: string
              enum: [match, minor_drift, major_drift, unchecked]
            major_drift_coins: {type: array, items: {type: string}}
            minor_drift_coins: {type: array, items: {type: string}}
            by_shape:
              type: object
              properties:
                positions: {type: array}
                open_orders: {type: array}
                fill_count: {type: array}
    DataPlaneDivergenceHistory:
      type: object
      required: [count, records]
      properties:
        count: {type: integer, minimum: 0}
        records:
          type: array
          items: {$ref: '#/components/schemas/DataPlaneDivergenceRecord'}
    SetupLifecycleSnapshot:
      type: object
      description: Per-(coin, direction) unified lifecycle — see /contracts/zero.setup_lifecycle.v1.schema.json.
      required: [schema, window_hours, count, lifecycles]
      properties:
        schema:
          type: string
          const: zero.setup_lifecycle.v1
        built_at: {type: [string, 'null'], format: date-time}
        window_hours: {type: number}
        count: {type: integer, minimum: 0}
        lifecycles: {type: array, items: {type: object}}
    GenesisEvidenceBundle:
      type: object
      description: One Genesis cycle's machine-readable evidence — see /contracts/zero.genesis.evidence.v1.schema.json.
      required: [schema, cycle_id, status, terminal, stages]
      properties:
        schema:
          type: string
          const: zero.genesis.evidence.v1
        cycle_id: {type: string}
        proposal_id: {type: string}
        first_event_at: {type: [string, 'null'], format: date-time}
        last_event_at: {type: [string, 'null'], format: date-time}
        event_count: {type: integer, minimum: 0}
        schema_violations: {type: integer, minimum: 0}
        status:
          type: string
          enum: [empty, errored, rolled_back, promoted,
                 'in_progress:init', 'in_progress:proposal',
                 'in_progress:guardian', 'in_progress:build',
                 'in_progress:red_team', 'in_progress:canary',
                 'in_progress:calibration']
        terminal: {type: boolean}
        stages: {type: object}
    GenesisEvidenceIndex:
      type: object
      required: [bundles, count]
      properties:
        count: {type: integer, minimum: 0}
        bundles:
          type: array
          items:
            type: object
            required: [cycle_id, status, terminal]
            properties:
              cycle_id: {type: string}
              status: {type: string}
              terminal: {type: boolean}
              last_event_at: {type: [string, 'null'], format: date-time}
              proposal_id: {type: string}
              event_count: {type: integer}
    Error:
      type: object
      required: [error]
      properties:
        error: {type: string}
        detail: {type: string}
security:
  - operatorBearer: []
paths:
  /v2/runtime-safety:
    get:
      operationId: getRuntimeSafety
      summary: Aggregated runtime safety state.
      description: |
        Joins risk, immune, circuit breaker, market data, per-daemon
        heartbeats, and data-plane divergence into one schemaed object
        with per-component freshness metadata.

        Overall status:
          ok       — all signals fresh, no halts
          degraded — freshness violation or minor divergence drift
          halted   — explicit halt or data-plane MAJOR drift
      responses:
        '200':
          description: Current safety state.
          content:
            application/json:
              schema: {$ref: '#/components/schemas/RuntimeSafetyState'}
        '401':
          description: Missing or invalid operator token.
          content: {application/json: {schema: {$ref: '#/components/schemas/Error'}}}

  /v2/data-plane-divergence:
    get:
      operationId: getDataPlaneDivergence
      summary: Latest HL data-plane divergence record.
      description: |
        Reports how the engine's cached HL state compares against the
        configured fallback source on positions, open orders, account
        value, and fill counts. Returns 'unchecked' when no fallback
        is configured.
      responses:
        '200':
          description: Latest record (canonical 'unchecked' shape when no record exists).
          content:
            application/json:
              schema: {$ref: '#/components/schemas/DataPlaneDivergenceRecord'}

  /v2/data-plane-divergence/history:
    get:
      operationId: getDataPlaneDivergenceHistory
      summary: Rolling divergence-record history.
      description: |
        Returns recent divergence records oldest-first for trend
        analysis ("has this been drifting all week?"). Rolling jsonl
        capped at ~3.5 days at 60s producer cadence.
      parameters:
        - name: limit
          in: query
          required: false
          schema: {type: integer, minimum: 1, maximum: 10000, default: 200}
      responses:
        '200':
          description: History records, oldest-first.
          content:
            application/json:
              schema: {$ref: '#/components/schemas/DataPlaneDivergenceHistory'}

  /v2/setup-lifecycles:
    get:
      operationId: getSetupLifecycles
      summary: Unified per-(coin, direction) lifecycle.
      description: |
        Folds approaching, near-miss, rejection, and trade streams into
        one record per (coin, direction). Returns the last persisted
        snapshot by default; pass refresh=true to recompute from source
        streams (more expensive).
      parameters:
        - name: window_hours
          in: query
          required: false
          schema: {type: number, default: 24}
        - name: refresh
          in: query
          required: false
          schema: {type: boolean, default: false}
      responses:
        '200':
          description: Current lifecycle snapshot.
          content:
            application/json:
              schema: {$ref: '#/components/schemas/SetupLifecycleSnapshot'}

  /v2/genesis/evidence:
    get:
      operationId: listGenesisEvidence
      summary: Recent Genesis evidence bundles.
      description: Index endpoint. Newest first.
      parameters:
        - name: limit
          in: query
          required: false
          schema: {type: integer, minimum: 1, maximum: 200, default: 50}
      responses:
        '200':
          description: Recent bundles index.
          content:
            application/json:
              schema: {$ref: '#/components/schemas/GenesisEvidenceIndex'}

  /v2/genesis/evidence/{cycle_id}:
    get:
      operationId: getGenesisEvidenceBundle
      summary: One Genesis cycle's evidence bundle.
      parameters:
        - name: cycle_id
          in: path
          required: true
          schema: {type: string}
      responses:
        '200':
          description: The bundle.
          content:
            application/json:
              schema: {$ref: '#/components/schemas/GenesisEvidenceBundle'}
        '404':
          description: No bundle for that cycle_id.
          content: {application/json: {schema: {$ref: '#/components/schemas/Error'}}}
