openapi: 3.0.3
info:
  title: Web3CDN Data Trust API
  description: |
    Historical-state data trust for RPC verifiers. Reference nodes vote on canonical
    RPC results at a ladder of block heights; the winning value becomes the published
    etalon (`expect_hex`). Clients fetch rules, run the same JSON-RPC calls against
    their own nodes, and compare responses.

    **Production base URL:** `https://dataproof.web3cdn.network`

    ## Authentication

    The **catalog** (`/v1/status`, `/v1/catalog`, `/v1/chains…`) is **public**. No API key.

    Admin (`/admin/v1/…`) still requires a DataProof admin key (`X-API-Key`).
  version: "1.2.0"
servers:
  - url: https://dataproof.web3cdn.network
    description: Production DataProof API
  - url: /
    description: This API instance
tags:
  - name: catalog
    description: Discovery — families, protocols, suites, chains
  - name: probes
    description: Published etalon rules and vote history
paths:
  /version:
    get:
      tags: [catalog]
      summary: Build version of this API process
      description: Jenkins `APP_VERSION` baked into the image. No API key.
      security: []
      responses:
        "200":
          description: Version
          content:
            application/json:
              schema:
                type: object
                properties:
                  service: { type: string, example: web3cdn-datatruth }
                  version: { type: string, example: "1842" }
  /v1/status:
    get:
      tags: [catalog]
      summary: Compact status (chain count)
      security: []
      responses:
        "200":
          description: Status
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok: { type: boolean }
                  chains: { type: integer }
        "401":
          description: Missing or invalid API key
  /v1/catalog:
    get:
      tags: [catalog]
      summary: Supported families, protocols, and verification suites
      description: |
        Static discovery tree. Use before fetching probes — each suite maps to one
        JSON-RPC method (e.g. `archive_balance` → `eth_getBalance`).
      security: []
      responses:
        "200":
          description: Catalog
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CatalogResponse"
        "401":
          description: Missing or invalid API key
  /v1/chains:
    get:
      tags: [catalog]
      summary: List configured chains
      description: |
        Optional filters: `family`, `protocol`, `suite` (or first entry of `suites` CSV).
        Returns enabled suites and probe counts per chain.
      security: []
      parameters:
        - $ref: "#/components/parameters/Family"
        - $ref: "#/components/parameters/Protocol"
        - name: suite
          in: query
          schema: { type: string, example: archive_balance }
        - name: suites
          in: query
          description: Comma-separated; only the first value is used as a chain filter.
          schema: { type: string, example: "archive_balance,get_block" }
      responses:
        "200":
          description: Chain list
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ChainListResponse"
        "401":
          description: Missing or invalid API key
  /v1/chains/{chain_id}:
    get:
      tags: [catalog]
      summary: Chain metadata and enabled suites
      security: []
      parameters:
        - $ref: "#/components/parameters/ChainID"
        - $ref: "#/components/parameters/IncludeCatalog"
      responses:
        "200":
          description: Chain detail
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ChainDetailResponse"
        "404":
          description: Chain not found
        "401":
          description: Missing or invalid API key
  /v1/chains/{chain_id}/probes:
    get:
      tags: [probes]
      summary: Published probe set (etalon rules)
      description: |
        Returns probes for one or more suites. Default suite is `archive_balance`.
        Response contains **only** the requested suite(s) — not the full suite catalog
        (use `GET /v1/chains/{chain_id}` or `?include=catalog`).

        **Multiple suites:** repeat `suite=` or use `suites=archive_balance,get_block`.
        Multiple suites return a `suites` map; a single suite returns a flat `ProbeSet`.
      security: []
      parameters:
        - $ref: "#/components/parameters/ChainID"
        - $ref: "#/components/parameters/Suite"
        - $ref: "#/components/parameters/Suites"
        - $ref: "#/components/parameters/IncludeCatalog"
        - name: block
          in: query
          schema: { type: integer }
        - name: blocks
          in: query
          description: Comma-separated block heights.
          schema: { type: string, example: "1,100,1000000" }
        - name: address
          in: query
          schema: { type: string }
      responses:
        "200":
          description: Probe set (single or multi-suite)
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: "#/components/schemas/ProbeSetResponse"
                  - $ref: "#/components/schemas/MultiSuiteProbeResponse"
        "400":
          description: Invalid suite or filter
        "404":
          description: Chain or probes not found
        "401":
          description: Missing or invalid API key
  /v1/chains/{chain_id}/probes/votes:
    get:
      tags: [probes]
      summary: Historical votes for accepted amounts
      description: |
        Who voted for (or against) the chosen etalon hex. Optional `block` and
        `address` filter a single probe. Rows are append-only historical trust.
      security: []
      parameters:
        - $ref: "#/components/parameters/ChainID"
        - name: block
          in: query
          schema: { type: integer }
        - name: address
          in: query
          schema: { type: string }
        - name: limit
          in: query
          schema: { type: integer, default: 200 }
      responses:
        "200":
          description: Vote ledger
        "401":
          description: Missing or invalid API key
  /v1/{family}/{protocol}/chains/{chain_id}/suites/{suite}/probes:
    get:
      tags: [probes]
      summary: Probe set (explicit family/protocol/suite path)
      description: |
        Same payload as `GET /v1/chains/{chain_id}/probes?suite=…`.
        Returns **404** when `family` or `protocol` does not match the chain row.
      security: []
      parameters:
        - name: family
          in: path
          required: true
          schema: { type: string, example: evm }
        - name: protocol
          in: path
          required: true
          schema: { type: string, example: jsonrpc }
        - $ref: "#/components/parameters/ChainID"
        - name: suite
          in: path
          required: true
          schema: { type: string, example: archive_balance }
        - $ref: "#/components/parameters/IncludeCatalog"
        - name: block
          in: query
          schema: { type: integer }
        - name: blocks
          in: query
          schema: { type: string }
        - name: address
          in: query
          schema: { type: string }
      responses:
        "200":
          description: Probe set
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProbeSetResponse"
        "404":
          description: Chain, family/protocol mismatch, or no probes
        "401":
          description: Missing or invalid API key
        "501":
          description: Suite not implemented
