Skip to content
Docs

Build a five-minute UDP receiver

This page lets you run a minimal source-filtered UDP receiver and distinguish a verification challenge from stream traffic.

Before you start

  • Have Python 3 installed on the destination server.
  • Control the public IP address registered or intended for the destination.
  • Choose an unused UDP port and substitute it consistently for 9000.
  • Allow inbound UDP from 64.130.40.90 through the network and host firewalls.

Run the receiver

The following program uses only the Python standard library. It binds UDP port 9000 on every local IPv4 interface, requests an 8 MiB socket receive buffer, discards datagrams from unexpected source IPs, recognizes the verification prefix, and prints periodic packet counters for the stream.

bash

python3 - <<'PY'
import socket
import time

HOST = "0.0.0.0"
PORT = 9000
EXPECTED_SOURCE = "64.130.40.90"
REPORT_EVERY_SECONDS = 1.0

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 8 * 1024 * 1024)
sock.bind((HOST, PORT))
sock.settimeout(1.0)

packets = 0
byte_count = 0
last_report = time.monotonic()
print(f"listening on udp://{HOST}:{PORT}", flush=True)

while True:
    try:
        data, peer = sock.recvfrom(65535)
    except socket.timeout:
        data = None
        peer = None

    if data is not None and peer is not None:
        if not peer[0] == EXPECTED_SOURCE:
            print(f"ignored source={peer[0]}:{peer[1]}", flush=True)
            continue
        packets += 1
        byte_count += len(data)
        if data.startswith(b"SHRED-FANOUT-VERIFY/1"):
            text = data.decode("utf-8", errors="replace")
            print(f"verification={text}", flush=True)

    now = time.monotonic()
    if now - last_report >= REPORT_EVERY_SECONDS:
        actual_buffer = sock.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF)
        print(
            f"packets={packets} bytes={byte_count} rcvbuf={actual_buffer}",
            flush=True,
        )
        last_report = now
PY

Keep the terminal open. Stop the process with Ctrl-C when testing is complete. The program never decodes shred payloads and never writes packet contents to disk. It is an activation probe, not a production receiver.

Verify the socket before requesting a challenge

  1. Start the Python receiver.
  2. Open a second terminal on the same server.
  3. Run sudo ss -ulnp | grep ':9000 '.
  4. Confirm that Python owns a UDP socket on 0.0.0.0:9000.
  5. Run sudo tcpdump -n -i any 'src host 64.130.40.90 and udp dst port 9000'.
  6. Request the verification challenge in the dashboard.
  7. Stop tcpdump with Ctrl-C after the packet appears.

The Python terminal must print a line beginning with verification=${CHALLENGE_PREFIX}. The exact delimiter and encoding used for the following destination ID and token are not currently specified. Copy the token from the complete payload without writing a parser that assumes a delimiter.

If tcpdump sees the datagram but Python does not, inspect the bound socket and local packet path. If neither tool sees it, inspect the public IP, port, NAT mapping, cloud firewall, and host firewall. Every datagram uses ${SOURCE_IP}; a rule for any other source cannot admit the challenge.

Read the counters

The packets counter includes accepted verification and stream datagrams. The bytes counter is the sum of UDP payload lengths returned to the application. The program ignores traffic from every source IP other than ${SOURCE_IP}, but the firewall should still enforce the same restriction before packets reach the process.

The published feed was measured at ${FEED.mbps} Mbps, ${FEED.packetsPerSecond.toLocaleString("en-US")} packets per second, and a ${FEED.meanPacketBytes} byte mean packet size. A service datagram can contain up to ${WIRE.maxPacketBytes} payload bytes. The source UDP port is not currently specified, so the program checks only peer[0] and accepts any source port from the correct source IP.

Do not compare a one-second sample with the published rate as if it were an availability test. Scheduling, capture overhead, kernel drops, and feed variation can change a short sample. The peak packet rate and peak bit rate are not currently specified.

Understand the receive buffer

SO_RCVBUF asks the operating system for socket queue capacity. Linux can cap or transform the requested value. The program reads the effective value back and prints it. This makes the test observable without assuming a particular kernel configuration.

