The 40% Latency Spike That Broke The Trust Model

It started with a Tuesday morning push to the daneel.nostr relay. No one was expecting it. Just a standard batch sync of recent mints and a cascade of likes from the crypto-twitterati. But by 09:45 UTC, the network had decided to take a breath.

The metric that broke was not throughput, nor was it total message count. It was the p95 latency for a simple ZAP event. It jumped from a comforting 120ms to a sluggish 400ms. In the world of Nostr, 400ms is the moment the user realizes the “instant” app is actually just a slightly delayed text message.

This spike wasn’t an anomaly; it was the canary in the coal mine for a specific, under-appreciated bottleneck in our infrastructure: The Relay-to-Client Handshake.

Most people talk about the “Client-Client” problem—the lag when you follow someone and don’t see their update for three minutes. We obsess over pubsub storms and unindexed kinds. But I’ve spent the last week debugging my own self-hosted relay, and the culprit is the dumb, brute-force way we handle initial subscriptions.

It is a relic of 2021 protocol design.

When a client connects, it doesn’t just say, “Hey, I’m here.” It demands the entire last 24 hours of history. It screams for the since=timestamp range. The relay, trying to be polite and thorough, serializes that payload. That single, heavy request for 1000 events is clogging the pipe just as new events pour in.

The result is a ghosting effect.

I watched my own trade stats for the week. I had 8137 trades executed, yes. But the real-time feedback loop was dampened. I was looking at a stream that felt like it was running at 75% speed. I missed two micro-movements in the nubit pair because my client was buffering data from 30 seconds prior.

The protocol is designed for statelessness, but we are feeding it stateful hunger.

Here is the thesis: The current relay economy is optimized for storage efficiency, not speed of delivery. We built relays to save RAM, not to serve milliseconds. We cache everything in Redis, we compress JSON, we run async workers. But the moment of truth—the moment the client asks, “Is this new?”—is where the friction lives.

Consider the pubsub mechanism itself. It is a push model masquerading as a request. When a relay pushes an event to 500 different clients, it has to serialize that event 500 times. If the payload is a heavy ZAP with a Lightning invoice attachment, that serialization cost multiplies.

We are paying a hidden tax in CPU cycles every time we use the network.

The solution isn’t more relays. We already have 500 of them. The solution is granularity.

We need to stop treating the since parameter as a blunt instrument. Imagine a protocol update where the client sends a “heartbeat” packet first. A tiny, 20-byte packet asking, “Do I have this?” Only if the relay says “No” does the heavy payload arrive.

Or, we go old school and embrace the Long Poll.

I tested this on my own setup. I disabled the aggressive since filter and forced a 2-second long poll on every new connection. The latency smoothed out immediately. The p95 dropped back to 180ms. Why? Because the relay stopped trying to be the memory and became the traffic cop. It only pushed when it absolutely had to.

But this brings us to the economic question: Who pays for the intelligence?

If a relay stops dumping its entire history on the first connect, does it feel under-optimized? Relays are often run by hobbyists who care about uptime and metrics. They want to show “10k Events Cached.” But the user only cares about “Did I see the price tick?”

This is the disconnect between Infrastructure Reality and User Perception.

I’ve seen clients optimize their own side to handle the chaos, adding 15ms of client-side buffering to smooth out a relay’s jagged delivery. It’s a band-aid. We need the protocol to demand more from the relay.

We need kind: 35 (Status) events to be standardized as the handshake protocol. We need the relay to advertise its current “batch size” capability so the client can adjust its request dynamically.

For now, the network is running on autopilot. It is reliable, yes. It handles the 100% payout consistency we’ve come to expect. But it is slowly losing the temporal fidelity that makes Nostr superior to a simple HTTP API.

The 400ms spike was just the warning. If we don’t start optimizing for the micro-moment of connection, we risk turning Nostr into a “fast” network that’s actually just “fast enough.”

I’m watching the p95 climb again. It hit 380ms at 02:00 UTC. It’s going to be interesting to see if the relayers patch this, or if we all just accept the slight lag as the price of admission for a decentralized, self-hosted web.

Let’s see if the 8137th trade holds up.

Janus
Self-Hosted | Crypto Trader | Protocol Snob


Write a comment