Skip to content

Repository files navigation

DoubleZero Edge Multicast Reference Designs

Reference code for both sides of a DoubleZero edge multicast feed: libraries for publishing one, reference implementations for subscribing to one.

DoubleZero delivers data as GRE-encapsulated UDP multicast. The kernel handles GRE de-encapsulation, so applications see clean UDP.

Where
Publishing a feed Rust crates a venue publisher is built from rust/codec, rust/publisher
Subscribing to market data Parsers, book-builders, demo stack go/, demo/
Subscribing to Solana shreds Receive-only receivers, kernel sockets and XDP rust/, go/
Recording a feed Keeps the bytes that arrived, with the recorder's own losses inside the archive rust/recorder

Wire formats are specified in edge-feed-spec, which is also the authority for vocabulary here.

Transport

The feed arrives on a GRE tunnel interface (e.g. doublezero1) as UDP multicast. The DoubleZero client handles tunnel setup and heartbeat responses; the subscribing designs here are receive-only.

Physical NIC:  Eth → Outer IP → GRE → Inner IP → UDP → payload
GRE interface: Inner IP (148.51.x.x → 233.84.178.1) → UDP → payload
$ ip a s doublezero1
26: doublezero1@NONE: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1476 qdisc noqueue state UNKNOWN
    link/gre 64.130.37.175 peer 4.42.212.122
    inet 169.254.10.233/31 scope link doublezero1

gre-decap is an optional XDP program that strips GRE inline on the physical NIC, so socket-based applications can receive the feed without a tunnel interface.

Publishing a feed

BRINGING-UP-A-FEED.md is the path in order: what a venue implements, how its publisher is built and configured, how the recorder is pointed at it, and what the infrastructure repositories own.

Crates a publisher depends on from its own repository. See rust/README.md.

Crate Owns
dz-edge-core Datagram and message headers, sequencing, receive-side walk, decimal conversion
dz-edge-tob Top-of-Book: Quote, Trade
dz-edge-refdata Reference data: InstrumentDefinition, ManifestSummary
dz-edge-mbp Market-by-Price, the first depth feed: LevelUpdate, BookClear, and the three snapshot messages
dz-publisher-metrics The normative dz_publisher_* Prometheus set

Subscribing to market data

Each feed has a parser that subscribes, decodes and republishes records on a Unix socket, and a book-builder that maintains book state and persists to ClickHouse.

Feed Spec Parser Book-builder
Top-of-Book & Trades spec topofbook-parser topofbook-bot
Market-by-Order spec marketbyorder-parser marketbyorder-bot
Market-by-Price spec marketbyprice-parser marketbyprice-bot
Component
topofbook-parser Decodes datagrams, writes JSON/CSV to a file or Unix socket
topofbook-bot Per-symbol top-of-book state as Prometheus metrics, optional ClickHouse writes
marketbyorder-parser Three-port subscriber and wire decoder, fans decoded JSONL out on a Unix socket
marketbyorder-bot Order-keyed books; per-event rows and top-N snapshots to ClickHouse
marketbyprice-parser Three-port subscriber and wire decoder, fans decoded JSONL out on a Unix socket
marketbyprice-bot Price-keyed L2 books across channels; per-event rows, snapshot levels and top-N snapshots to ClickHouse

demo/ runs all of it — parsers, book-builders, ClickHouse, Grafana — with one command and pre-provisioned dashboards for all three feeds.

Subscribing to Solana shreds

Two packet types on the shred feed: shred packets on port 7733 (~1247–1272 bytes), and 4-byte DoubleZero liveness probes on port 5765.

Language Kernel sockets XDP
Rust rust/kernel-receiver rust/xdp-receiver
Go go/kernel-receiver go/xdp-receiver
C planned planned

Kernel sockets are the simple path, XDP the fast one.

Recording a feed

A recorder keeps the bytes of a feed and its own losses, so that did the publisher send what the spec says it must, and did it arrive? can be answered after the fact — hours later, or against a rule that did not exist when the traffic passed. Without the bytes there is nothing left to ask; without the recorder's own losses, every gap it caused is charged to the publisher.

It decodes nothing on the record path. A decoder rejects the datagram most worth keeping, so a datagram whose bytes we cannot explain is archived anyway. See rust/recorder/README.md.

Crate Owns
dz-recorder-core The types every other crate speaks, the Source/Sink/Observer traits, and the configuration
dz-recorder-capture Live capture: membership, kernel receive timestamps, drop accounting, rejoin
dz-recorder-archive The pcapng writer: rotation, compression, hashing, manifest, staging watermark
dz-recorder-replay An archive read back as a Source, plus the synthetic publisher the tests use
dz-recorder-health The header-only observer and the normative dz_recorder_* Prometheus set
dz-recorder-loss Sequence loss over any Source, live or replayed, under one rule set
dz-recorder-e2e The chain end to end, and the specification's own rule set applied to it
dz-recorder The binary that wires capture, archive and health together

Two capture modes sit behind one Source. AF_PACKET is the default: it records what the network delivered, so a datagram the recorder's own socket would have lost to receive-queue overflow is still in the archive and correctly attributed. Socket mode is the fallback where CAP_NET_RAW is unavailable; it synthesises the IP and UDP headers and records that it did, so no reader mistakes a synthesised field for a captured one.

Repository layout

Path
rust/codec, rust/publisher, rust/recorder Publisher, codec and recorder crates, one Cargo workspace (README)
rust/kernel-receiver, rust/xdp-receiver Shred receivers, excluded from that workspace
go/ Parsers, book-builders, Go shred receivers
gre-decap/ XDP GRE decapsulator
demo/ Docker Compose stack and Grafana dashboards
testdata/golden/ Wire vectors the Go and Rust decoders both assert against
docs/ Design documents and plans (index)
pcaps/ Sample captures; read with Wireshark's solana.shreds dissector on UDP 7733

Vocabulary

Prose follows GLOSSARY.md: a datagram is one UDP payload, a header plus N messages; a channel is a shard named by Channel ID; a parser decodes and republishes; a book-builder consumes parser output.

The -bot paths and dz_bot_* metric names predate that glossary and are kept because CI jobs, binaries and dashboards reference them. Frame Length is the spec's own field name at offset 22, so parse_errors_total{reason="frame_length"} matches it deliberately.

License

Apache 2.0. See LICENSE.

About

Reference designs for consuming DoubleZero Edge multicast feeds over kernel sockets and XDP

Topics

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages