openapi: 3.1.1
info:
  title: FermiHDI DMS Southbound API
  description: |
    API for communication between HD Nodes and the Data Management System (DMS).
    
    Security Model:
    1. **Registration (Phase 1)**: Standard HTTPS. Nodes trust the DMS Root CA.
    2. **Telemetry/Config (Phase 2)**: Mutual TLS (mTLS). Nodes must present a certificate signed during Phase 1.
  version: 1.1.2
servers:
  - url: https://{dms_host}:6986
    variables:
      dms_host:
        default: dms.fermihdi.local

paths:
  /v1/register:
    post:
      summary: Node Registration (Phase 1)
      description: Exchanges a CSR for a signed mTLS certificate. Requires trust in FermiHDI Root CA.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RegistrationRequest'
      responses:
        '200':
          description: Successful registration
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RegistrationResponse'
        '202':
          description: Registration pending manual admin approval
          content:
            application/json:
              schema:
                type: object
                properties:
                  status: { type: string, enum: [pending_approval] }
        '403':
          description: Forbidden - token invalid or node not provisioned

  /v1/telemetry/heartbeat:
    post:
      summary: Heartbeat & Telemetry Reporting (Phase 2)
      description: Periodic telemetry update. Requires mTLS.
      security:
        - mtls: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NodeTelemetry'
      responses:
        '202':
          description: Telemetry accepted

  /v1/heartbeat:
    post:
      summary: Simple Keep-alive
      description: Keeps the node session alive without sending metrics. Requires mTLS.
      security:
        - mtls: []
      responses:
        '204':
          description: Heartbeat recorded

  /v1/config/{nodeId}:
    get:
      summary: Retrieve Node Configuration
      description: Fetches specialized control parameters for the node. Requires mTLS.
      parameters:
        - name: nodeId
          in: path
          required: true
          schema:
            type: string
      security:
        - mtls: []
      responses:
        '200':
          description: Configuration payload
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NodeConfig'

  /v1/dataset/schema:
    get:
      summary: Get Dataset Schema
      description: Returns the bit-level dataset schema for the requester's HD Instance. Requires mTLS.
      security:
        - mtls: []
      responses:
        '200':
          description: Dataset schema
          content:
            application/json:
              schema: { type: object }

  /v1/client/info:
    get:
      summary: Get Combined Client Context
      description: Returns the instance ID, schema, and current SCC routing targets. Requires mTLS.
      security:
        - mtls: []
      responses:
        '200':
          description: Combined info
          content:
            application/json:
              schema:
                type: object
                properties:
                  instance_id: { type: string }
                  schema: { type: object }
                  scc_targets:
                    type: object
                    additionalProperties:
                      type: array
                      items: { type: string }

  /v1/config/{nodeId}/status:
    post:
      summary: Report Configuration Sync Status
      description: Allows a node to report whether the latest config and PBAC policies applied successfully or failed. Requires mTLS.
      parameters:
        - name: nodeId
          in: path
          required: true
          schema:
            type: string
      security:
        - mtls: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ConfigSyncStatus'
      responses:
        '202':
          description: Status accepted

  /v1/nodes/{node_id}/ready:
    post:
      summary: Node Phase 2 readiness self-report
      description: >
        Called by a node after completing Phase 2 startup. Promotes the node to
        FullyReady status and triggers topology-aware cascade config pushes.
      parameters:
        - name: node_id
          in: path
          required: true
          schema: { type: string }
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                node_id: { type: string }
      responses:
        '202': { description: Status accepted }
        '404': { description: Node not registered }

  /v1/topology/offline:
    post:
      summary: Report Node Offline
      description: Notifies the DMS that an SN or SCC is gracefully shutting down after its current batch.
      security:
        - mtls: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [node_id, role]
              properties:
                node_id: { type: string }
                role: { type: string, enum: [SN, SCC] }
                active_batches_draining: { type: integer }
      responses:
        '202':
          description: Offline transition recorded


webhooks:
  proxyConfigPush:
    post:
      summary: Proxy Configuration Update (WebHook/Push)
      description: |
        Pushed by the DMS to a registered **hd_proxy** node when its routing context changes. 
        **Endpoint Path**: `POST /v1/proxy/config`
      security:
        - mtls: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NodeConfig'
      responses:
        '202':
          description: Configuration update received

  shutdownPush:
    post:
      summary: Node Graceful Shutdown Command (WebHook/Push)
      description: |
        Pushed by the DMS to a highly-available node to instruct it to cleanly finish its active batch and exit.
        **Expected Target URL**: `POST /v1/admin/shutdown` (or configured admin interface)
      security:
        - mtls: []
      responses:
        '202':
          description: Shutdown initiated

  sccConfigPush:
    post:
      summary: SCC Configuration Update (WebHook/Push)
      description: |
        Pushed by the DMS to a **Storage Cluster Controller (SCC)** when an SN in its cluster changes status (Online/Offline).
        **Endpoint Path**: `POST /v1/scc/config`
      security:
        - mtls: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NodeConfig'
      responses:
        '202':
          description: Configuration update received

  ingestConfigPush:
    post:
      summary: Ingest Node Configuration Update (WebHook/Push)
      description: |
        Pushed by the DMS to an **Ingest Node** when new Storage Clusters are added to the HD Instance.
        **Endpoint Path**: `POST /v1/ingest/config`
      security:
        - mtls: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NodeConfig'
      responses:
        '202':
          description: Configuration update received

  snConfigPush:
    post:
      summary: Storage Node Configuration Update (WebHook/Push)
      description: |
        Pushed by the DMS to a **Storage Node (SN)** when its throughput limits or SCC targets change.
        **Endpoint Path**: `POST /v1/sn/config`
      security:
        - mtls: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NodeConfig'
      responses:
        '202':
          description: Configuration update received

