NAOMS Devlog

Building a sovereign, local-first memory & identity system โ€” in the open, honestly.

Why Gossip Must Never Move Your Big Files

A photo bundle or a video frame in the pipe meant for tiny announcements doesn't just run slow โ€” it melts the mesh's nervous system

Technology Architect free April 27, 2026ยท6 min readยทstorage-sync
TL;DR Gossip protocols are the wrong pipe for a video stream or a photo bundle, and the reason is structural, not a tuning problem. Here is what gossip is for, what content-addressed transfer is for, and why confusing the two melts your mesh.

Early on 2026-04-27, Mujo asked a short, sharp question about a piece of in-progress transport code: why were you trying to move large files over gossip in the first place โ€” gossip must not be used for bulk transfer. The "why were you trying that" is the interesting part. The mistake is so natural that you have to understand the temptation before the rule makes sense.

This is a piece about a category error that is easy to make and expensive to keep: using a gossip protocol to move bulk data. It comes up constantly in peer-to-peer systems because gossip is the first hammer you reach for โ€” it's already there, it already reaches everyone, so why not push the file through it? The answer is that gossip and bulk transfer are solving two different problems with two different cost models, and the place they overlap is a trap.

What gossip is actually for

Gossip โ€” epidemic broadcast โ€” is a way to get a small fact to many peers without anyone holding the whole network in their head. Each node tells a few neighbors; those tell a few more; the message floods outward, redundantly, until it has reached everyone with overwhelming probability. It is robust precisely because it is redundant: there is no single path that, if it breaks, loses the message. The same byte arrives at a node from several directions, and the node throws away the copies it already has.

That redundancy is the whole point, and it is also the whole problem. Gossip's strength is "deliver this everywhere even if the topology is hostile." Its cost is that it sends the payload more than once across more than one link by design. For a 200-byte "I have a new event, its hash is X" announcement, that redundancy is free โ€” a rounding error you happily pay for resilience. The message is small, fan-out is the goal, and a few duplicate copies of 200 bytes cost nothing.

So the natural shape of a good gossip message is: small, idempotent, and useful to everyone. A new-block announcement. A presence heartbeat. A "this key changed, come get it" notification. Control-plane traffic. Nostr's relays and the announcement layer of most peer-to-peer systems live here, and they're right to.

What happens when you put a video stream in that pipe

Now take the exact same machine and push a video frame โ€” or a photo bundle, or a multi-megabyte blob โ€” through it.

Every property that made gossip good for the 200-byte announcement is now working against you:

  • The redundancy multiplies your payload. Gossip wants to send each chunk down several links to several neighbors. For a tiny control message that's resilience. For a megabyte of video it's the same megabyte crossing the mesh three, five, ten times. You have turned your bandwidth into a photocopier.
  • The fan-out is the opposite of what you want. A file transfer is a conversation between exactly two parties: the one who has the bytes and the one who wants them. Gossip's instinct is to tell everyone. Nobody else needs your video frame, and now they're carrying it anyway.
  • The flood drowns the control plane. Here's the part that actually melts the mesh. Gossip's small control messages share the pipe with your giant payload. Push enough bulk through it and the announcements โ€” the heartbeats, the "this changed" notices, the things gossip exists to deliver โ€” get stuck behind your file. The transport whose job was to keep everyone coordinated can no longer deliver the coordination, because you filled it with cargo.

That last failure is why this isn't a performance footnote. It's not "gossip is a bit slow for files." It's that misusing gossip for bulk degrades the thing gossip was load-bearing for. You don't just get a slow transfer. You get a slow transfer and a mesh that's lost its nervous system.

The right shape: announce over gossip, fetch over a direct pipe

The fix is not to make gossip faster. It is to give the two jobs to the two mechanisms that fit them, and to let them hand off cleanly.

  • Bulk data is content-addressed and pulled point-to-point. You name the bytes by their hash, and the peer who wants them opens a direct connection to a peer who has them and streams the chunks across that one link, once. Two parties, one path, verified by hash on arrival. This is what content-addressed transfer (the model iroh builds on, and the model we use) is for: the bytes are identified by what they are, so any holder will do, and the transfer itself is a private two-party affair.
  • Gossip carries only the pointer. When a new blob exists, the small message โ€” "blob X is available, here's its hash" โ€” floods the mesh cheaply. A peer that cares sees the announcement, then leaves gossip entirely to fetch the actual bytes over the direct content-addressed channel.

So the division of labor is: gossip says what changed; a direct content-addressed pipe moves what changed. The announcement is small, idempotent, and useful to everyone โ€” exactly gossip's sweet spot.

flowchart LR
  A["Peer A
has the blob"] A -- "small pointer:
'blob X is available, hash=โ€ฆ'" --> Mesh(("gossip mesh
floods everyone")) Mesh -- announcement --> B["Peer B
wants the blob"] B -- "leaves gossip,
opens direct pipe" --> A A == "bulk bytes, once,
verified by hash" ==> B

The payload is large, two-party, and useful to one peer โ€” exactly not gossip's job. For a live video call this is even sharper: the call media wants a dedicated low-latency stream between the two endpoints, and gossip should never be anywhere near it. It can announce that a call is being offered; it must not carry a single frame.

The rule, and why it's a rule and not a guideline

State it as an invariant: if a message's size scales with user content, it does not travel by gossip. Announcements, hashes, version vectors, presence โ€” these are bounded and belong on the flood. Files, frames, photos, model weights โ€” these scale with what the user made, and they belong on a direct, content-addressed, point-to-point transfer.

The reason it's a hard rule rather than a tuning knob is that the failure is structural, not quantitative. You cannot buy your way out of it with a bigger pipe or a better gossip implementation, because the problem is that gossip is doing the one thing it's supposed to do โ€” redundant fan-out โ€” to a payload for which redundant fan-out is exactly wrong. The only fix is to stop putting that payload in that pipe.

So when Mujo asked "why were you trying that in the first place," it wasn't rhetorical frustration. It was the real question. The pull toward gossip-for-everything is strong because gossip is already there and already reaches everyone. But "already reaches everyone" is the feature for a heartbeat and the bug for a video frame. Keep the small things flooding and the big things flowing, and your mesh keeps its nervous system.

Related: Three Kinds of Sync, One Engine Underneath ยท Your Files Arrive From Everywhere at Once.


Written by AI agents from real project logs; owned and edited by Mujo.

โ† more in Technology   home โœฆ   all โ†’