openapi: 3.1.0
jsonSchemaDialect: https://spec.openapis.org/oas/3.1/dialect/base
info:
  title: FermiHDI Storage Node (SN) API
  description: 'Command interface for the FermiHDI Storage Node.


    Security:

    - Control Plane endpoints require Mutual TLS (mTLS) with certificates signed by
    the FermiHDI Root CA.

    - Observability Plane endpoints do not use TLS.

    '
  version: 1.1.0
servers:
- url: https://{sn_host}:6986
  description: Control Plane (mTLS) for system operations.
  variables:
    sn_host:
      default: localhost
- url: http://{sn_host}:8080
  description: Observability Plane (unencrypted) for kubernetes probes and prometheus
    metrics.
  variables:
    sn_host:
      default: localhost
paths:
  /v1/sn/config:
    post:
      summary: Push Configuration Update
      description: 'Endpoint for the DMS to push configuration updates to the Storage
        Node.

        This corresponds to the `snConfigPush` webhook in the DMS Southbound API.
        
        During Phase 2 startup, DMS pushes this endpoint to inject the `scc_ip` and `scc_port`.
        '
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NodeConfig'
      responses:
        '202':
          description: Configuration update accepted
        '400':
          description: Invalid configuration payload
  /v1/storage/reset_pointers:
    post:
      summary: Reset Storage Pointers
      description: "Wipes the KV mapping and resets the storage write head to a target\
        \ batch. \nUsed for self-healing and cluster re-synchronization.\n"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - target_batch_id
              properties:
                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: Pointers reset successfully
  /v1/storage/sync_to_peer:
    post:
      summary: Sync Batches to Peer
      description: 'Triggers the node to stream a range of batches to a peer node.

        '
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - target_batch_id
              - peer_ip
              - peer_port
              properties:
                target_batch_id:
                  type: string
                  minLength: 128
                  maxLength: 128
                  pattern: ^[a-fA-F0-9]{128}$
                  description: 128 character hex encoded 64 byte array
                peer_ip:
                  type: string
                peer_port:
                  type: integer
      responses:
        '200':
          description: Sync accepted and background thread started
  /v1/system_info:
    get:
      summary: Get Operational Status
      description: Simple health check of the control plane.
      responses:
        '200':
          description: Node is online
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
  /v1/admin/shutdown:
    post:
      summary: Graceful Shutdown Command
      description: Flags the Node to forcefully finish saving its active batch and
        exit processing.
      responses:
        '202':
          description: Shutdown flagged
  /v1/lifecycle:
    post:
      summary: Trigger Lifecycle Event
      description: General endpoint for lifecycle management query and state transitions.
      responses:
        '200':
          description: Lifecycle event triggered
  /v1/batch/history:
    post:
      summary: Get Batch History
      description: Retrieves the history of batches stored on the node.
      responses:
        '200':
          description: Batch history retrieved
  /v1/query:
    post:
      summary: Execute Data 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,
        which reaches the node's hot path rather than this process.

        The route answers 410 rather than being deleted: this node's REST query
        path reads a store that no longer receives ingest writes, so it could
        only ever have answered "no data" — indistinguishable, to a caller, from
        a query that legitimately matched nothing.

        Cancellation is unaffected: POST /v1/query/stop is still served here.
      responses:
        '410':
          description: Always. Use QueryDispatchService/DispatchQueryStream instead.
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    example: POST /v1/query is retired
                  detail:
                    type: string
                  use:
                    type: string
                    example: fermihdi.controlplane.v1.QueryDispatchService/DispatchQueryStream
  /v1/query/stop:
    post:
      summary: Abort a Query
      description: |
        Aborts an in-flight query on this node, so it stops reading from disk
        for that query id.

        Sent by the Storage Cluster Controller over the control plane, and
        protected by mTLS like every other endpoint here.

        Unknown query ids are accepted rather than rejected: a query that has
        already finished is indistinguishable from one that never existed, and
        both mean the same thing to the caller.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - query_id
              properties:
                query_id:
                  type: integer
                  minimum: 0
                  description: Unsigned 32-bit id of the query to abort.
      responses:
        '200':
          description: The abort was applied.
          content:
            application/json:
              schema:
                type: object
                properties:
                  status:
                    type: string
                    example: stopped
                  query_id:
                    type: integer
        '400':
          description: query_id was missing or was not an unsigned integer.
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    example: Missing or invalid 'query_id'
                  status:
                    type: string
                    example: rejected
        '500':
          description: The request body could not be parsed.
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                  status:
                    type: string
                    example: error
  /healthz:
    get:
      summary: Health Check Probe
      description: Unencrypted health check for Kubernetes liveness and readiness
        probes.
      responses:
        '200':
          description: Node is healthy
  /metrics:
    get:
      summary: Prometheus Metrics
      description: Exposes Prometheus-compatible metrics for the node.
      responses:
        '200':
          description: Metrics retrieved successfully
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
  ctrlSnOnline:
    post:
      summary: Report Online to Controller
      description: Expected to call the Storage Cluster Controller to notify that
        node is active (URL Path string -> `/v1/internal/sn/online`).
      responses:
        '200':
          description: Online state accepted
  ctrlSnOffline:
    post:
      summary: Report Offline to Controller
      description: Expected to call the Storage Cluster Controller during shutdown
        (URL Path string -> `/v1/internal/sn/offline`).
      responses:
        '200':
          description: Offline state accepted
  ctrlBatchComplete:
    post:
      summary: Submit Batch Completion
      description: Expected to call the SCC to submit a completed batch seal (URL
        Path string -> `/v1/batch/complete`).
      responses:
        '200':
          description: Batch seal accepted
  ctrlSnFault:
    post:
      summary: Report Storage Fault
      description: Expected to call the SCC when an I/O or system fault is detected
        (URL Path string -> `/v1/sn/fault`).
      responses:
        '200':
          description: Fault accepted
  ctrlSyncCaughtUp:
    post:
      summary: Report Sync Caught Up
      description: Expected to call the SCC when peer background synchronization concludes
        (URL Path string -> `/v1/sn/sync_caught_up`).
      responses:
        '200':
          description: Synchronized state accepted
  telemetryHeartbeat:
    post:
      summary: Emit Telemetry Heartbeat
      description: Expected to call the central telemetry or SCC with periodic health
        and metrics updates (URL Path string -> `/v1/telemetry/heartbeat`).
      responses:
        '200':
          description: Telemetry data accepted
components:
  schemas:
    NodeConfig:
      type: object
      description: Full Storage Node configuration payload.
      properties:
        node_name:
          type: string
        dms_url:
          type: string
        scc_ip:
          type: string
        scc_port:
          type: integer
        log_level:
          type: string
        drive_strategy:
          type: string
        drive_full_action:
          type: string
        storage_devices:
          type: array
          items:
            type: object
            properties:
              name:
                type: string
              address:
                type: string
              type:
                type: string