components:
  securitySchemes:
    Web3CDNApiKey:
      type: apiKey
      in: header
      name: X-API-Key
      description: |
        Your Web3CDN RPC API key. Same value works as:
        `Authorization: Bearer <key>` or query `?api_key=<key>`.
        Use one method per request — not all at once.
  parameters:
    ChainID:
      name: chain_id
      in: path
      required: true
      schema: { type: string, example: "0x1" }
    Family:
      name: family
      in: query
      schema: { type: string, example: evm }
    Protocol:
      name: protocol
      in: query
      schema: { type: string, example: jsonrpc }
    Suite:
      name: suite
      in: query
      description: Repeat for multiple suites.
      schema: { type: string, example: archive_balance }
    Suites:
      name: suites
      in: query
      description: Comma-separated suite IDs.
      schema: { type: string, example: "archive_balance,get_block" }
    IncludeCatalog:
      name: include
      in: query
      description: Set to `catalog` to include `verification_suites` in the response.
      schema: { type: string, example: catalog }
  schemas:
    CatalogResponse:
      type: object
      properties:
        ok: { type: boolean }
        base_url: { type: string }
        families:
          type: array
          items:
            $ref: "#/components/schemas/FamilyInfo"
    FamilyInfo:
      type: object
      properties:
        id: { type: string }
        protocols:
          type: array
          items:
            $ref: "#/components/schemas/ProtocolInfo"
    ProtocolInfo:
      type: object
      properties:
        id: { type: string }
        suites:
          type: array
          items:
            $ref: "#/components/schemas/SuiteInfo"
    SuiteInfo:
      type: object
      properties:
        id: { type: string }
        method: { type: string }
        label: { type: string }
        enabled: { type: boolean }
    ChainListResponse:
      type: object
      properties:
        ok: { type: boolean }
        chains:
          type: array
          items:
            $ref: "#/components/schemas/PublicChain"
    ChainDetailResponse:
      type: object
      properties:
        ok: { type: boolean }
        chain:
          $ref: "#/components/schemas/PublicChain"
        verification_suites:
          type: array
          items:
            $ref: "#/components/schemas/SuiteInfo"
    PublicChain:
      type: object
      properties:
        chain_id: { type: string }
        family: { type: string }
        protocol: { type: string }
        spec_id: { type: string }
        suites:
          type: array
          items:
            $ref: "#/components/schemas/PublicChainSuite"
    PublicChainSuite:
      type: object
      properties:
        id: { type: string }
        label: { type: string }
        method: { type: string }
        enabled: { type: boolean }
        probe_count: { type: integer }
    Probe:
      type: object
      properties:
        block: { type: integer }
        address: { type: string }
        expect_hex: { type: string }
        method: { type: string }
        rpc_account: { type: string }
        trust:
          $ref: "#/components/schemas/TrustSummary"
    TrustSummary:
      type: object
      properties:
        quorum_need: { type: integer }
        for: { type: integer }
        against: { type: integer }
        zero: { type: integer }
        errors: { type: integer }
        confirms: { type: integer }
        last_trained_at: { type: string, format: date-time }
        train_run_id: { type: integer }
    ProbeSet:
      type: object
      properties:
        family: { type: string }
        protocol: { type: string }
        suite: { type: string }
        chain_id: { type: string }
        generated_at: { type: string, format: date-time }
        trained_at: { type: string, format: date-time }
        tip_at_train: { type: integer }
        probes:
          type: array
          items:
            $ref: "#/components/schemas/Probe"
    ProbeSetResponse:
      allOf:
        - type: object
          properties:
            ok: { type: boolean }
        - $ref: "#/components/schemas/ProbeSet"
    MultiSuiteProbeResponse:
      type: object
      properties:
        ok: { type: boolean }
        chain_id: { type: string }
        family: { type: string }
        protocol: { type: string }
        suites:
          type: object
          additionalProperties:
            $ref: "#/components/schemas/ProbeSet"
