openapi: 3.1.0
jsonSchemaDialect: https://spec.openapis.org/oas/3.1/dialect/base
info:
  title: FermiHDI Storage Cluster Controller (SCC) API
  description: "API for the Storage Cluster Controller (SCC). \nThe SCC coordinates\
    \ queries and ingestion within a Storage Cluster.\n\nSecurity:\n- All endpoints\
    \ require Mutual TLS (mTLS) with certificates signed by the FermiHDI Root CA.\n"
  version: 1.0.0
servers:
- url: https://{scc_host}:6986
  variables:
    scc_host:
      default: localhost
paths:
  /v1/query:
    post:
      summary: Submit Analytic Query (retired)
      deprecated: true
      description: |
        Retired on 2026-08-09, when the data plane was promoted and query
        dispatch moved to the gRPC
        `fermihdi.controlplane.v1.QueryDispatchService/DispatchQueryStream` RPC.

        The route answers 410 rather than being deleted, because its success
        path returned 200 with an empty found-batches list when nothing matched
        — a caller could not tell "the API is retired" apart from "no data". It
        would also have fanned queries out over a REST path the Storage Nodes no
        longer serve, and reported success for work that never happened.
      responses:
        '410':
          description: Always. Use QueryDispatchService/DispatchQueryStream instead.
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    example: POST /v1/query is retired on the SCC
                  detail:
                    type: string
                  use:
                    type: string
                    example: fermihdi.controlplane.v1.QueryDispatchService/DispatchQueryStream
  /v1/query/stop:
    post:
      summary: Stop a Query (retired)
      deprecated: true
      description: |
        Retired on 2026-08-09, when the data plane was promoted and query
        cancellation moved to the unary gRPC RPC
        `fermihdi.controlplane.v1.QueryDispatchService/CancelQuery`, which an
        SCC both serves and forwards to every Storage Node it fanned the query
        out to.

        The route answers 410 rather than being deleted, because its success
        path returned a result a caller could not tell apart from "that query
        had already finished". Deleting it would have let a retired API look
        like a completed cancellation. The request body is ignored.

        Callers still POSTing here are not cancelling anything.
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                query_id:
                  type: integer
                  minimum: 0
                  description: Accepted by the retired route and ignored.
      responses:
        '410':
          description: Always. Use QueryDispatchService/CancelQuery instead.
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    example: POST /v1/query/stop is retired on the SCC
                  detail:
                    type: string
                  use:
                    type: string
                    example: fermihdi.controlplane.v1.QueryDispatchService/CancelQuery
  /v1/lifecycle:
    post:
      summary: Update Node State
      description: Updates the operational state of the SCC.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                action:
                  type: string
                  enum:
                  - ONLINE
                  - REBALANCING
                  - FAULTY
    get:
      summary: Get Node State
      responses:
        '200':
          description: Current state
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  state:
                    type: string
  /v1/scc/config:
    post:
      summary: Push Configuration Update
      description: 'Endpoint for the DMS to push configuration updates to the SCC.

        This corresponds to the `sccConfigPush` webhook in the DMS Southbound API.

        DMS pushes this endpoint with the full node configuration including sn_targets.
        On first receipt of a non-empty sn_targets, the SCC completes Phase 2 startup
        and reports StatusFullyReady to DMS via POST /v1/nodes/{node_id}/ready.
        '
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NodeConfig'
      responses:
        '202':
          description: Configuration update accepted
        '400':
          description: Invalid configuration payload
  /v1/sn/fault:
    post:
      summary: Report Storage Node Fault
      description: Triggered by an SN or monitor when a fault/mismatch is detected.
        Initiates self-healing.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - node_name
              - target_batch_id
              properties:
                node_name:
                  type: string
                target_batch_id:
                  type: string
                  minLength: 128
                  maxLength: 128
                  pattern: ^[a-fA-F0-9]{128}$
                  description: 128 character hex encoded 64 byte array
      responses:
        '200':
          description: Healing initiated
  /v1/batch/complete:
    post:
      summary: Report Batch Completion
      description: SNs report their calculated hash for a batch to the SCC for validation.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - node_name
              - batch_id
              - ending_hash
              properties:
                node_name:
                  type: string
                batch_id:
                  type: string
                  minLength: 128
                  maxLength: 128
                  pattern: ^[a-fA-F0-9]{128}$
                  description: 128 character hex encoded 64 byte array
                ending_hash:
                  type: string
      responses:
        '200':
          description: Validation successful or fault initiated
  /healthz:
    get:
      summary: Health Check
      responses:
        '200':
          description: OK
  /livez:
    get:
      summary: Liveness Probe
      responses:
        '200':
          description: OK
  /readyz:
    get:
      summary: Readiness Probe
      responses:
        '200':
          description: OK
  /metrics:
    get:
      summary: Prometheus Metrics
      responses:
        '200':
          description: Metrics text payload
  /v1/pki/status:
    get:
      summary: Get PKI/Certificate Status
      description: Returns the current certificate serial number, expiration date,
        and mTLS status.
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                  cert_serial:
                    type: string
                  expiration:
                    type: string
                    format: date-time
                  mtls_active:
                    type: boolean
  /v1/pki/rotate:
    post:
      summary: Trigger Certificate Rotation
      description: 'Triggers the node to generate a new CSR and initiate a Phase 1
        registration update with the DMS.

        The node will reload its mTLS context upon completion.

        '
      responses:
        '200':
          description: Rotation initiated successfully
        '500':
          description: Rotation failed or DMS unavailable
  /v1/sn/sync_caught_up:
    post:
      summary: Sync Caught Up
      description: Internal webhook for self-healing complete.
      responses:
        '200':
          description: marked complete
  /v1/internal/sn/offline:
    post:
      summary: Unregister SN Routing
      description: Webhook received from a child SN declaring it has committed its
        EOB and is exiting. Remouts routing locally.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - node_name
              properties:
                node_name:
                  type: string
      responses:
        '200':
          description: Path cleared
  /v1/internal/sn/online:
    post:
      summary: Register SN Routing
      description: Webhook received from a new child SN mapping its IP into the dispatcher.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - node_name
              - port
              properties:
                node_name:
                  type: string
                port:
                  type: integer
      responses:
        '200':
          description: Path registered
  /v1/admin/shutdown:
    post:
      summary: Graceful Shutdown Command
      description: Flags the SCC to stop ingress, flush EOB to SNs, and securely disconnect.
        Usually pushed mechanically by DMS.
      responses:
        '202':
          description: Shutdown flagged