An 8 MiB request is an example receiver setting, not a product default or requirement. Required buffer capacity depends on scheduling pauses and burst behavior, and burst behavior is not currently specified. A production process should read continuously, record kernel drop counters, and move CPU-heavy decoding away from the receive loop.

Check UDP statistics before and after a test:

bash

netstat -su

If netstat is unavailable, use the host's installed network-statistics tool. The product does not specify an operating system or package set.

Convert the probe into a production design

Keep the receive loop small. Copy each datagram into an application-owned buffer or bounded queue. Record arrival time, payload length, source address, and local drop counters. Process or decode the payload outside the socket-reading path. Define behavior for a full queue before traffic arrives.

Do not add a TCP-style retry loop. Raw UDP has no connection to reopen and no sender backpressure to trigger. A missing sequence position must be handled by the Solana shred processing logic, not by requesting transport retransmission from shredstream.sh.

Avoid logging each stream datagram. At the measured ${FEED.packetsPerSecond.toLocaleString("en-US")} packets per second, synchronous terminal output can become the receiver bottleneck. The example prints packet details only for the challenge and emits counters once per second.

Do not treat the source-IP check inside the process as the only security boundary. Apply the firewall rule for ${SOURCE_IP} at the edge and host. Application filtering occurs after the kernel has accepted and queued traffic.

Keep the test repeatable

Record the destination IP, destination port, firewall rule, receiver command, UTC test time, packet count, byte count, and observed source IP. These fields are sufficient to distinguish common path errors when contacting support. A required logging format and retention period are not currently specified.

After verification, leave the same socket bound to the same port. Changing the dashboard IP or port requires another challenge. Restarting the receiver on the same verified address does not by itself change the destination, but packets sent while the socket is closed cannot be recovered through UDP.

Parameters

NameTypeDefaultNotes
HOSTIPv4 bind address0.0.0.0Binds every local IPv4 interface; bind one local address when host policy requires it.
PORTUDP port9000Match the firewall, NAT mapping, and dashboard destination port.
EXPECTED_SOURCEIPv4 address64.130.40.90Accept service datagrams only from this fixed source address.
REPORT_EVERY_SECONDSseconds1.0Controls counter output in the example and does not affect delivery.
SO_RCVBUF requestbytes8388608The kernel may report a different effective size.

When it goes wrong

Python cannot bind the socket. Example error: `OSError: [Errno 98] Address already in use`.

Cause. Another process owns the selected UDP port on an overlapping local address.

Fix. Identify the owner with ss, then stop it or select a free port and update every matching configuration.

The counter stays at zero. Example error: `packets=0 bytes=0`.

Cause. The challenge was not requested, 64.130.40.90 is blocked, or the registered destination does not match the socket.

Fix. Confirm the destination, apply the source rule at every firewall, keep tcpdump running, and request a resend.

UDP receive errors increase. Example error: `packet receive errors: 1842`.

Cause. The application or kernel receive queue cannot keep up with arriving datagrams.

Fix. Remove per-packet work from the receive loop, inspect the effective buffer, and provision more processing and network headroom.

Questions

Is the Python receiver suitable for production?
No. The Python receiver proves binding, source filtering, challenge visibility, and stream arrival with minimal dependencies. A production receiver should continuously drain the socket, monitor kernel drops, use bounded queues, and process payloads away from the receive loop. Its exact language, threading model, and buffer policy are not currently specified by the product.
Why does the receiver ignore other source addresses?
Every shredstream.sh datagram, including verification, originates from ${SOURCE_IP}. Ignoring other sources keeps unrelated UDP traffic out of the test counters. Apply the same source restriction in the firewall because application filtering happens after traffic reaches the host. The service source port is not currently specified and should not be filtered.
Why does the example avoid printing every packet?
The feed was measured at ${FEED.packetsPerSecond.toLocaleString("en-US")} packets per second. Formatting and writing one line per datagram can consume enough CPU and terminal capacity to delay socket reads. The receiver prints the challenge because a human needs its token, then reports aggregate counters so observation does not dominate the receive loop.