{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "FermiHDI Storage Node Configuration",
  "description": "JSON schema for the configuration of a FermiHDI Storage Node.",
  "type": "object",
  "properties": {
    "node_name": {
      "type": "string",
      "description": "The identifying host/pod name of the Storage Node. Must be globally unique.",
      "default": "sn-alpha-001"
    },
    "dms_url": {
      "type": "string",
      "description": "The endpoint for the primary Data Management System.",
      "default": "http://127.0.0.1:9090"
    },
    "command_port": {
      "type": "integer",
      "description": "The primary operational TCP port over which the SCC pushes mTLS-secured query batches.",
      "default": 6986
    },
    "command_interface": {
      "type": "string",
      "description": "The IPv4 interface for the Command Network to bind to.",
      "default": "0.0.0.0"
    },
    "observability_port": {
      "type": "integer",
      "description": "Unsecured HTTP healthcheck server and telemetry metrics endpoint.",
      "default": 8080
    },
    "observability_interface": {
      "type": "string",
      "description": "The IPv4 interface for the Observability Network to bind to.",
      "default": "0.0.0.0"
    },
    "log_level": {
      "type": [
        "string",
        "integer"
      ],
      "description": "Filter for JSON structured logs. Can be an integer (0-3) or string ('debug', 'info', 'warn', 'error').",
      "default": "info"
    },
    "otel_grpc_url": {
      "type": "string",
      "description": "The Endpoint URL for OpenTelemetry GRPC forwarding.",
      "default": ""
    },
    "production_interface": {
      "type": "string",
      "description": "The physical PCIe network interface bound to the VFIO kernel module used for fast-path DPDK packet RX/TX. If 'none', uses posix system stack.",
      "default": "none"
    },
    "production_ip": {
      "type": "string",
      "description": "The IPv4 interface to bind DPDK/Seastar POSIX fallbacks onto.",
      "default": "0.0.0.0"
    },
    "production_port": {
      "type": "integer",
      "description": "The operational data ingress/egress port to bind the Production fast-path onto.",
      "default": 8000
    },
    "scc_ip": {
      "type": "string",
      "description": "The IPv4 address of the parent Storage Cluster Controller (SCC) orchestrating this node.",
      "default": "127.0.0.1"
    },
    "scc_port": {
      "type": "integer",
      "description": "The command port of the parent SCC for fast-path DPDK response bridging.",
      "default": 8000
    },
    "storage_devices": {
      "type": "array",
      "description": "Defines physical drives mapping strategy. If omitted, uses default internal SPDK hardware discovery sequences.",
      "items": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "default": "dev"
          },
          "address": {
            "type": "string",
            "default": ""
          },
          "type": {
            "type": "string",
            "default": "nvme"
          }
        }
      }
    },
    "drive_strategy": {
      "type": "string",
      "enum": [
        "fill_sequential",
        "round_robin"
      ],
      "description": "Data allocation strategy.",
      "default": "fill_sequential"
    },
    "drive_full_action": {
      "type": "string",
      "enum": [
        "stop_and_signal",
        "wiping_oldest_ttl"
      ],
      "description": "Behavior logic when achieving 100% capacity.",
      "default": "stop_and_signal"
    },
    "hash_offload_platform": {
      "type": "string",
      "enum": [
        "none",
        "usb_fpga",
        "nvidia_bluefield",
        "amd_alveo_v80"
      ],
      "description": "Offloader selection for hashing subsystem.",
      "default": "none"
    },
    "filter_device_selector": {
      "type": "string",
      "enum": [
        "gpu",
        "cpu",
        "host"
      ],
      "description": "Defines primary accelerator type for filter. Uses SYCL conventions.",
      "default": "gpu"
    },
    "use_ocf": {
      "type": "boolean",
      "description": "Enables Open CAS Framework (OCF) for storage tiering.",
      "default": false
    },
    "ocf_cache_devices": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Comma separated list of fast device names to act as cache for OCF.",
      "default": []
    },
    "ocf_core_devices": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Comma separated list of slow/bulk device names to act as primary cores for OCF.",
      "default": []
    },
    "master_cpuset": {
      "type": "string",
      "description": "The primary core allocation topology map. Bypasses DMS polling if populated.",
      "default": ""
    },
    "transport_cores": {
      "type": "integer",
      "description": "Core count strictly allocated for DPDK network RX/TX polling.",
      "default": 1
    },
    "storage_cores": {
      "type": "integer",
      "description": "Core count dedicated for SPDK NVMe poll mode access.",
      "default": 1
    },
    "hash_cores": {
      "type": "integer",
      "description": "Core count for inline hashing routines.",
      "default": 1
    },
    "kv_cores": {
      "type": "integer",
      "description": "Core count for the tracking engine.",
      "default": 1
    },
    "filter_cores": {
      "type": "integer",
      "description": "Core count for SYCL/GPU hardware accelerators.",
      "default": 1
    },
    "bootstrap_token": {
      "type": "string",
      "description": "One-time use token from WebUX for Phase 1 registration (RSA-4096).",
      "default": ""
    },
    "k8s_token": {
      "type": "string",
      "description": "Kubernetes ServiceAccount JWT for automated platform identity registration.",
      "default": ""
    },
    "client_cert_file": {
      "type": "string",
      "description": "Path to manually injected x.509 certificate.",
      "default": ""
    },
    "client_key_file": {
      "type": "string",
      "description": "Path to manually injected RSA private key.",
      "default": ""
    },
    "pki_wrap_key": {
      "type": "string",
      "description": "Optional AES key for encrypting persisted identity files.",
      "default": ""
    },
    "hash_algorithm": {
      "type": "string",
      "description": "The hashing algorithm for chain fingerprints.",
      "default": "sha512"
    },
    "opt_in": {
      "type": "boolean",
      "description": "Opt-in to telemetry parsing and reports.",
      "default": false
    },
    "sentry_dsn": {
      "type": "string",
      "description": "Remote crash reporting URL for debug stack traces.",
      "default": "http://fad1cbbe866a27d7be3964f73b478fa8@sentry-relay:3000/4511130313031680"
    },
    "dry_run": {
      "type": "boolean",
      "description": "Enables dry-run engine emulation overrides.",
      "default": false
    },
    "hash_chain_block_size": {
      "type": "integer",
      "description": "Number of record hashes accumulated into one block before chaining. Reduces serial chain steps from N to ceil(N/B). Instance-wide \u2014 must be identical across all nodes. Default: 64.",
      "default": 64,
      "minimum": 1
    },
    "default_query_verify": {
      "type": "boolean",
      "description": "Admin default for /v1/query hash re-verification. When true (default), the SN re-verifies the batch chain hash before filtering and streaming results. When false, hash check is skipped for maximum read throughput \u2014 the SYCL/GPU filter is still applied. Can be overridden per-query via the 'force_verify' payload field. Dynamically updatable via DMS config push.",
      "default": true
    },
    "hash_none_explicit_opt_in": {
      "type": "boolean",
      "description": "Safety gate required when hash_algorithm is set to 'none'. When true, the node will accept and operate in hash-disabled mode. When false (default) and hash_algorithm is 'none', the node will refuse to start and log a fatal error. This prevents accidental disabling of chain integrity verification.",
      "default": false
    },
    "max_in_flight": {
      "type": "integer",
      "description": "TD-001: Maximum number of NVMe write batches that can be simultaneously in-flight per storage device. Controls the write ring buffer depth. Higher values increase write throughput at the cost of memory (each slot is MDTS bytes). 0 means use the hardware-reported NVMe queue depth \u00d7 storage_cores. Minimum: 1.",
      "default": 0,
      "minimum": 0
    },
    "max_active_batches": {
      "type": "integer",
      "description": "TD-004: Maximum number of query BatchReadJobs that RunDriveReader will process concurrently per storage device. When all slots are occupied, new jobs yield-spin until one completes. Limits peak NVMe read QD and prevents read starvation under heavy concurrent query load. Default: 8.",
      "default": 8,
      "minimum": 1
    },
    "nvme_read_qd": {
      "type": "integer",
      "description": "TD-004: Maximum total NVMe read in-flight I/Os across all active read batches per device. Divided equally among active batches (each gets nvme_read_qd / max_active_batches slots). Default: 128 (matches typical NVMe io_queue_depth). Set to the hardware io_queue_depth for best utilisation.",
      "default": 128,
      "minimum": 1
    },
    "default_query_qos": {
      "type": "integer",
      "description": "TD-004: Default QoS weight (1\u2013255) assigned to query batch NVMe read scheduling when the query does not specify a 'query_qos' field. Higher weights receive proportionally more NVMe read QD budget when multiple batches compete. Default: 1 (lowest priority, equal weight). DMS-pushable without node restart.",
      "default": 1,
      "minimum": 1,
      "maximum": 255
    },
    "record_size": {
      "type": "integer",
      "description": "The explicit byte boundary determining record splitting during sequential background hashing.",
      "default": 150
    },
    "max_hash_queue_bytes": {
      "type": "integer",
      "description": "Megabyte limit configuring memory safety caps on the background hash processing queue protecting RAM footprint."
    },
    "batch_timeout_ms": {
      "type": "integer",
      "description": "Inactivity timer threshold in milliseconds dictating when to automatically severe lingering incomplete network ingest batches."
    },
    "timeout_action": {
      "type": "string",
      "enum": [
        "EOB",
        "BBN"
      ],
      "default": "EOB",
      "description": "Action executed against uncompleted batches hitting timeout limits mapping directly to DPDK control frames."
    },
    "sn_targets": {
      "type": "string",
      "description": "Comma-separated topology map strings identifying replica nodes specifically matching node_name:ip:control_port:prod_port structure (e.g., sn1:10.0.0.1:8000:6987)."
    },
    "sc_status": {
      "type": "string",
      "description": "Indicates the explicit upstream Operational Status of the bounded logical Storage Cluster.",
      "default": "Active"
    },
    "webhooks": {
      "type": "array",
      "description": "Optional list of WebHook endpoints to notify upon significant node lifecycle events (e.g., storage_full, fault, registered).",
      "items": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "List of event types to trigger this webhook (e.g., 'fault', 'capacity_alert')."
          },
          "headers": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Custom HTTP headers to include with the webhook POST request."
          }
        },
        "required": [
          "url"
        ]
      }
    }
  }
}
