<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Spiral200</title>
    <description>The latest articles on DEV Community by Spiral200 (@spiral2000999).</description>
    <link>https://dev.to/spiral2000999</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3885349%2F048ac4a7-d71d-4f70-8417-0fbdf369b301.png</url>
      <title>DEV Community: Spiral200</title>
      <link>https://dev.to/spiral2000999</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/spiral2000999"/>
    <language>en</language>
    <item>
      <title>I built a small P2P lib over UDP and it actually works</title>
      <dc:creator>Spiral200</dc:creator>
      <pubDate>Sat, 18 Apr 2026 01:33:02 +0000</pubDate>
      <link>https://dev.to/spiral2000999/i-built-a-small-p2p-lib-over-udp-and-it-actually-works-1e1i</link>
      <guid>https://dev.to/spiral2000999/i-built-a-small-p2p-lib-over-udp-and-it-actually-works-1e1i</guid>
      <description>&lt;h1&gt;
  
  
  I built a small P2P lib over UDP and it actually works
&lt;/h1&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;So I built one.&lt;/p&gt;




&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Peers with a full-cone NAT automatically become relays for others. No config needed.&lt;/p&gt;




&lt;h2&gt;
  
  
  The protocol is tiny on purpose
&lt;/h2&gt;

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

&lt;p&gt;The minimum to get two peers talking:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;X25519 key exchange + HKDF to derive session keys&lt;/li&gt;
&lt;li&gt;ChaCha20-Poly1305 for encryption&lt;/li&gt;
&lt;li&gt;Two handshake frames (&lt;code&gt;0xA1&lt;/code&gt; hello, &lt;code&gt;0xA2&lt;/code&gt; hello ack)&lt;/li&gt;
&lt;li&gt;Done&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's it. The rest — DHT, relay, gossip mesh, PEX — is layered on top and optional.&lt;/p&gt;

&lt;p&gt;I kept it this way so porting to another language is actually doable. You don't need to reimplement everything at once.&lt;/p&gt;




&lt;h2&gt;
  
  
  File structure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each file does one thing. You can read any of them in isolation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Swarm&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;js-setowire&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;crypto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;swarm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Swarm&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;topic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-topic&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;swarm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;announce&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;swarm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;connection&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;peer&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;swarm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;peer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's also a terminal chat app in the repo if you want to see a real usage example.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's still rough
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;No test suite yet&lt;/li&gt;
&lt;li&gt;DHT is minimal, not hardened against Sybil attacks&lt;/li&gt;
&lt;li&gt;Only Node.js for now&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But it works. I've tested it across different NATs and the relay fallback kicks in when hole punching fails.&lt;/p&gt;




&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/setowire-org/js-setowire" rel="noopener noreferrer"&gt;https://github.com/setowire-org/js-setowire&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you end up porting it to another language or have thoughts on the protocol design, I'd love to hear it.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>p2p</category>
      <category>opensource</category>
      <category>networking</category>
    </item>
  </channel>
</rss>
