DEV Community

Spiral200
Spiral200

Posted on

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
Enter fullscreen mode Exit fullscreen mode

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());
});
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (0)