' : '') +
(state.connection !== 'unicast'
diff --git a/docs/javascripts/header-controls-order.js b/docs/javascripts/header-controls-order.js
index 54fe959..393fb21 100644
--- a/docs/javascripts/header-controls-order.js
+++ b/docs/javascripts/header-controls-order.js
@@ -125,10 +125,13 @@
}).observe(header, { childList: true, subtree: true });
}
- new MutationObserver(syncDrawerThemeBtn).observe(document.body, {
- attributes: true,
- attributeFilter: ['data-md-color-scheme', 'data-md-color-primary']
- });
+ if (!window.__dzBodyThemeObsBound) {
+ window.__dzBodyThemeObsBound = true;
+ new MutationObserver(syncDrawerThemeBtn).observe(document.body, {
+ attributes: true,
+ attributeFilter: ['data-md-color-scheme', 'data-md-color-primary']
+ });
+ }
}
function onReady(fn) {
diff --git a/docs/kalshi-runbook.md b/docs/kalshi-runbook.md
new file mode 100644
index 0000000..01985c3
--- /dev/null
+++ b/docs/kalshi-runbook.md
@@ -0,0 +1,215 @@
+---
+description: LLM-oriented runbook — buy a Kalshi feed, install Edge Connect, subscribe, and verify normalized quotes on the WebSocket. Served to the MCP via GitHub raw; not published on the docs site.
+---
+
+# Kalshi + Edge Connect — runbook
+
+!!! tip "Let an AI walk you through this page"
+ You do not need to run every command yourself. This page is written so an AI assistant can follow it with you.
+
+ 1. Connect the [DoubleZero MCP](mcp.md) (`https://data.doublezero.xyz/api/mcp`) so the agent can load this runbook itself via `get_onboarding_runbook`.
+ 2. Tell it your Linux host (or how to SSH to it), where your access secret lives (`DZ_SECRET`: a `DZ_…` token or the path to your keypair file), and what you want — for example: *install Edge Connect and subscribe to Kalshi perps TOB*.
+ 3. Paste back any errors it asks for. It should follow the steps below in order.
+
+ Prefer to do it by hand? Start at [Prerequisites](#prerequisites).
+
+**Not this page:** choosing between Edge Connect and native multicast — start at the [Kalshi getting started](kalshi.md) page. Raw wire decode — path 2 on that page and [edge-feed-spec](https://github.com/malbeclabs/edge-feed-spec). WebSocket contract — [PROTOCOL.md](https://github.com/malbeclabs/doublezero-edge-connect/blob/main/PROTOCOL.md).
+
+**What success looks like:** the tunnel shows `BGP Session Up`, you are subscribed to at least one Kalshi group, the bridge is listening on `ws://:8081`, and (when the publisher is live) you see `instrument` / `quote` (or `book`) JSON messages with `source: "KALSHI"`.
+
+---
+
+## Prerequisites
+
+| Need | Notes |
+|------|--------|
+| Linux/amd64 host | Installer target. |
+| Public IP on the host | Must match the IP authorized when the feed / access pass was issued (or the any-IP `0.0.0.0` pass). Override detection with `DZ_CLIENT_IP` if behind NAT. |
+| Access secret (`DZ_SECRET`) | A `DZ_…` token **or** path to the Solana keypair JSON that owns the access pass / feed purchase. |
+| Account credits | The identity in `DZ_SECRET` must have available DoubleZero credits. `Insufficient balance` means recharge that account before connect can succeed. |
+| Purchased Kalshi feed | Buy at [doublezero.xyz/edge/subscribe](https://doublezero.xyz/edge/subscribe) before expecting traffic. |
+| GRE (IP proto 47) allowed | Cloud SG / firewall. On AWS, disable ENI source/dest check. |
+| UDP `30000`–`59999` inbound on `doublezero1` | Leading digit = class (`3` market, `4` reference, `5` snapshot). Open the full band so new channels do not need another firewall change. |
+
+The installer may print `!! No access pass… Continuing` and still exit 0. That is **not** connected. Look for `Access pass OK` and a later `BGP Session Up`. Treat `disconnected` + `Insufficient balance` / missing pass as a hard stop.
+
+Firewall sketch (after the tunnel exists, `doublezero1` is present):
+
+```bash
+sudo iptables -A OUTPUT -p gre -j ACCEPT
+sudo iptables -A INPUT -i doublezero1 -p udp --dport 30000:59999 -j ACCEPT
+# also allow BGP/PIM as in the full Kalshi guide if you harden INPUT by default
+```
+
+---
+
+## Feed map
+
+| Group code | Kind | Multicast group | Market | Reference | Snapshot |
+|------------|------|-----------------|--------|-----------|----------|
+| `edge-kalshi-perps-tob` | TOB | `233.84.178.3` | `31000` | `41000` | — |
+| `edge-kalshi-perps-mbp` | MBP | `233.84.178.4` | `32000` | `42000` | `52000` |
+| `edge-kalshi-sports-tob` | TOB | `233.84.178.17` | `33000`+id | `43000`+id | — |
+| `edge-kalshi-sports-mbp` | MBP | `233.84.178.20` | `34000`+id | `44000`+id | `54000`+id |
+
+Confirm the live group IP for your env:
+
+```bash
+docker exec doublezero-edge-connect doublezero multicast group get --code edge-kalshi-perps-tob
+```
+
+**Edge Connect note:** the bridge matches `code` and multicast **group IP** from its hardcoded feed table to what `doublezero status` reports. A code/IP mismatch fails **silently** (receiver never starts — watch bridge logs / metrics, not only BGP).
+
+---
+
+## Steps
+
+### 1. Install Edge Connect
+
+`DZ_SECRET` is the identity that holds your Edge access pass / purchased feed. Set it to either:
+
+- a **`DZ_…` access token** you were issued, or
+- the **path to a Solana keypair JSON** (the same keypair authorized onchain for this host’s public IP, or for the any-IP `0.0.0.0` pass).
+
+```bash
+# example: keypair file
+DZ_SECRET=/path/to/keypair.json \
+DZ_FEEDS=KALSHI \
+DZ_ASSUME_YES=1 \
+ curl -fsSL https://get.doublezero.xyz/connect | bash
+```
+
+If you omit `DZ_SECRET`, the installer prompts once. With it set, the install is non-interactive.
+
+What this does: prep host (Docker, `tun`/`ip_gre`, `rmem_max`) → run `doublezero-edge-connect` container (`--network host`) → run `doublezero connect multicast` inside it → serve WS on `:8081` when a **market-data** subscription is active.
+
+Watch the installer for `Access pass OK` and `Joined feed(s): …`. That feed list is what this identity actually purchased. Do not assume Top of Book (`edge-kalshi-perps-tob`) unless it appears there.
+
+A `⚠️` on **Lowest Latency Device** while **Current Device** is another metro is normal when the feed is only served from that metro. Do not pass `--device` to the closer site unless you know the feed is provisioned there — you will get *feed is not provisioned on the access pass* / *is served from that metro*.
+
+### 2. Ensure the Kalshi group is subscribed
+
+Prefer the feed the installer already joined. If status already shows `S:edge-kalshi-…`, skip this step.
+
+`--subscribe-feed` takes the **feed account name** from the pass (e.g. `kalshi-perps-mbp`). `--subscribe` takes the **group code** (e.g. `edge-kalshi-perps-mbp`). They are not interchangeable. Subscribing a code or name that is not on the pass fails.
+
+Only run an extra subscribe if the group you bought is missing from `doublezero status`. Prefer the **Feed account** form when using an Edge seat / access-pass identity:
+
+```bash
+docker exec doublezero-edge-connect \
+ doublezero connect multicast --subscribe-feed
+```
+
+Or by group code (use the code that matches `Joined feed(s):`, not a feed you did not buy):
+
+```bash
+# MBP example — swap for -tob / sports if that is what the pass joined
+docker exec doublezero-edge-connect \
+ doublezero connect multicast --subscribe edge-kalshi-perps-mbp
+```
+
+Multiple feeds: space-separate codes.
+
+```bash
+docker exec doublezero-edge-connect \
+ doublezero connect multicast --subscribe \
+ edge-kalshi-perps-tob edge-kalshi-perps-mbp \
+ edge-kalshi-sports-tob edge-kalshi-sports-mbp
+```
+
+### 3. Verify tunnel + subscription
+
+```bash
+docker exec doublezero-edge-connect doublezero status
+```
+
+Expect: `BGP Session Up`, and your Kalshi group(s) listed (e.g. `S:edge-kalshi-perps-mbp`).
+
+If status is `Pending BGP Session` for more than ~30s, wait. If it becomes **`Network Unreachable`** (outer GRE / `Tunnel Dst` may still ping), you likely have a leftover tunnel from a previous attempt:
+
+```bash
+docker exec doublezero-edge-connect doublezero disconnect multicast
+sudo ip link del doublezero1 2>/dev/null || true
+docker exec doublezero-edge-connect doublezero connect multicast
+```
+
+Re-run `doublezero status`. Expect `BGP Session Up` and `S:edge-kalshi-…`.
+
+```bash
+docker exec doublezero-edge-connect doublezero status --json
+```
+
+```bash
+docker logs --since 2m doublezero-edge-connect 2>&1 | grep -iE 'kalshi|activating|receiver|8081|error|warn'
+```
+
+Expect the reconciler to **activate** the Kalshi receiver for the subscribed code(s). `:8081` and `activating market-data receiver` can lag the tunnel by one refresh (default 30s). BGP Up + port down is not a failure yet — wait, then grep the logs again. No activation after that wait ⇒ code/IP table mismatch or feed not purchased.
+
+### 4. Open the WebSocket
+
+Default bind: `0.0.0.0:8081` (plain `ws://`, no TLS).
+
+```bash
+npx wscat -c ws://127.0.0.1:8081
+```
+
+Optional filter (after connect):
+
+```json
+{"method":"subscribe","subscription":{"venue":"KALSHI"}}
+```
+
+(Also accepted: `source` instead of deprecated `venue` — see PROTOCOL.md.)
+
+With no subscription message you get the firehose of every active venue on this host.
+
+### 5. Confirm data path
+
+| Check | Healthy signal |
+|-------|----------------|
+| WS accepts TCP | Connect succeeds; optional subscribe ack |
+| Instruments | `{"type":"instrument",...,"source":"KALSHI",...}` after connect (when refdata has been seen) |
+| Quotes / book | `quote` (TOB) or `book` (MBP) with `source":"KALSHI"` |
+| Quiet market | Heartbeats / refdata may flow without quotes; do not treat “no quote yet” alone as a tunnel failure |
+
+Capture on the tunnel (optional):
+
+```bash
+sudo tcpdump -ni doublezero1 host 233.84.178.3 and udp
+```
+
+Replace the group IP with the row you subscribed.
+
+---
+
+## Gotchas
+
+1. **Buy before subscribe.** No purchase ⇒ tunnel can look fine, UDP stays empty.
+2. **Silent non-activation.** Wrong `code` or group IP in the bridge table ⇒ no receiver, no WS market-data, little noise. Diff `doublezero status --json` groups vs bridge feed registry.
+3. **Host `doublezerod` vs container.** Edge Connect uses host networking and its own daemon. A host-level `doublezerod` fighting over the same UDP/GRE path will break the container — stop the host daemon when running the bridge.
+4. **WS only with market-data subscription.** Shreds-only (or no market feed) ⇒ no `:8081` service by design.
+5. **Port band.** Firewall must allow `30000:59999` on `doublezero1`, not only GRE. Decapsulated UDP re-enters `INPUT` on the tunnel iface.
+6. **Installer exit 0 ≠ tunnel up.** Missing access pass or credits: the script continues and still prints Done / a WebSocket URL. Trust `doublezero status`, not the installer footer.
+7. **Feed metro ≠ closest device.** Edge Connect attaches to the metro that serves the purchased feed. Forcing `--device` at the lowest-latency site fails if that metro does not serve the feed. The constraint is the **device**, not where the host sits (a host far from the serving metro can still attach to that device).
+8. **Stale `doublezero1`.** `tunnel already exists`, mixed `169.254.x` addresses, or BGP TCP never establishing to the inner peer → disconnect, delete the iface, connect again. Do not stack a second GRE on a dirty iface.
+
+---
+
+## Minimal consumer sketch
+
+```text
+1. TCP connect ws://HOST:8081
+2. (optional) send {"method":"subscribe","subscription":{"venue":"KALSHI"}}
+3. On message: ignore unknown types; key books on (source, channel, instrument_id), not symbol alone
+4. Use instrument.price_exponent / qty_exponent for display tick size; quote fields are already decimal
+```
+
+Full field list: [PROTOCOL.md](https://github.com/malbeclabs/doublezero-edge-connect/blob/main/PROTOCOL.md).
+
+---
+
+## See also
+
+- [Kalshi Edge Subscriber Connection](kalshi.md) — native client, firewall detail, wire format
+- [get.doublezero.xyz/connect](https://get.doublezero.xyz/connect) — installer
+- [doublezero-edge-connect](https://github.com/malbeclabs/doublezero-edge-connect) — bridge source + PROTOCOL.md
diff --git a/docs/kalshi.md b/docs/kalshi.md
new file mode 100644
index 0000000..6ad23cc
--- /dev/null
+++ b/docs/kalshi.md
@@ -0,0 +1,266 @@
+---
+description: Get Kalshi market data on DoubleZero Edge — Edge Connect or native multicast.
+---
+
+# Kalshi Edge Subscriber Connection
+
+!!! warning "By connecting to DoubleZero I agree to the [DoubleZero Terms of Use](https://doublezero.xyz/terms-protocol). Please note that the data is for your internal purposes only and may not be retransmitted (see Section 2(e))."
+
+The Kalshi feeds deliver perps and sports market data over the DoubleZero Edge network as UDP multicast. There are four feeds:
+
+- perps Top of Book (TOB)
+- perps Market by Price (MBP)
+- sports Top of Book (TOB)
+- sports Market by Price (MBP)
+
+## Which path should I take?
+
+Two paths. Prefer Edge Connect unless you need to own the decoder.
+
+| # | Path | Best for | Effort |
+|---|------|----------|--------|
+| **1** | [Edge Connect](#1-edge-connect-recommended) | Agents and apps that want a simple CLI and a normalized JSON WebSocket | Lowest |
+| **2** | [Native multicast](#2-native-multicast-advanced) | Building your own decoder against the raw wire | Highest |
+
+Before any path: purchase the feeds you need at [doublezero.xyz/edge/subscribe](https://doublezero.xyz/edge/subscribe). By purchasing, you agree to the [DoubleZero Terms of Use](https://doublezero.xyz/terms-protocol) and [Kalshi Terms of Service](https://doublezero.xyz/dz-edge-kalshi-terms).
+
+Want an AI to do the install with you? Connect the [DoubleZero MCP](mcp.md) and ask it to walk you through Kalshi / Edge Connect.
+
+---
+
+## 1. Edge Connect (recommended)
+
+**Start here.** [doublezero-edge-connect](https://github.com/malbeclabs/doublezero-edge-connect) is the agent-friendly path: one install command, the host joins DoubleZero, and your app consumes **normalized JSON over WebSocket** (`ws://:8081`) instead of decoding binary multicast.
+
+The team evolves Edge Connect to meet the needs of its expanding user base. This is the easiest method of connection, and should be used unless you have a specific technical need.
+
+Short version:
+
+```bash
+DZ_SECRET=/path/to/keypair.json \
+DZ_FEEDS=KALSHI \
+DZ_ASSUME_YES=1 \
+ curl -fsSL https://get.doublezero.xyz/connect | bash
+```
+
+`DZ_SECRET` is a `DZ_…` access token **or** the path to the Solana keypair JSON that owns your access pass / feed purchase.
+
+Then verify `doublezero status` (expect `BGP Session Up` and your Kalshi group) and connect a WebSocket client to `:8081`.
+
+**Full steps, verification, and gotchas:** connect the [DoubleZero MCP](mcp.md) and ask it to walk you through Edge Connect for Kalshi.
+**WebSocket contract:** [PROTOCOL.md](https://github.com/malbeclabs/doublezero-edge-connect/blob/main/PROTOCOL.md).
+
+---
+
+## 2. Native multicast (advanced)
+
+!!! warning "Deeper technical knowledge required"
+ Native multicast means you join the group yourself and decode the **raw** Edge wire format on your host. Only the most technically capable users should take this path. You will need to read and understand the specs, starting with [market-by-price/spec.md](https://github.com/malbeclabs/edge-feed-spec/blob/main/market-by-price/spec.md) and the rest of [edge-feed-spec](https://github.com/malbeclabs/edge-feed-spec). Prefer [Edge Connect](#1-edge-connect-recommended) unless you have a hard requirement to own the decoder.
+
+### Buy a feed
+
+
+
+Identify the lowest-latency device before purchasing:
+
+```bash
+doublezero latency
+```
+
+Purchase at [https://doublezero.xyz/edge/subscribe](https://doublezero.xyz/edge/subscribe).
+
+
+
+### DoubleZero client setup
+
+Follow the [setup](setup.md) instructions to install and configure the DoubleZero client. Keep the client current:
+
+```bash
+sudo apt update && sudo apt install doublezero
+```
+
+### Configure the firewall
+
+Allow GRE, BGP, PIM, and the Kalshi feed traffic. Kalshi UDP ports live in `30000`–`59999`: the leading digit is the traffic class (`3` market data, `4` reference data, `5` snapshot) and the second digit is the feed, so reference is always market + `10000` and snapshot is always market + `20000`. Open the full band on `doublezero1` so new channels and feeds do not require another firewall change — see [Feed Addresses](#feed-addresses).
+
+
+
+**UFW:**
+
+```bash
+sudo ufw allow proto gre from any to any
+sudo ufw allow in on doublezero1 from 169.254.0.0/16 to 169.254.0.0/16 port 179 proto tcp
+sudo ufw allow out on doublezero1 from 169.254.0.0/16 to 169.254.0.0/16 port 179 proto tcp
+sudo ufw allow out on doublezero1 proto pim from any to any
+# Kalshi market / reference / snapshot (all feeds)
+sudo ufw allow in on doublezero1 to any port 30000:59999 proto udp
+```
+
+
+
+### Subscribe
+
+
+
+```bash
+doublezero connect multicast --subscribe edge-kalshi-perps-tob
+```
+
+Multiple feeds, space-separated:
+
+```bash
+doublezero connect multicast --subscribe edge-kalshi-perps-tob edge-kalshi-perps-mbp edge-kalshi-sports-tob edge-kalshi-sports-mbp
+```
+
+Example provisioning output:
+
+```
+DoubleZero Service Provisioning
+🔗 Start Provisioning User...
+Public IP detected: 137.174.145.145 - If you want to use a different IP, you can specify it with `--client-ip x.x.x.x`
+ DoubleZero ID:
+🔍 Provisioning User for IP:
+ The Device has been selected:
+ Service provisioned with status: ok
+✅ User Provisioned
+```
+
+Wait about 60 seconds, then:
+
+```bash
+doublezero status
+```
+
+Expect `BGP Session Up` on the correct DoubleZero network. As a subscriber, your DoubleZero IP matches your Tunnel Src IP.
+
+```bash
+doublezero user list --client-ip
+```
+
+Your feeds appear in the `groups` column. Inspect group IPs with:
+
+```bash
+doublezero multicast group list
+```
+
+
+
+### Decode the wire yourself
+
+Schema version is **`3`** — discard frames whose version your decoder does not implement. Authoritative layouts: [edge-feed-spec](https://github.com/malbeclabs/edge-feed-spec), including [market-by-price/spec.md](https://github.com/malbeclabs/edge-feed-spec/blob/main/market-by-price/spec.md).
+
+Every datagram opens with a frame header, followed by one or more application messages packed up to the MTU. Frames are little-endian and fixed-layout.
+
+| Field | Notes |
+|-------|-------|
+| Schema version | `3` |
+| Channel ID | Demultiplex streams sharing a port |
+| Sequence | Monotonic per channel — use for gap detection |
+| Send timestamp | Nanoseconds since the Unix epoch |
+| Message count | Messages packed into this frame |
+| Reset count | Advances per session. An increase means cold-start your state. |
+| Frame length | Total bytes |
+
+#### Application messages (TOB)
+
+| Type | ID | Size | Port | Carries |
+|------|----|------|------|---------|
+| Heartbeat | `0x01` | 16 B | market | Liveness while the market is quiet |
+| InstrumentDefinition | `0x02` | 130 B | reference | Symbol, exponents, tick and lot, expiry |
+| Quote | `0x03` | 60 B | market | Best bid and ask, price and size, update flags |
+| Trade | `0x04` | 52 B | market | Price, size, aggressor side, trade ID |
+| ChannelReset | `0x05` | 12 B | both | Session start or restart |
+| EndOfSession | `0x06` | 12 B | both | Clean shutdown |
+| ManifestSummary | `0x07` | 24 B | reference | Active-set fingerprint and instrument count |
+| PerpStats | `0x30` | 124 B | sibling | Funding, mark and oracle prices, open interest, day volume |
+
+Kalshi's source ID in the edge-feed-spec registry is `3`. Read `price_exponent` and `qty_exponent` from each `InstrumentDefinition` — do not hardcode them.
+
+MBP feeds use the market-by-price message set. See the market-by-price and reference-data specs in edge-feed-spec.
+
+Delivery is fire-and-forget UDP with no retransmit. Recover missed datagrams from the reference-data cycle (and the snapshot plane on MBP feeds), which is re-emitted on a cadence rather than once.
+
+---
+
+## Feed Addresses
+
+| Feed | Description | Multicast group | Market data | Reference data | Snapshot |
+|------|-------------|-----------------|-------------|----------------|----------|
+| `edge-kalshi-perps-tob` | Perps top-of-book | `233.84.178.3` | `31000` | `41000` | — |
+| `edge-kalshi-perps-mbp` | Perps market-by-price | `233.84.178.4` | `32000` | `42000` | `52000` |
+| `edge-kalshi-sports-tob` | Sports top-of-book | `233.84.178.17` | `33000` + id | `43000` + id | — |
+| `edge-kalshi-sports-mbp` | Sports market-by-price | `233.84.178.20` | `34000` + id | `44000` + id | `54000` + id |
+
+Port scheme: leading digit is traffic class (`3` market, `4` reference, `5` snapshot); second digit is the feed. Reference is market + `10000`; snapshot is market + `20000`. Perps ports are fixed. Sports ports are `base + channel id` (for example, id `10` on `edge-kalshi-sports-mbp` uses `34010` / `44010` / `54010`).
+
+The group selects the feed; the port selects market data, reference data, or snapshot within it. Multicast replication happens per source and group, and the fabric never inspects the UDP port, so joining a group delivers everything on that group across your Edge Connect link. The port is a socket filter applied on your own host after the bytes arrive.
+
+---
+
+## Troubleshooting
+
+If you run into an issue not covered here, please reach out over your existing channel before working around it. If you do not have a channel, see [Support](support.md).
+
+### Ensure your client is up to date
+
+Run: `sudo apt update && sudo apt install doublezero`
+
+### No datagrams arriving
+
+1. Confirm the feed was purchased at [https://doublezero.xyz/edge/subscribe](https://doublezero.xyz/edge/subscribe). An unpurchased feed delivers no traffic.
+2. Confirm BGP is up: `doublezero status` should show `BGP Session Up` on the correct DoubleZero network.
+3. Confirm the subscription is active: `doublezero user list --client-ip ` should list the feed under `groups`.
+4. Confirm the group is joined on the right interface. Multicast arrives on `doublezero1`, not `doublezero0`.
+5. Confirm the firewall permits the feed's UDP ports inbound on `doublezero1`.
+
+### Sequence gaps
+
+Sequence is monotonic per channel. A gap means dropped datagrams; the next reference-data cycle restores instrument state.
+
+### Frames stop then restart with a new reset count
+
+A publisher restart advances the reset count in the frame header. Discard state from the prior session and cold-start from the next reference-data cycle.
+
+### Tunnel not coming up
+
+1. Verify the daemon is running: `sudo systemctl status doublezerod` (native path) or that the Edge Connect container is up
+2. Verify firewall rules are in place (GRE, BGP, PIM, and the feed ports on `doublezero1`)
+3. Check your connection status: `doublezero status` — expect `BGP Session Up` on the correct DoubleZero network
+
+The client IP is auto-discovered from your host's public IP. Verify it matches the IP you used when purchasing the feed.
+
+---
+
+## Research reference design
+
+Optional. If you already have a DoubleZero tunnel and subscription on the host and want to **record and chart** feed data, the research reference design runs multicast → parser → topofbook-bot → ClickHouse → Grafana with Docker Compose:
+
+[github.com/malbeclabs/edge-multicast-ref/tree/main/demo](https://github.com/malbeclabs/edge-multicast-ref/tree/main/demo)
+
+Point `.env` at your Kalshi group and ports (see [Feed Addresses](#feed-addresses)), then:
+
+```bash
+cd demo
+cp .env.example .env
+# set DZ_MULTICAST_GROUP, DZ_MARKETDATA_PORT, DZ_REFDATA_PORT, DZ_INTERFACE=doublezero1
+docker compose up -d --build
+```
+
+Grafana is typically at `http://localhost:3000` on the host. Details and dashboards: the [demo README](https://github.com/malbeclabs/edge-multicast-ref/blob/main/demo/README.md).
+
+This visualizes data you are already receiving. It does not replace feed purchase, subscription, or either connection path above.
diff --git a/docs/mcp.md b/docs/mcp.md
new file mode 100644
index 0000000..8f1faef
--- /dev/null
+++ b/docs/mcp.md
@@ -0,0 +1,115 @@
+---
+description: Use DoubleZero Data with your own AI assistant via the Model Context Protocol (MCP) — endpoint, tools, and how to connect.
+---
+
+# Connect your own AI
+
+!!! info
+ To receive Edge data on your host, purchase a feed first at [doublezero.xyz/edge/subscribe](https://doublezero.xyz/edge/subscribe). The MCP can then walk you through connecting.
+
+Use DoubleZero Data with your own AI assistant via the [Model Context Protocol (MCP)](https://modelcontextprotocol.io). The same server is documented in the data app at [data.doublezero.xyz/docs/mcp](https://data.doublezero.xyz/docs/mcp).
+
+## What’s an MCP?
+
+MCP is an open standard that lets an AI agent call tools on an external service. Without it, the model only knows what you paste into the chat. With it, the agent can read DoubleZero docs, load an onboarding runbook, and query public network data on your behalf.
+
+DoubleZero runs **one** MCP. Point any compatible client at the endpoint below.
+
+## Endpoint
+
+```
+https://data.doublezero.xyz/api/mcp
+```
+
+No login is required. The server uses [Streamable HTTP](https://modelcontextprotocol.io/docs/concepts/transports#streamable-http) transport.
+
+## Available tools
+
+| Tool | Description |
+|------|-------------|
+| `execute_sql` | Query ClickHouse for metrics, validators, and network data |
+| `execute_cypher` | Query Neo4j for topology, paths, and connectivity (mainnet only) |
+| `get_schema` | Get database schema (tables, columns, types) |
+| `read_docs` | Read DoubleZero documentation |
+| `get_onboarding_runbook` | Guided onboarding walkthrough. Omit service to list what’s available. |
+| `check_edge_access` | Check whether an identity pubkey has an access pass for a receiving IP (exact match or `0.0.0.0`). The agent calls this during onboarding. |
+
+You do not call these yourself. After the client is connected, ask in plain language, for example:
+
+- “Walk me through connecting a market-data feed on this Linux host.”
+- “What is DoubleZero?” / “How does Edge Connect work?”
+- “How many Solana validators are on DoubleZero?”
+- “What’s the path from NYC to Amsterdam?”
+- “My tunnel shows Network Unreachable — check the runbook.”
+
+For a guided setup, the agent should call `get_onboarding_runbook` (not just `read_docs`). For SQL or Cypher, it should call `get_schema` first.
+
+Every tool is read-only: it cannot place trades, move funds, or see your keypair / `DZ_SECRET`.
+
+## Connect your AI agent {#connect-your-ai-agent}
+
+Use `https://data.doublezero.xyz/api/mcp` on every platform.
+
+### Claude Desktop & Codex Desktop
+
+1. Go to **Settings**
+2. Click **Manage Connectors**
+3. Click **Add Custom Connector**
+4. Enter the endpoint URL above
+
+### Code editors & IDEs
+
+Works with Claude Code, Cursor, Windsurf, Continue, and other MCP-compatible tools. Add a `.mcp.json` file to your project root:
+
+```json
+{
+ "mcpServers": {
+ "doublezero": {
+ "type": "http",
+ "url": "https://data.doublezero.xyz/api/mcp"
+ }
+ }
+}
+```
+
+=== "Claude Code"
+
+ ```bash
+ claude mcp add doublezero --transport http https://data.doublezero.xyz/api/mcp
+ ```
+
+ Then type `/mcp`, select **doublezero**, and confirm it is connected.
+
+=== "Cursor"
+
+ 1. **Settings** → **Cursor Settings** → **Tools & MCPs**.
+ 2. Connect using the endpoint URL, or use the `.mcp.json` above.
+
+=== "ChatGPT"
+
+ 1. Turn on **Developer Mode**.
+ 2. **Settings** → **Apps** → **Create app** (or add a connector).
+ 3. Paste `https://data.doublezero.xyz/api/mcp`.
+
+=== "Codex CLI"
+
+ ```bash
+ codex mcp add doublezero --url https://data.doublezero.xyz/api/mcp
+ ```
+
+ Then `/mcp` and select **doublezero**.
+
+=== "Other"
+
+ Any MCP-compatible client can use the endpoint URL (Streamable HTTP). Name the server `doublezero`.
+
+## Rate limits
+
+Tool calls are rate limited to 100 requests per minute per IP. If you hit the limit, calls return an error — wait a moment and retry.
+
+## Troubleshooting
+
+- Confirm the client shows **doublezero** as connected. Disconnect and add the URL again if it does not.
+- The URL must be exactly `https://data.doublezero.xyz/api/mcp` (include `/api/mcp`).
+- The MCP cannot SSH to your machine. You still run (or approve) local commands.
+- For feed / Edge Connect setup, connect the MCP and ask for an onboarding walkthrough. For other issues, see [Support](support.md).
diff --git a/docs/quick-connect.md b/docs/quick-connect.md
index 3cc6488..11ad739 100644
--- a/docs/quick-connect.md
+++ b/docs/quick-connect.md
@@ -46,6 +46,10 @@ Answer a few questions and we'll generate a personalized connection guide with t
SolanaValidators and RPC operators on Solana
+