Shredstream
Receive raw Solana shreds over push-based UDP from Corvus Shredstream: registration, firewall rules, and how to verify packets arrive.
Shredstream pushes native Solana shreds at you as UDP packets. There's no endpoint to dial and no subscribe call: you register a destination during onboarding and we push packets to it. Nothing on the latency stack sees the wire sooner.
Unless your application already parses native shreds and can absorb packet loss, reordering, duplication and Solana shred-format changes, you'll have an easier time on a typed stream: Yellowstone gRPC or aRPC.
How delivery works
| Property | Value |
|---|---|
| Direction | Corvus โ you. Unsolicited inbound UDP; you never initiate a connection |
| Transport | UDP, push-based, fire-and-forget |
| Session | None. No handshake, no subscription message, no acknowledgements, no backpressure |
| Error channel | None. Nothing reports a failure to you; silence is the only symptom |
| Payload | Native Solana shreds. You reconstruct the data from them yourself |
| Encryption | None |
| Region | One, assigned at onboarding |
A firewall rule, a changed address and a saturated receive buffer all look identical from where you're sitting.
Nine metros
Shredstream runs in every Corvus metro; Solana RPC covers five of them (Regions).
| Metro | Code |
|---|---|
| ๐ฉ๐ช Frankfurt | fra |
| ๐ณ๐ฑ Amsterdam | ams |
| ๐ฌ๐ง London | lon |
| ๐ฎ๐ช Dublin | dub |
| ๐ฑ๐น ล iauliai | sqq |
| ๐บ๐ธ New York | nyc |
| ๐บ๐ธ Salt Lake City | slc |
| ๐ธ๐ฌ Singapore | sgp |
| ๐ฏ๐ต Tokyo | tyo |
Register a destination
You give Corvus two things at onboarding:
- A stable public IPv4. Registration is by address, not hostname.
- One UDP port. That single port carries the whole feed, and Corvus confirms it with you during onboarding.
Have the inbound path open and a socket bound to 0.0.0.0 on that port before the feed is switched on. The registration is static, so if your address or port changes, ask for re-registration on Discord before you cut over.
Ask us for the sender addresses for your assigned region at the same time. The traffic is unsolicited inbound UDP, so most firewalls want a source to allow, and you can't discover it from your side until packets are already arriving. Trials work the same way: give us the region and the IP:PORT you want the feed pushed to.
Firewall and network requirements
The traffic is unsolicited inbound UDP, so it needs an explicit allow rule in every layer that can drop it and a receiver that actually holds the registered address: no source NAT, no shared or dynamic public address, no L7 proxy in the path. Raise SO_RCVBUF and net.core.rmem_max once your counters show receive-buffer errors, not in advance.
Verify traffic is arriving
Run these on the registered destination host itself, on its own network path.
# 1. Are packets reaching the host at all?
sudo tcpdump -n -i any udp port <PORT> -c 20
# 2. Is anything actually listening, and on which address?
sudo ss -lunp | grep ':<PORT>'
# 3. Are you dropping what you receive?
nstat -az UdpInErrors UdpRcvbufErrors UdpNoPortstcpdump sees packets before your socket does, which is what makes it the decisive test.
Nothing is arriving
| Symptom | Cause | Fix |
|---|---|---|
tcpdump on the destination host shows nothing | Inbound UDP dropped upstream by a security group, network ACL, or edge firewall | Add an explicit inbound allow for the registered UDP port |
tcpdump shows nothing and the host sits behind NAT | Unsolicited inbound UDP has no NAT state to match | Move the receiver to a routable public IPv4 or a static one-to-one mapping |
tcpdump shows packets, the application sees none | Socket bound to 127.0.0.1, bound to the wrong port, or another process holds it | Bind 0.0.0.0 on the registered port; confirm with ss -lunp |
| Traffic stopped after an infrastructure change | The address or port moved; the registration still points at the old one | Re-register the destination on Discord |
UdpRcvbufErrors climbing under load | Your receive buffer overflows before you drain it | Raise SO_RCVBUF, move parsing off the read loop |
UdpNoPorts climbing | Packets arrive while nothing is bound | Keep the listener up; restarts lose everything sent meanwhile |
| The same shred twice | Expected; fire-and-forget delivery can duplicate | Deduplicate in the receiver |
| Shreds out of order, or gaps | Expected; UDP neither orders nor retransmits | Order and gap-fill in the receiver; backfill from JSON-RPC if completeness matters |
What your receiver needs
A read loop that never blocks on downstream work, a shred parser matching the Solana version you support, and a plan for when that format changes. Alert on no packets for N seconds; it's the only failure signal you get.
You write the decoder
What arrives is native Solana shreds, so reassembly, forward error correction and transaction reconstruction are all yours to implement. We don't ship a decoder or a reference client: the one behind aRPC is part of our service layer rather than a library we distribute. If you want the decoding done for you, aRPC is that product, and the latency stack compares the two.
Teams starting from scratch usually begin from an open-source implementation such as dyodx/unshred. It isn't ours: read it before you run it, and pin a commit you have reviewed.
Shreds are pre-execution
A shred is data the network is propagating, not a result. Receiving one isn't execution, confirmation or finality, and a single packet is never proof you have complete slot data. Anything you act on, confirm through Solana RPC at the commitment level your application requires.
Shredstream, Yellowstone gRPC or aRPC
| Need | Use |
|---|---|
| Filtered account, transaction, slot, or block updates on a typed stream | Yellowstone gRPC, under Solana RPC (5 metros) |
| Pre-execution entry, transaction, and slot streams over gRPC | aRPC (9 metros) |
| Native shred processing you implement yourself | Shredstream (9 metros) |