{
  "name": "Firo Spark Sync Monitor",
  "version": 1,
  "purpose": "Shared fleet telemetry for public Firo Electrum backends: tip freshness, Spark coin group and setHash consistency, and anonymity set fetch health.",
  "audience": [
    "firo_community",
    "wallet_and_mobile_developers",
    "electrum_operators",
    "core_and_tooling"
  ],
  "complements": "Wallet Electrum clients talk to one host. This API watches the curated public fleet so the Firo community can share tip and Spark health without each app re downloading a full anon set for monitoring.",
  "not_for": [
    "in_wallet_server_picker",
    "replace_wallet_electrum_or_spark_sdk",
    "user_device_rtt_ranking",
    "authoritative_chain_state"
  ],
  "endpoints": {
    "/api/status": "Full fleet snapshot with lag, Spark consistency, and anon set health.",
    "/api/spark": "Compact Spark consensus and inconsistent endpoint ids.",
    "/api/ci": "Pass or fail JSON (HTTP 200 when ok=true: spark_ok and max_lag≤2; else 503). Use spark_ok for Spark-only gates.",
    "/api/docs": "Machine readable field reference and usage notes.",
    "/api/badge": "Embeddable SVG fleet health badge.",
    "/api/history": "Durable time series (ships with funded always on hosting)."
  },
  "probes": [
    {
      "method": "server.version",
      "why": "reachability, TLS, software version"
    },
    {
      "method": "blockchain.headers.subscribe",
      "why": "height and lag vs majority safe fleet tip"
    },
    {
      "method": "spark.getsparklatestcoinid",
      "why": "active Spark coin group id, cross checked fleet wide"
    },
    {
      "method": "spark.getsparkanonymityset",
      "why": "full anon set fetch time, size (MB and coins), and setHash consistency"
    }
  ],
  "privacy": "No addresses, keys, transactions, or wallet traffic. Public Electrum methods only.",
  "latency_note": "latency_ms is probe RTT from this monitor only. Secondary to lag and Spark consistency. Not user device RTT.",
  "status_fields": {
    "checked_at": "ISO timestamp when light probes finished for this response",
    "reference": "Majority safe fleet tip height (highest height at least two backends agree on)",
    "spark_consensus": "Modal spark.getsparklatestcoinid across reachable backends",
    "spark_sethash_consensus": "Modal anon set setHash across measured backends",
    "anonset_checked_at": "When the last full anon set sweep finished",
    "anonset_source": "How anon set figures were produced (snapshot or live)",
    "summary": "{ total, green, yellow, red } fleet counts",
    "stats": "Aggregates for lag, probe RTT, and anon set median ms / MB / coins",
    "endpoints": "Per backend rows (see endpoint_fields)",
    "meta": "Purpose, audience, probe methods, and status rules"
  },
  "endpoint_fields": {
    "id": "Stable registry id",
    "name": "Display name",
    "operator": "Operator label",
    "host": "Electrum TLS host",
    "port": "Electrum TLS port (usually 50002)",
    "ok": "Light probe succeeded",
    "height": "Reported chain tip",
    "lag": "Blocks behind fleet reference",
    "latency_ms": "Probe RTT from this monitor (secondary metric)",
    "version": "ElectrumX version string",
    "spark_latest_coin_id": "Active Spark coin group id",
    "spark_consistent": "Whether spark_latest_coin_id matches spark_consensus",
    "anonset": "{ ok, ms, coins, mb, setHash, group, consistent, error } or null if not measured yet",
    "status": "green | yellow | red from lag, Spark consistency, and probe RTT rules",
    "error": "Light probe error string if any"
  },
  "example_uses": [
    "Alert when a public backend has spark_consistent=false or anonset.consistent=false",
    "Triage whether a Spark sync issue is an app bug or a backend serving a slow or divergent set",
    "Compare anonset.mb and anonset.ms across operators before publishing recommended backends",
    "Embed /api/badge or poll /api/status and /api/ci for docs and status pages"
  ],
  "example_curl": "curl -sS https://firo-spark-sync-monitor.vercel.app/api/status | jq \"{summary, spark_sethash_consensus, endpoints: [.endpoints[] | {name, lag, spark_consistent, anonset}]}\"",
  "live_status": "/api/status"
}