webhooks:
  dmsRegister:
    post:
      summary: Register with DMS
      description: Expected to call the DMS Registration Endpoint (URL Path string
        -> `/v1/register` or `/v1/dms/register`).
      responses:
        '200':
          description: Registration accepted
  dmsConfigStatus:
    post:
      summary: Report Config Status to DMS
      description: Expected to call the DMS Config Status Endpoint (URL Path string
        -> `/v1/config/{node_name}/status`).
      responses:
        '200':
          description: Status accepted
  dmsTopologyOffline:
    post:
      summary: Report Offline to DMS
      description: Expected to call the DMS Topology Offline Endpoint during shutdown
        (URL Path string -> `/v1/topology/offline`).
      responses:
        '200':
          description: Offline report accepted
  sccAlertDms:
    post:
      summary: Emit Backpressure Alerts
      description: Expected to call the DMS to alert upstreams regarding TCP Window
        saturation (URL Path string -> `/v1/alerts/scc`).
      responses:
        '200':
          description: Alert acknowledged
  sccConfigPush:
    post:
      summary: SCC Configuration Update (Pushed by DMS)
      description: 'This is the webhook root object identifying that the DMS pushes
        config updates to this node.

        '
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NodeConfig'
      responses:
        '202':
          description: Accepted
components:
  schemas:
    QueryRequest:
      type: object
      required:
      - query_id
      properties:
        query_id:
          type: integer
        sql:
          type: string
        batch_ids:
          type: array
          items:
            type: string
        sub_queries:
          type: array
          items:
            type: object
    NodeConfig:
      type: object
      properties:
        record_size:
          type: integer
        max_hash_queue_bytes:
          type: integer
        batch_timeout_ms:
          type: integer
        timeout_action:
          type: string
          enum:
          - EOB
          - BBN
        sn_targets:
          type: string
          description: Comma-separated SN list (name:ip:cmd_port:prod_port)
        log_level:
          type: string
          enum:
          - DEBUG
          - INFO
          - WARN
          - ERROR