components:
  securitySchemes:
    mtls:
      type: mutualTLS
  schemas:
    RegistrationRequest:
      type: object
      required: [node_id, node_type, csr, system_info]
      properties:
        node_id:
          type: string
        node_type:
          type: string
          enum: [HD_SN, HD_SCC, HD_INGEST, HD_DP, HD_QP, HD_PROXY]
        csr:
          type: string
          description: Base64 encoded PEM CSR
        bootstrap_token:
          type: string
          description: One-time use token generated in WebUX
        k8s_token:
          type: string
          description: Kubernetes ServiceAccount JWT token
        system_info:
          $ref: '#/components/schemas/SystemInfo'

    RegistrationResponse:
      type: object
      properties:
        certificate:
          type: string
          description: Signed x.509 PEM certificate
        config:
          $ref: '#/components/schemas/NodeConfig'

    SystemInfo:
      type: object
      properties:
        os: { type: string }
        kernel_version: { type: string }
        arch: { type: string }
        hostname: { type: string }

    NodeTelemetry:
      type: object
      required: [node_id, health_status]
      properties:
        node_id: { type: string }
        health_status:
          type: string
          enum: [Healthy, Degraded, Unhealthy]
        cpu_utilization: { type: number }
        mem_utilization: { type: number }
        queries_serviced: { type: integer }
        records_serviced: { type: integer }
        mb_processed: { type: number }
        storage_devices:
          type: array
          items:
            $ref: '#/components/schemas/StorageDevice'

    StorageDevice:
      type: object
      properties:
        device_path: { type: string }
        device_type: { type: string }
        interface: { type: string }
        used_megabytes: { type: number }
        total_megabytes: { type: number }

    NodeConfig:
      type: object
      properties:
        storage_strategy: { type: string }
        alert_thresholds:
          type: object
          additionalProperties: { type: number }
        scc_targets:
          type: array
          items:
            $ref: '#/components/schemas/SCCTarget'
        target_storage_clusters:
          type: array
          items: { type: string }
          description: "Manual selection of Storage Clusters (SCID)."
        ingression_type:
          type: string
          enum: [kafka, rabbitmq, mqtt, file, s3]
          default: kafka
        input_format:
          type: string
          enum: [json, rowbinary, csv]
          default: json
        schema_file:
          type: string
          default: dataset_schema.json
        batch_interval_seconds:
          type: integer
          nullable: true
        batch_id_field:
          type: string
          default: ""
        tcp_max_session_duration_sec:
          type: integer
          default: 600
        env:
          type: string
          enum: [production, development]
          default: production
        log_level:
          type: string
          enum: [DEBUG, INFO, WARN, ERROR]
          default: INFO
        otel_collector_url:
          type: string
          default: ""
        dms_url:
          type: string
          default: "http://dms:6986"
        healthcheck_port:
          type: integer
          default: 8081
        opt_in:
          type: boolean
          default: false
        webhooks:
          type: array
          items:
            type: object
            properties:
              url: { type: string }
              events:
                type: array
                items: { type: string }
              headers:
                type: object
                additionalProperties: { type: string }
        target_storage_clusters:
          type: array
          items: { type: string }
          description: Manual list of Storage Cluster IDs to target.
        kafka:
          type: object
          properties:
            brokers: { type: string }
            topic: { type: string }
            group_id: { type: string }
        rabbitmq:
          type: object
          properties:
            url: { type: string }
            queue: { type: string }
        mqtt:
          type: object
          properties:
            address: { type: string }
            client_id: { type: string }
            topic: { type: string }
        file:
          type: object
          properties:
            directory: { type: string }
        s3:
          type: object
          properties:
            bucket: { type: string }
            prefix: { type: string }
            region: { type: string }
            access_key_id: { type: string }
            secret_access_key: { type: string }
            endpoint: { type: string }

        # SCC Specific Configuration
        record_size: { type: integer, default: 0 }
        max_hash_queue_bytes: { type: integer, default: 2048 }
        batch_timeout_ms: { type: integer, default: 5000 }
        timeout_action:
          type: string
          enum: [EOB, BBN]
          default: EOB
        sn_targets: { type: string, description: "Comma-separated SN list" }
        production_interface: { type: string }
        production_ip: { type: string }
        production_port: { type: integer }
        command_interface: { type: string }
        command_port: { type: integer }
        observability_interface: { type: string }
        observability_port: { type: integer }
        master_cpuset: { type: string }
        transport_cores: { type: integer }
        kv_cores: { type: integer }

    SCCTarget:
      type: object
      required: [address, port]
      properties:
        address: { type: string }
        port: { type: integer }
        description: { type: string }

    ConfigSyncStatus:
      type: object
      required: [status]
      properties:
        status:
          type: string
          enum: [success, failed]
        message:
          type: string
          description: Optional error message or success details
        round_batch_id_field:
          type: boolean
          description: If true, rounds the extracted batch ID to the time index interval for time_series datasets.
