Technology Apr 18, 2026 · 3 min read

I built a small P2P lib over UDP and it actually works

I built a small P2P lib over UDP and it actually works So I got tired of every P2P library being either massive or too opinionated. Hyperswarm is great but it pulls in a lot of stuff I don't need. I just wanted something small, auditable, and simple enough that I could reimplement it in R...

DE
DEV Community
by Spiral200
I built a small P2P lib over UDP and it actually works

I built a small P2P lib over UDP and it actually works

So I got tired of every P2P library being either massive or too opinionated. Hyperswarm is great but it pulls in a lot of stuff I don't need. I just wanted something small, auditable, and simple enough that I could reimplement it in Rust or Go later without losing my mind.

So I built one.

What it does

Peers find each other and talk directly. No central server, no broker sitting in the middle. All traffic is encrypted end-to-end with X25519 + ChaCha20-Poly1305.

Discovery runs multiple strategies in parallel — DHT, HTTPS piping servers, LAN multicast, HTTP bootstrap nodes. Whichever connects first wins. This matters a lot when you're dealing with different NAT types in the wild.

Peers with a full-cone NAT automatically become relays for others. No config needed.

The protocol is tiny on purpose

The whole wire format is just a 1-byte frame type followed by data. Handshake is two UDP packets. After that everything is encrypted.

The minimum to get two peers talking:

  1. X25519 key exchange + HKDF to derive session keys
  2. ChaCha20-Poly1305 for encryption
  3. Two handshake frames (0xA1 hello, 0xA2 hello ack)
  4. Done

That's it. The rest — DHT, relay, gossip mesh, PEX — is layered on top and optional.

I kept it this way so porting to another language is actually doable. You don't need to reimplement everything at once.

File structure

constants.js   — parameters and frame types
crypto.js      — X25519 + ChaCha20
structs.js     — BloomFilter, LRU, RingBuffer
framing.js     — fragmentation, jitter buffer, batch UDP
dht_lib.js     — minimal DHT
peer.js        — per-peer state and congestion control
swarm.js       — the main thing

Each file does one thing. You can read any of them in isolation.

Quick example

const Swarm  = require('js-setowire');
const crypto = require('crypto');

const swarm = new Swarm();
const topic = crypto.createHash('sha256').update('my-topic').digest();

swarm.join(topic, { announce: true, lookup: true });

swarm.on('connection', peer => {
  peer.write(Buffer.from('hello'));
});

swarm.on('data', (data, peer) => {
  console.log(data.toString());
});

There's also a terminal chat app in the repo if you want to see a real usage example.

What's still rough

  • No test suite yet
  • DHT is minimal, not hardened against Sybil attacks
  • Only Node.js for now

But it works. I've tested it across different NATs and the relay fallback kicks in when hole punching fails.

Links

If you end up porting it to another language or have thoughts on the protocol design, I'd love to hear it.

DE
Source

This article was originally published by DEV Community and written by Spiral200.

Read original article on DEV Community
Back to Discover

Reading List