Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,6 @@ For more details, see the [DoubleZero Protocol](https://doublezero.xyz/whitepape
## Next Steps

* *For Users* - Start with our [Quick Connect Wizard](quick-connect.md) to create a custom setup based on user type or go straight to [initial setup](setup.md) and follow the setup flow manually.
* *For AI agents* - Connect Claude, Cursor, ChatGPT, or Codex to the [DoubleZero MCP](mcp.md) and ask it to walk you through setup.
* *For Contributors* - See how to [Contribute](contribute-overview.md) additional capacity to grow the DoubleZero network further.
* *More Information* - Read more about what makes up the [Architecture](architecture.md) of the DoubleZero network.
39 changes: 34 additions & 5 deletions docs/javascripts/connection-wizard.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
network: { 'mainnet-beta': 'Mainnet-Beta', 'testnet': 'Testnet' },
os: { 'deb': 'Ubuntu / Debian', 'rpm': 'Rocky / RHEL' },
firewall: { 'iptables': 'iptables', 'ufw': 'UFW' },
tenant: { 'solana': 'Solana', 'shelby': 'Shelby', 'new-tenant': 'New Tenant' },
tenant: { 'solana': 'Solana', 'kalshi': 'Kalshi', 'shelby': 'Shelby', 'new-tenant': 'New Tenant' },
usertype: { 'validator': 'Validator', 'rpc': 'RPC' },
connection: { 'unicast': 'Unicast (IBRL)', 'multicast': 'Multicast', 'both': 'Both' },
multicastrole: { 'publisher': 'Publisher', 'subscriber': 'Subscriber' }
Expand All @@ -39,7 +39,7 @@
if (state.tenant === 'new-tenant') return null;
return 'firewall';
case 'firewall':
if (state.tenant === 'shelby') return null;
if (state.tenant === 'shelby' || state.tenant === 'kalshi') return null;
return 'usertype';
case 'usertype': return 'connection';
case 'connection':
Expand Down Expand Up @@ -242,6 +242,11 @@
sources.push({ page: '../setup/', section: 'firewall-gre-bgp-' + s.firewall });
if (s.tenant == 'solana') {
sources.push({ page: connectionPage(s).page, section: 'firewall-' + s.firewall });
} else if (s.tenant === 'kalshi') {
sources.push({
page: '../kalshi/',
section: 'kalshi-firewall-' + s.firewall
});
}
return sources;
}
Expand Down Expand Up @@ -288,18 +293,34 @@
show: function(s) { return s.usertype === 'rpc' || s.tenant === 'shelby'; },
sources: function(s) {
var page = s.tenant === 'shelby'
? '../Shelby%20Permissioned%20Connection/'
? '../shelby/'
: '../Permissioned%20Connection/';
return [{ page: page, section: 'rpc-onboarding' }];
}
},
{
id: 'kalshi-buy-feed',
title: 'Buy a Feed',
show: function(s) { return s.tenant === 'kalshi'; },
sources: function() {
return [{ page: '../kalshi/', section: 'kalshi-buy-feed' }];
}
},
{
id: 'kalshi-subscribe',
title: 'Subscribe to the Kalshi Feeds',
show: function(s) { return s.tenant === 'kalshi'; },
sources: function() {
return [{ page: '../kalshi/', section: 'kalshi-subscribe' }];
}
},
{
id: 'connect-ibrl',
title: 'Connect in IBRL Mode',
show: function(s) { return s.tenant === 'shelby' || s.connection === 'unicast' || s.connection === 'both'; },
sources: function(s) {
if (s.tenant === 'shelby') {
return [{ page: '../Shelby%20Permissioned%20Connection/', section: 'rpc-connect-ibrl' }];
return [{ page: '../shelby/', section: 'rpc-connect-ibrl' }];
}
if (s.usertype === 'rpc') {
return [{ page: '../Permissioned%20Connection/', section: 'rpc-connect-ibrl' }];
Expand Down Expand Up @@ -348,6 +369,12 @@
labelFor('os', state.os) + '</strong>.</p>';
}

if (state.tenant === 'kalshi') {
return '<p class="wizard-summary-line">Subscribe to the <strong>Kalshi</strong> market-data ' +
'feeds on DoubleZero <strong>' + networkLabel + '</strong> on <strong>' +
labelFor('os', state.os) + '</strong>.</p>';
}

var connLabel = state.connection === 'both' ? 'unicast + multicast'
: state.connection === 'unicast' ? 'unicast (IBRL)' : 'multicast';
var roleNote = state.multicastrole ? ' as a ' + state.multicastrole : '';
Expand All @@ -368,7 +395,9 @@
? '<li><a href="../DZ%20Mainnet-beta%20Connection/">Mainnet-Beta Connection</a></li>'
: '<li><a href="../DZ%20Testnet%20Connection/">Testnet Connection</a></li>') +
(state.tenant === 'shelby'
? '<li><a href="../Shelby%20Permissioned%20Connection/">Shelby Permissioned Connection</a></li>' : '') +
? '<li><a href="../shelby/">Shelby</a></li>' : '') +
(state.tenant === 'kalshi'
? '<li><a href="../kalshi/">Kalshi</a></li>' : '') +
(state.usertype === 'rpc' && state.tenant !== 'shelby'
? '<li><a href="../Permissioned%20Connection/">Permissioned Connection</a></li>' : '') +
(state.connection !== 'unicast'
Expand Down
11 changes: 7 additions & 4 deletions docs/javascripts/header-controls-order.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
215 changes: 215 additions & 0 deletions docs/kalshi-runbook.md
Original file line number Diff line number Diff line change
@@ -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://<host>: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 <feed-account-name>
```

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
Loading
Loading