{
  "docVersion": 1,
  "id": "template-realtime-analytics-kappa",
  "name": "Kappa — real-time analytics",
  "purpose": "Streaming-only analytics: apps emit events to a retained log; a windowed stream job aggregates with watermarks; an idempotent sink serves dashboards. Reprocessing = replay.",
  "nodes": {
    "apps": {
      "type": "mobile",
      "label": "Product Apps",
      "x": 0,
      "y": 240,
      "runtime": "web · mobile · backend"
    },
    "ingest": {
      "type": "gateway",
      "label": "Event Gateway",
      "x": 260,
      "y": 240,
      "runtime": "validate + stamp event-time",
      "patterns": [
        "stateless",
        "autoscale"
      ]
    },
    "log": {
      "type": "stream",
      "label": "Event Log",
      "x": 520,
      "y": 240,
      "runtime": "retained 30d · partitioned",
      "color": "purple",
      "patterns": [
        "replicated",
        "sharded"
      ],
      "notes": "Source of truth. Consumers keep their own offsets; replaying from 0 rebuilds any downstream view."
    },
    "job": {
      "type": "worker",
      "label": "Streaming Job",
      "x": 780,
      "y": 240,
      "runtime": "windows + watermarks",
      "color": "blue",
      "notes": "Tumbling 1-min + session windows. Watermark = max event-time seen minus allowed lateness; closing a window emits its aggregate."
    },
    "late": {
      "type": "dlq",
      "label": "Late-Event Parking",
      "x": 780,
      "y": 460,
      "runtime": "after watermark → side output",
      "color": "amber"
    },
    "sink": {
      "type": "timeseries",
      "label": "Serving Store",
      "x": 1040,
      "y": 240,
      "runtime": "upsert by (window, key)",
      "patterns": [
        "replicated"
      ],
      "notes": "Idempotent upserts keyed by window id — replays overwrite identically instead of double-counting."
    },
    "dash": {
      "type": "dashboard",
      "label": "Live Dashboards",
      "x": 1300,
      "y": 240,
      "runtime": "p95, funnels, counts"
    }
  },
  "edges": {
    "e1": {
      "from": "apps",
      "to": "ingest",
      "fromSide": "right",
      "toSide": "left",
      "label": "events",
      "animated": true
    },
    "e2": {
      "from": "ingest",
      "to": "log",
      "fromSide": "right",
      "toSide": "left",
      "label": "append",
      "animated": true,
      "patterns": [
        "batch",
        "compressed"
      ]
    },
    "e3": {
      "from": "log",
      "to": "job",
      "fromSide": "right",
      "toSide": "left",
      "label": "consume · offset-tracked",
      "animated": true,
      "patterns": [
        "ordered"
      ]
    },
    "e4": {
      "from": "job",
      "to": "sink",
      "fromSide": "right",
      "toSide": "left",
      "label": "window aggregates",
      "patterns": [
        "idempotent"
      ]
    },
    "e5": {
      "from": "job",
      "to": "late",
      "fromSide": "bottom",
      "toSide": "top",
      "label": "too late for window",
      "style": "dashed"
    },
    "e6": {
      "from": "sink",
      "to": "dash",
      "fromSide": "right",
      "toSide": "left",
      "label": "query"
    },
    "e7": {
      "from": "log",
      "to": "job",
      "fromSide": "bottom",
      "toSide": "bottom",
      "label": "REPLAY from offset 0 (new version)",
      "style": "dashed",
      "color": "green"
    }
  },
  "boxes": {
    "b1": {
      "x": 480,
      "y": 120,
      "w": 860,
      "h": 300,
      "label": "the only pipeline — no batch twin to drift",
      "color": "blue",
      "opacity": 0.04
    }
  },
  "texts": {
    "t1": {
      "x": 0,
      "y": 470,
      "text": "kappa's bet: one codebase, reprocessing by replay.\nthe cost: the log must retain enough history,\nand the sink must be idempotent — or replays\ndouble-count everything.",
      "color": "green",
      "size": 12
    }
  },
  "meta": {
    "createdAt": "2026-07-13T09:00:00.000Z",
    "modifiedAt": "2026-07-13T09:00:00.000Z"
  }
}