{
  "version": "proto-tc20-4",
  "prototype": "tc20",
  "title": "ThunderCloud 2.0 · Phase 1 installed-base slice",
  "subtitle": "EarthSpark open stack — GroundBolt ↔ SparkNet-HTTP ↔ meters · SymmetricDS sync · optional self-host cloud",
  "titleGround": "TC2.0 · GroundBolt only (no cloud)",
  "subtitleGround": "On-site basestation path — meters · gateway · GroundBolt · SparkNet-HTTP · Ansible · local ops (no ThunderCloud cloud / SymmetricDS / managed SaaS)",
  "roadmapPhase": 1,
  "roadmapPhaseLabel": "Installed-base interop — SparkMeter / TC2.0 open path (dev preview)",
  "outOfScope": "OpenAMI MQTT northbound, MeshEMS/Street-EMS, IEEE 1547/2030.5/OpenADR, non-SparkMeter OEM drivers, production TC1.x managed SaaS internals",
  "outOfScopeGround": "ThunderCloud cloud half, SymmetricDS sync, managed EarthSpark SaaS, remote CRM that needs cloud, OpenAMI / MeshEMS / IEEE grid northbound",
  "hostViews": [
    { "id": "full", "label": "Full stack (site + cloud)", "short": "Full stack" },
    { "id": "ground", "label": "GroundBolt only (no cloud)", "short": "GroundBolt only" }
  ],
  "groups": [
    { "id": "billing", "label": "Billing & payment", "short": "Billing" },
    { "id": "cutoffs", "label": "Cutoffs & control", "short": "Cutoffs" },
    { "id": "theft", "label": "Theft & loss", "short": "Theft" },
    { "id": "visibility", "label": "Visibility & operations", "short": "Visibility" },
    { "id": "comms", "label": "When communications fail", "short": "Comms failure" },
    { "id": "integration", "label": "Integration & long-term freedom", "short": "Integration" },
    { "id": "affordability", "label": "Affordability", "short": "Affordability" }
  ],
  "problems": [
    { "id": "prob-usage", "group": "billing", "label": "Per-home kWh", "evidence": "HARD: reading.energy" },
    { "id": "prob-prepaid", "group": "billing", "label": "Collect before use", "evidence": "PARTIAL: balance + EnableSts; no token mint in driver" },
    { "id": "prob-balance", "group": "billing", "label": "Show balance", "evidence": "HARD*: POST …/balance-and-flags (recommended)" },
    { "id": "prob-disconnect", "group": "cutoffs", "label": "Disconnect", "evidence": "HARD: ElectricalMeterCommandDisable" },
    { "id": "prob-reconnect", "group": "cutoffs", "label": "Reconnect", "evidence": "HARD: ElectricalMeterCommandEnable" },
    { "id": "prob-tamper", "group": "theft", "label": "Tamper state", "evidence": "HARD: reading.state=13 Tamper" },
    { "id": "prob-instant-power", "group": "visibility", "label": "Instant power", "evidence": "HARD: true_power_inst/avg" },
    { "id": "prob-voltage", "group": "visibility", "label": "Voltage at home", "evidence": "HARD: voltage_avg/min/max" },
    { "id": "prob-identity", "group": "visibility", "label": "Node ID & firmware", "evidence": "PARTIAL: node_id + firmware_version; no serial/clock fields" },
    { "id": "prob-cloud-down", "group": "comms", "label": "Cloud unreachable", "evidence": "ABSENT in driver — GroundBolt app topology" },
    { "id": "prob-crm", "group": "integration", "label": "Mobile money CRM", "hostModes": ["full"], "evidence": "ABSENT in driver — platform" },
    { "id": "prob-vendor-exit", "group": "integration", "label": "Vendor exit", "evidence": "PARTIAL: open contract + init aes_key; meter key escrow = ops" },
    { "id": "prob-protocols", "group": "integration", "label": "Mixed protocols", "evidence": "META: vendor-neutral HTTP+SSE/gRPC contract" },
    { "id": "prob-mdms-cost", "group": "affordability", "label": "MDMS cost", "evidence": "ABSENT in driver — platform" }
  ],
  "hardware": [
    { "id": "meter", "label": "SparkMeter revenue meter", "short": "SM meter", "desc": "Field meters on SparkNet RF mesh" },
    { "id": "ciu", "label": "CIU · keypad / customer UI", "short": "CIU" },
    { "id": "gateway", "label": "SparkNet gateway (e.g. Firefly)", "short": "Gateway", "desc": "Serial link from GroundBolt host to mesh" },
    { "id": "groundbolt", "label": "GroundBolt basestation", "short": "GroundBolt", "desc": "On-site TC2.0 app + local Postgres" },
    { "id": "cloud", "label": "ThunderCloud cloud host", "short": "TC cloud", "desc": "Same app as GroundBolt; EarthSpark-hosted or self-hosted", "hostModes": ["full"] }
  ],
  "softwareKinds": [
    { "id": "driver", "label": "Meter driver", "short": "Driver" },
    { "id": "edge", "label": "Basestation app", "short": "Ground" },
    { "id": "sync", "label": "Ground ↔ cloud sync", "short": "Sync" },
    { "id": "platform", "label": "Operator application", "short": "App" },
    { "id": "ops", "label": "Deploy & keys", "short": "Ops" }
  ],
  "software": [
    {
      "id": "sw-sparknet-http",
      "label": "SparkNet-HTTP driver",
      "kind": "driver",
      "hardware": ["meter", "gateway", "groundbolt"],
      "hostModes": ["full", "ground"],
      "phase": [1],
      "specs": ["HTTP+SSE", "gRPC opt", "binary release"],
      "href": "https://github.com/EarthSpark/sparknet-http"
    },
    {
      "id": "sw-driver-spec",
      "label": "meter-driver-spec contract",
      "kind": "driver",
      "hardware": ["meter", "groundbolt"],
      "hostModes": ["full", "ground"],
      "phase": [1],
      "specs": ["OpenAPI", "vendor-neutral"],
      "href": "https://github.com/EarthSpark/meter-driver-spec"
    },
    {
      "id": "sw-groundbolt",
      "label": "GroundBolt (TC2.0 on site)",
      "kind": "edge",
      "hardware": ["meter", "ciu", "groundbolt"],
      "hostModes": ["full", "ground"],
      "phase": [1],
      "specs": ["Flask app", "local Postgres", "dev preview"],
      "href": "https://github.com/EarthSpark/thundercloud"
    },
    {
      "id": "sw-symmetricds",
      "label": "SymmetricDS sync",
      "kind": "sync",
      "hardware": ["groundbolt", "cloud"],
      "hostModes": ["full"],
      "phase": [1],
      "specs": ["bidirectional DB"],
      "href": "https://github.com/EarthSpark/symmetricds"
    },
    {
      "id": "sw-tc-cloud",
      "label": "ThunderCloud cloud half",
      "kind": "platform",
      "hardware": ["meter", "cloud"],
      "hostModes": ["full"],
      "phase": [1],
      "specs": ["same codebase", "remote ops"],
      "href": "https://github.com/EarthSpark/thundercloud"
    },
    {
      "id": "sw-ansible",
      "label": "GroundBolt Ansible bootstrap",
      "kind": "ops",
      "hardware": ["groundbolt"],
      "hostModes": ["full", "ground"],
      "phase": [1],
      "specs": ["Docker", "NetBird"],
      "href": "https://github.com/EarthSpark/ansible"
    },
    {
      "id": "sw-keys",
      "label": "Meter encryption-key options",
      "kind": "ops",
      "hardware": ["meter", "cloud"],
      "hostModes": ["full", "ground"],
      "phase": [1],
      "specs": ["EarthSpark hold", "A2EI escrow", "operator hold"],
      "href": "https://www.sparkmeter.io/news"
    },
    {
      "id": "sw-managed",
      "label": "Managed path (EarthSpark SaaS)",
      "kind": "platform",
      "hardware": ["meter", "cloud", "groundbolt"],
      "hostModes": ["full"],
      "phase": [1],
      "specs": ["TC1.x today", "continuity"],
      "href": "https://www.sparkmeter.io/open-source.html"
    }
  ],
  "links": [
    ["prob-usage", "sw-sparknet-http"],
    ["prob-usage", "sw-groundbolt"],
    ["prob-usage", "sw-tc-cloud"],
    ["prob-prepaid", "sw-sparknet-http"],
    ["prob-prepaid", "sw-groundbolt"],
    ["prob-prepaid", "sw-tc-cloud"],
    ["prob-prepaid", "sw-managed"],
    ["prob-balance", "sw-sparknet-http"],
    ["prob-balance", "sw-groundbolt"],
    ["prob-balance", "sw-tc-cloud"],
    ["prob-disconnect", "sw-sparknet-http"],
    ["prob-disconnect", "sw-groundbolt"],
    ["prob-reconnect", "sw-sparknet-http"],
    ["prob-reconnect", "sw-groundbolt"],
    ["prob-tamper", "sw-sparknet-http"],
    ["prob-tamper", "sw-groundbolt"],
    ["prob-instant-power", "sw-sparknet-http"],
    ["prob-instant-power", "sw-groundbolt"],
    ["prob-voltage", "sw-sparknet-http"],
    ["prob-voltage", "sw-groundbolt"],
    ["prob-identity", "sw-sparknet-http"],
    ["prob-identity", "sw-driver-spec"],
    ["prob-cloud-down", "sw-groundbolt"],
    ["prob-cloud-down", "sw-symmetricds"],
    ["prob-crm", "sw-tc-cloud"],
    ["prob-crm", "sw-managed"],
    ["prob-vendor-exit", "sw-sparknet-http"],
    ["prob-vendor-exit", "sw-driver-spec"],
    ["prob-vendor-exit", "sw-keys"],
    ["prob-vendor-exit", "sw-ansible"],
    ["prob-protocols", "sw-driver-spec"],
    ["prob-protocols", "sw-sparknet-http"],
    ["prob-mdms-cost", "sw-tc-cloud"],
    ["prob-mdms-cost", "sw-managed"],
    ["prob-mdms-cost", "sw-ansible"]
  ]
}
