<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Peerix – Blog</title><link>https://peerix.dev/blog/</link><description>Recent content in Blog on Peerix</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Thu, 25 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://peerix.dev/blog/index.xml" rel="self" type="application/rss+xml"/><item><title>Peerix v0.5 Released</title><link>https://peerix.dev/blog/release-v0-5/</link><pubDate>Thu, 25 Jun 2026 00:00:00 +0000</pubDate><guid>https://peerix.dev/blog/release-v0-5/</guid><description>
&lt;p>Peerix &lt;code>v0.5&lt;/code> is now available. This is a significant update that reimagines how data travels between peers — introducing streaming transfers with progress tracking, abortable transmissions, and optional metadata — while simplifying the underlying channel architecture.&lt;/p>
&lt;h2>Highlights: Streaming Data Transfers&lt;span class="hx:absolute hx:-mt-20" id="highlights-streaming-data-transfers">&lt;/span>
&lt;a href="#highlights-streaming-data-transfers" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h2>&lt;p>v0.5 fundamentally changes how &lt;code>send&lt;/code> works and how you receive data over channels. Instead of a fire-and-forget API, &lt;code>send&lt;/code> now returns an async iterable that doubles as a promise — letting you track transfer progress or simply await completion.&lt;/p>
&lt;h3>ReadableStream for incoming and outgoing data&lt;span class="hx:absolute hx:-mt-20" id="readablestream-for-incoming-and-outgoing-data">&lt;/span>
&lt;a href="#readablestream-for-incoming-and-outgoing-data" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h3>&lt;p>The &lt;code>send&lt;/code> method now supports streaming any data through a single channel. Large payloads are automatically chunked and buffered on the wire, so you can transmit files and binary blobs without worrying about maximum message size limits.&lt;/p>
&lt;p>On the receiving side, the &lt;code>channel:message&lt;/code> event fires as soon as the first chunk arrives. The &lt;code>data&lt;/code> payload is a &lt;code>ReadableStream&lt;/code>/&lt;code>Promise&lt;/code> that you can iterate over to receive chunks incrementally — or consume as a promise to get the fully assembled message when you don&amp;rsquo;t need progress tracking.&lt;/p>
&lt;h3>Progress tracking&lt;span class="hx:absolute hx:-mt-20" id="progress-tracking">&lt;/span>
&lt;a href="#progress-tracking" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h3>&lt;p>The transfer object returned by &lt;code>send&lt;/code> is an async iterator. By looping over it, you can observe how much data has been sent at each step:&lt;/p>
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">
&lt;div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">file&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">File&lt;/span>&lt;span class="p">([&lt;/span>&lt;span class="k">new&lt;/span> &lt;span class="nx">Uint8Array&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1024&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">1024&lt;/span>&lt;span class="p">)],&lt;/span> &lt;span class="s2">&amp;#34;example.dat&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">transfer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">peer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">send&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">file&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">label&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;chat&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// channel label
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">info&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">file&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">size&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">file&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">size&lt;/span> &lt;span class="p">},&lt;/span> &lt;span class="c1">// metadata
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nx">signal&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">AbortSignal&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">timeout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">10000&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="c1">// abort signal
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// track the progress of the transfer
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">for&lt;/span> &lt;span class="kr">await&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">progress&lt;/span> &lt;span class="k">of&lt;/span> &lt;span class="nx">transfer&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">label&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">current&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">total&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">progress&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">percent&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">Math&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">round&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">current&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="nx">total&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">100&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`[&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">id&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">:&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">label&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">] Sending... &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">percent&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">%`&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/div>&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copy code"
aria-label="Copy code"
data-copied-label="Copied!"
>
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;/button>
&lt;/div>
&lt;/div>
&lt;p>Receiving the file and tracking progress on the other end:&lt;/p>
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">
&lt;div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">peer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">on&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;channel:message&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">remote&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">label&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">data&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">info&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">e&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">let&lt;/span> &lt;span class="nx">current&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">chunks&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// read data by chunks
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="kr">await&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">chunk&lt;/span> &lt;span class="k">of&lt;/span> &lt;span class="nx">data&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">chunks&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">push&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">chunk&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">current&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="nx">chunk&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">length&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">percent&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">Math&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">round&lt;/span>&lt;span class="p">((&lt;/span>&lt;span class="nx">current&lt;/span> &lt;span class="o">/&lt;/span> &lt;span class="nx">info&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">size&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">100&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`[&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">remote&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">id&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">:&lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">label&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">] Receiving... &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">percent&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">%`&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">file&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">File&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">chunks&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">info&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Received:&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">file&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// cancel the stream if needed
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// data.cancel();
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">});&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/div>&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copy code"
aria-label="Copy code"
data-copied-label="Copied!"
>
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;/button>
&lt;/div>
&lt;/div>
&lt;h3>Abortable transfers&lt;span class="hx:absolute hx:-mt-20" id="abortable-transfers">&lt;/span>
&lt;a href="#abortable-transfers" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h3>&lt;p>You can cancel a transmission at any time using &lt;code>AbortSignal&lt;/code>. The example above uses &lt;code>AbortSignal.timeout(10000)&lt;/code> to auto-cancel after 10 seconds, but you can also create an &lt;code>AbortController&lt;/code> and call &lt;code>.abort()&lt;/code> manually whenever your application logic dictates.&lt;/p>
&lt;p>Cancellation works on the receiver side as well. Since incoming &lt;code>data&lt;/code> is a &lt;code>ReadableStream&lt;/code>, you can call &lt;code>data.cancel()&lt;/code> at any point during iteration to stop receiving further chunks.&lt;/p>
&lt;h3>Optional Metadata&lt;span class="hx:absolute hx:-mt-20" id="optional-metadata">&lt;/span>
&lt;a href="#optional-metadata" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h3>&lt;p>The new &lt;code>info&lt;/code> option on &lt;code>send&lt;/code> lets you attach arbitrary metadata alongside your data. This metadata is delivered with every &lt;code>channel:message&lt;/code> event, so the receiving peer can inspect it without waiting for the full payload. This is useful for describing the payload — file names, sizes, types, or any custom information the receiver needs to process the message correctly.&lt;/p>
&lt;h2>Breaking changes&lt;span class="hx:absolute hx:-mt-20" id="breaking-changes">&lt;/span>
&lt;a href="#breaking-changes" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h2>&lt;ol>
&lt;li>Data is now sent through a single data channel per peer. If you omit the &lt;code>label&lt;/code> option, messages are routed to the &lt;code>default&lt;/code> channel instead of all existing channels.&lt;/li>
&lt;li>Incoming data is now either a &lt;code>ReadableStream&lt;/code> or a &lt;code>Promise&lt;/code>. Read the stream to track receiving progress or use the promise to get a specific data type when the full message is received.&lt;/li>
&lt;/ol>
&lt;h2>Peer Serialization&lt;span class="hx:absolute hx:-mt-20" id="peer-serialization">&lt;/span>
&lt;a href="#peer-serialization" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h2>&lt;p>A new &lt;code>toJSON&lt;/code> method was added to both local and remote peers. Use it to serialize peer identity information for logging, debugging, or state persistence:&lt;/p>
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">
&lt;div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">peer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">toJSON&lt;/span>&lt;span class="p">());&lt;/span> &lt;span class="c1">// { id: &amp;#34;...&amp;#34;, room: &amp;#34;...&amp;#34;, ... }
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/div>&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copy code"
aria-label="Copy code"
data-copied-label="Copied!"
>
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;/button>
&lt;/div>
&lt;/div>
&lt;h2>Testing Improvements&lt;span class="hx:absolute hx:-mt-20" id="testing-improvements">&lt;/span>
&lt;a href="#testing-improvements" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h2>&lt;p>Unit tests were added for internal utilities and several signaling drivers. This improves confidence in core functionality and makes future refactoring safer.&lt;/p>
&lt;h2>Upgrade Notes&lt;span class="hx:absolute hx:-mt-20" id="upgrade-notes">&lt;/span>
&lt;a href="#upgrade-notes" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h2>&lt;p>When upgrading to &lt;code>v0.5&lt;/code>, review the following breaking changes:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>&lt;code>send&lt;/code> API&lt;/strong>: The return value is now an async iterable transfer object instead of a plain Promise. If you previously awaited &lt;code>send&lt;/code> directly, it will still work (the transfer resolves when delivery completes), but you can now also iterate over it for progress updates.&lt;/li>
&lt;li>&lt;strong>Incoming data shape&lt;/strong>: The &lt;code>data&lt;/code> in &lt;code>channel:message&lt;/code> events is now always a &lt;code>ReadableStream&lt;/code>/&lt;code>Promise&lt;/code>. To receive the full message, await it: &lt;code>const payload = await event.data&lt;/code>. To track receiving progress, iterate with &lt;code>for await (const chunk of event.data)&lt;/code>.&lt;/li>
&lt;li>&lt;strong>Single channel&lt;/strong>: Data is sent over one data channel instead of multiple. The &lt;code>default&lt;/code> label is used when no &lt;code>label&lt;/code> is provided.&lt;/li>
&lt;/ul>
&lt;h2>Conclusion&lt;span class="hx:absolute hx:-mt-20" id="conclusion">&lt;/span>
&lt;a href="#conclusion" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h2>&lt;p>v0.5 transforms data channel messaging into a first-class streaming experience with progress visibility and transfer control built in. For full details on these changes, please visit our updated resources:&lt;/p>
&lt;ul>
&lt;li>📚 &lt;a href="https://peerix.dev/docs/"target="_blank" rel="noopener">Documentation&lt;svg class="hx:inline hx:rtl:rotate-270 hx:align-baseline" height="1em" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
&lt;path d="m9.1716 7.7574h7.0711m0 0v7.0711m0-7.0711-8.4853 8.4853" stroke-linecap="round" stroke-linejoin="round"/>
&lt;/svg>&lt;/a>&lt;/li>
&lt;li>📑 &lt;a href="https://api.peerix.dev/"target="_blank" rel="noopener">API Reference&lt;svg class="hx:inline hx:rtl:rotate-270 hx:align-baseline" height="1em" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
&lt;path d="m9.1716 7.7574h7.0711m0 0v7.0711m0-7.0711-8.4853 8.4853" stroke-linecap="round" stroke-linejoin="round"/>
&lt;/svg>&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Peerix v0.4 Released</title><link>https://peerix.dev/blog/release-v0-4/</link><pubDate>Thu, 28 May 2026 00:00:00 +0000</pubDate><guid>https://peerix.dev/blog/release-v0-4/</guid><description>
&lt;p>Peerix &lt;code>v0.4&lt;/code> is now available. Peerix continues its mission of simplifying WebRTC development by abstracting the heavy lifting of signaling and connection management into a minimal, transport-agnostic API. This release focuses on expanding our signaling transport options for production environments, refining our API for a better developer experience, and optimizing performance under the hood.&lt;/p>
&lt;h2>Highlights: New Signaling Drivers for Production Scale&lt;span class="hx:absolute hx:-mt-20" id="highlights-new-signaling-drivers-for-production-scale">&lt;/span>
&lt;a href="#highlights-new-signaling-drivers-for-production-scale" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h2>&lt;p>While our earlier releases focused on getting peers connected, v0.4 is about moving from local prototypes to distributed production environments. We&amp;rsquo;ve added two major signaling drivers to support industry-standard messaging infrastructure.&lt;/p>
&lt;h3>MQTT Driver&lt;span class="hx:absolute hx:-mt-20" id="mqtt-driver">&lt;/span>
&lt;a href="#mqtt-driver" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h3>&lt;p>The &lt;code>MqttDriver&lt;/code> enables signaling via the MQTT protocol and is specifically designed for MQTT over WebSockets. This allows developers to leverage robust brokers like Mosquitto for their signaling needs. It is an ideal choice for lightweight pub/sub signaling within existing messaging ecosystems.&lt;/p>
&lt;ul>
&lt;li>Requires the external &lt;code>mqtt&lt;/code> module.&lt;/li>
&lt;li>Requires a running MQTT broker, for example, a &lt;a href="https://peerix.dev/docs/servers/self-hosting/#mosquitto-server">Mosquitto server&lt;/a>.&lt;/li>
&lt;li>Documentation: &lt;a href="https://peerix.dev/docs/drivers/mqtt/">MQTT Driver&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3>Centrifuge Driver&lt;span class="hx:absolute hx:-mt-20" id="centrifuge-driver">&lt;/span>
&lt;a href="#centrifuge-driver" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h3>&lt;p>The &lt;code>CentrifugeDriver&lt;/code> integrates with the Centrifugo real-time messaging server. This driver is built for developers who need high fan-out capabilities and reliable reconnection support in high-traffic production environments.&lt;/p>
&lt;ul>
&lt;li>Requires the external &lt;code>centrifuge&lt;/code> module.&lt;/li>
&lt;li>Requires a running &lt;a href="https://peerix.dev/docs/servers/self-hosting/#centrifugo-server">Centrifugo server&lt;/a>.&lt;/li>
&lt;li>Documentation: &lt;a href="https://peerix.dev/docs/drivers/centrifuge/">Centrifuge Driver&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3>SSE Driver Enhancements&lt;span class="hx:absolute hx:-mt-20" id="sse-driver-enhancements">&lt;/span>
&lt;a href="#sse-driver-enhancements" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h3>&lt;p>The &lt;code>SseDriver&lt;/code> has been updated to default to the Mercure-compatible endpoint (&lt;code>/.well-known/mercure&lt;/code>), making it easier for developers using the Mercure hub to get started without additional configuration. This change simplifies setup and reduces friction for those leveraging Mercure for their signaling needs.&lt;/p>
&lt;ul>
&lt;li>No external modules required.&lt;/li>
&lt;li>Requires a running &lt;a href="https://peerix.dev/docs/servers/self-hosting/#mercure-server">Mercure server&lt;/a> or another SSE-compatible server (with a code example provided).&lt;/li>
&lt;li>Documentation: &lt;a href="https://peerix.dev/docs/drivers/sse/">SSE Driver&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2>Improved Developer Experience: Local Testing with Docker&lt;span class="hx:absolute hx:-mt-20" id="improved-developer-experience-local-testing-with-docker">&lt;/span>
&lt;a href="#improved-developer-experience-local-testing-with-docker" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h2>&lt;p>To lower the barrier for local development, v0.4 now includes example code snippets and configurations for running signaling backends locally using Docker. You can now quickly spin up instances of NATS, Mercure, Mosquitto, and Centrifugo with standardized setups. This ensures that your local testing environment mirrors your production infrastructure as closely as possible.&lt;/p>
&lt;h2>API Refinements&lt;span class="hx:absolute hx:-mt-20" id="api-refinements">&lt;/span>
&lt;a href="#api-refinements" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h2>&lt;p>In our ongoing effort to create a more intuitive and consistent API, we have made some key changes to method naming conventions. The term &lt;code>publish&lt;/code> has been reserved exclusively for the Signaling Layer to align with standard messaging terminology. For the Application Layer, we have introduced &lt;code>share&lt;/code> and &lt;code>unshare&lt;/code> to better describe how media and data streams are managed. This change helps avoid confusion between signaling events and application-level actions.&lt;/p>
&lt;p>&lt;strong>Migration Mapping&lt;/strong>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Old Method&lt;/th>
&lt;th>New Method&lt;/th>
&lt;th>Layer&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>Driver.dispatch&lt;/code>&lt;/td>
&lt;td>&lt;code>Driver.publish&lt;/code>&lt;/td>
&lt;td>Driver&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>Peer.publish&lt;/code>&lt;/td>
&lt;td>&lt;code>Peer.share&lt;/code>&lt;/td>
&lt;td>Peer (media streams)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>Peer.unpublish&lt;/code>&lt;/td>
&lt;td>&lt;code>Peer.unshare&lt;/code>&lt;/td>
&lt;td>Peer (media streams)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2>Performance and Technical Optimizations&lt;span class="hx:absolute hx:-mt-20" id="performance-and-technical-optimizations">&lt;/span>
&lt;a href="#performance-and-technical-optimizations" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h2>&lt;p>As a core maintainer, my focus is often on the &amp;ldquo;under-the-hood&amp;rdquo; efficiencies that keep your apps performant. This release includes several technical wins for reducing message overhead:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Protobuf-like Format&lt;/strong>: We have transitioned from a custom binary format to a Protobuf-like format for signaling messages. This transition allows us to use an industry-standard serialization method and reuse it across several internal components, reducing the need for custom parsing logic and minimizing message sizes.&lt;/li>
&lt;li>&lt;strong>ICE Candidate Debouncing&lt;/strong>: To minimize signaling chatter, we&amp;rsquo;ve introduced the &lt;code>iceCandidateDebounce&lt;/code> option in the Peer class. It defaults to &lt;code>50ms&lt;/code>, batching candidates to reduce the frequency of messages sent during the negotiation phase.&lt;/li>
&lt;li>&lt;strong>SocketIoDriver Enhancements&lt;/strong>: We have added an &lt;code>ackTimeout&lt;/code> option to the SocketIoDriver, giving you granular control over how long the client waits for server acknowledgments.&lt;/li>
&lt;li>&lt;strong>Simplified Event Naming&lt;/strong>: Most drivers now use peer or room identifiers directly as event names. By avoiding string concatenation, we&amp;rsquo;ve shortened name lengths to reduce signaling overhead.&lt;/li>
&lt;/ul>
&lt;h2>Upgrade and Migration Notes&lt;span class="hx:absolute hx:-mt-20" id="upgrade-and-migration-notes">&lt;/span>
&lt;a href="#upgrade-and-migration-notes" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h2>&lt;p>If you are moving from &lt;code>v0.3&lt;/code> to &lt;code>v0.4&lt;/code>, please review these breaking changes:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Method Renaming&lt;/strong>: All application-level calls to &lt;code>publish&lt;/code> and &lt;code>unpublish&lt;/code> must be updated to &lt;code>share&lt;/code> and &lt;code>unshare&lt;/code>. This change applies to both the &lt;code>Peer&lt;/code> and &lt;code>RemotePeer&lt;/code> classes.&lt;/li>
&lt;li>&lt;strong>Driver Active State&lt;/strong>: The Driver class now initializes with the &lt;code>active&lt;/code> accessor set to &lt;code>false&lt;/code> by default. Ensure your logic accounts for the driver state during startup.&lt;/li>
&lt;li>&lt;strong>Prefix Handling &amp;amp; Separators&lt;/strong>: Some drivers now use empty prefixes by default and no longer automatically include separators. If you require a separator, it must be part of your prefix string.
&lt;ul>
&lt;li>&lt;em>Example&lt;/em>: If your prefix is &lt;code>peerix&lt;/code> and the event is &lt;code>event&lt;/code>, it will result in &lt;code>peerixevent&lt;/code>. To get &lt;code>peerix:event&lt;/code>, you must define the prefix as &lt;code>peerix:&lt;/code>.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Namespace Hashing Compatibility&lt;/strong>: When namespaceHashing is disabled, the room property is now automatically escaped. This prevents character compatibility issues with certain signaling backends that do not support specific symbols or spaces.&lt;/li>
&lt;/ul>
&lt;h2>Conclusion&lt;span class="hx:absolute hx:-mt-20" id="conclusion">&lt;/span>
&lt;a href="#conclusion" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h2>&lt;p>This release brings us closer to a truly plug-and-play WebRTC experience that scales. For full details on these changes, please visit our updated resources:&lt;/p>
&lt;ul>
&lt;li>📚 &lt;a href="https://peerix.dev/docs/"target="_blank" rel="noopener">Documentation&lt;svg class="hx:inline hx:rtl:rotate-270 hx:align-baseline" height="1em" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
&lt;path d="m9.1716 7.7574h7.0711m0 0v7.0711m0-7.0711-8.4853 8.4853" stroke-linecap="round" stroke-linejoin="round"/>
&lt;/svg>&lt;/a>&lt;/li>
&lt;li>📑 &lt;a href="https://api.peerix.dev/"target="_blank" rel="noopener">API Reference&lt;svg class="hx:inline hx:rtl:rotate-270 hx:align-baseline" height="1em" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
&lt;path d="m9.1716 7.7574h7.0711m0 0v7.0711m0-7.0711-8.4853 8.4853" stroke-linecap="round" stroke-linejoin="round"/>
&lt;/svg>&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Peerix v0.3 Released</title><link>https://peerix.dev/blog/release-v0-3/</link><pubDate>Sat, 23 May 2026 00:00:00 +0000</pubDate><guid>https://peerix.dev/blog/release-v0-3/</guid><description>
&lt;p>Peerix &lt;code>v0.3&lt;/code> is now available. This release introduces a new SSE signaling driver, strengthens secure defaults, improves signaling efficiency, and updates compatibility and licensing for wider adoption.&lt;/p>
&lt;h2>Highlights&lt;span class="hx:absolute hx:-mt-20" id="highlights">&lt;/span>
&lt;a href="#highlights" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h2>&lt;h3>New &lt;code>SseDriver&lt;/code> for signaling&lt;span class="hx:absolute hx:-mt-20" id="new-ssedriver-for-signaling">&lt;/span>
&lt;a href="#new-ssedriver-for-signaling" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h3>&lt;p>Peerix now includes &lt;a href="https://peerix.dev/docs/drivers/sse/">&lt;code>SseDriver&lt;/code>&lt;/a> for signaling over &lt;a href="https://developer.mozilla.org/docs/Web/API/Server-sent_events"target="_blank" rel="noopener">Server-Sent Events (SSE)&lt;svg class="hx:inline hx:rtl:rotate-270 hx:align-baseline" height="1em" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
&lt;path d="m9.1716 7.7574h7.0711m0 0v7.0711m0-7.0711-8.4853 8.4853" stroke-linecap="round" stroke-linejoin="round"/>
&lt;/svg>&lt;/a>.&lt;/p>
&lt;p>The SSE driver is a browser-friendly option for production signaling: incoming updates are delivered through a single SSE stream, while outbound signaling messages are sent via standard HTTP &lt;code>POST&lt;/code> requests.&lt;/p>
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">
&lt;div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">Peer&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">SseDriver&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="s2">&amp;#34;peerix&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">driver&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">SseDriver&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">url&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;http://localhost:8080/api/sse&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">peer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Peer&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">driver&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">await&lt;/span> &lt;span class="nx">peer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">join&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">room&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;my-room&amp;#34;&lt;/span> &lt;span class="p">});&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/div>&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copy code"
aria-label="Copy code"
data-copied-label="Copied!"
>
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;/button>
&lt;/div>
&lt;/div>
&lt;p>See the &lt;a href="https://peerix.dev/docs/drivers/sse/">SSE Driver docs&lt;/a> for full usage and a server example.&lt;/p>
&lt;h3>Lower signaling overhead with ICE candidate debouncing&lt;span class="hx:absolute hx:-mt-20" id="lower-signaling-overhead-with-ice-candidate-debouncing">&lt;/span>
&lt;a href="#lower-signaling-overhead-with-ice-candidate-debouncing" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h3>&lt;p>Peerix now debounces ICE candidate signaling. This reduces message bursts during negotiation and lowers transport load, especially in rooms where multiple peers join around the same time.&lt;/p>
&lt;h3>More secure defaults&lt;span class="hx:absolute hx:-mt-20" id="more-secure-defaults">&lt;/span>
&lt;a href="#more-secure-defaults" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h3>&lt;p>&lt;code>v0.3&lt;/code> improves signaling security and identity defaults:&lt;/p>
&lt;ul>
&lt;li>Signaling encryption now uses derived keys instead of manually supplied secret keys.&lt;/li>
&lt;li>Signaling encryption is enabled by default.&lt;/li>
&lt;li>Peer IDs now use compressed public keys.&lt;/li>
&lt;li>&lt;code>signalingHashing&lt;/code> has been renamed to &lt;code>namespaceHashing&lt;/code> for clarity and consistency.&lt;/li>
&lt;/ul>
&lt;p>These updates make secure setups easier out of the box while preserving flexibility for advanced deployments.&lt;/p>
&lt;h3>Modern runtime baseline&lt;span class="hx:absolute hx:-mt-20" id="modern-runtime-baseline">&lt;/span>
&lt;a href="#modern-runtime-baseline" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h3>&lt;p>This release raises the minimum build target to &lt;code>ES2020&lt;/code> to support modern JavaScript features such as &lt;code>BigInt&lt;/code>.&lt;/p>
&lt;h3>Updated licensing&lt;span class="hx:absolute hx:-mt-20" id="updated-licensing">&lt;/span>
&lt;a href="#updated-licensing" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h3>&lt;p>Peerix is now licensed under &lt;code>Apache-2.0&lt;/code>, replacing &lt;code>GPL-3.0&lt;/code>. This makes adoption easier for both open-source and commercial projects.&lt;/p>
&lt;h2>Upgrade notes&lt;span class="hx:absolute hx:-mt-20" id="upgrade-notes">&lt;/span>
&lt;a href="#upgrade-notes" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h2>&lt;p>When upgrading to &lt;code>v0.3&lt;/code>, review the following:&lt;/p>
&lt;ul>
&lt;li>If you used manually managed signaling secret keys, migrate to the new derived-key flow.&lt;/li>
&lt;li>If you configured &lt;code>signalingHashing&lt;/code>, rename it to &lt;code>namespaceHashing&lt;/code>.&lt;/li>
&lt;li>If your environment depends on older JavaScript targets, ensure your toolchain supports &lt;code>ES2020&lt;/code>.&lt;/li>
&lt;li>If you need cross-browser/server signaling without WebSockets, consider moving to &lt;a href="https://peerix.dev/docs/drivers/sse/">&lt;code>SseDriver&lt;/code>&lt;/a>.&lt;/li>
&lt;/ul>
&lt;h2>Thank you&lt;span class="hx:absolute hx:-mt-20" id="thank-you">&lt;/span>
&lt;a href="#thank-you" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h2>&lt;p>Thanks to everyone testing Peerix and sharing feedback. Your input continues to improve reliability, performance, and developer experience.&lt;/p>
&lt;p>More improvements are coming soon across core functionality, drivers, examples, and documentation.&lt;/p></description></item><item><title>Peerix v0.2 Released</title><link>https://peerix.dev/blog/release-v0-2/</link><pubDate>Wed, 13 May 2026 00:00:00 +0000</pubDate><guid>https://peerix.dev/blog/release-v0-2/</guid><description>
&lt;p>Peerix &lt;code>v0.2&lt;/code> is now available. This release expands signaling options, simplifies interoperability, and improves default privacy behavior in production environments.&lt;/p>
&lt;h2>Highlights&lt;span class="hx:absolute hx:-mt-20" id="highlights">&lt;/span>
&lt;a href="#highlights" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h2>&lt;h3>New signaling drivers&lt;span class="hx:absolute hx:-mt-20" id="new-signaling-drivers">&lt;/span>
&lt;a href="#new-signaling-drivers" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h3>&lt;p>Peerix now includes two new built-in signaling drivers:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://peerix.dev/docs/drivers/socket-io/">&lt;code>SocketIoDriver&lt;/code>&lt;/a> for applications using Socket.IO infrastructure.&lt;/li>
&lt;li>&lt;a href="https://peerix.dev/docs/drivers/supabase/">&lt;code>SupabaseDriver&lt;/code>&lt;/a> for projects built on Supabase Realtime.&lt;/li>
&lt;/ul>
&lt;p>These drivers make it easier to integrate Peerix into existing stacks without adding custom signaling glue code.&lt;/p>
&lt;p>In &lt;code>v0.2&lt;/code>, both drivers follow the same setup pattern used by other Peerix signaling drivers: create your transport client, pass it to the driver, then initialize &lt;code>Peer&lt;/code> with that driver. This makes it simple to switch transports without changing application-level peer logic.&lt;/p>
&lt;h4>Socket.IO driver&lt;span class="hx:absolute hx:-mt-20" id="socketio-driver">&lt;/span>
&lt;a href="#socketio-driver" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h4>&lt;p>&lt;a href="https://socket.io/"target="_blank" rel="noopener">Socket.IO&lt;svg class="hx:inline hx:rtl:rotate-270 hx:align-baseline" height="1em" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
&lt;path d="m9.1716 7.7574h7.0711m0 0v7.0711m0-7.0711-8.4853 8.4853" stroke-linecap="round" stroke-linejoin="round"/>
&lt;/svg>&lt;/a> is a popular real-time communication library that provides a WebSocket-like API with fallbacks for older browsers. The &lt;code>SocketIoDriver&lt;/code> allows you to use Socket.IO as the signaling transport for Peerix, enabling seamless integration with existing Socket.IO servers.&lt;/p>
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">
&lt;div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">Peer&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">SocketIoDriver&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="s2">&amp;#34;peerix&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">io&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="s2">&amp;#34;socket.io-client&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">socket&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">io&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;https://your-socket-server&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">driver&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">SocketIoDriver&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">socket&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">peer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Peer&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">driver&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">await&lt;/span> &lt;span class="nx">peer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">join&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">room&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;my-room&amp;#34;&lt;/span> &lt;span class="p">});&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/div>&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copy code"
aria-label="Copy code"
data-copied-label="Copied!"
>
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;/button>
&lt;/div>
&lt;/div>
&lt;p>Note that you will need a compatible server-side Socket.IO implementation to use this driver. See the &lt;a href="https://peerix.dev/docs/drivers/socket-io/#server-example">Socket.IO Driver docs&lt;/a> for a code example.&lt;/p>
&lt;h4>Supabase driver&lt;span class="hx:absolute hx:-mt-20" id="supabase-driver">&lt;/span>
&lt;a href="#supabase-driver" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h4>&lt;p>&lt;a href="https://supabase.com/docs/guides/realtime"target="_blank" rel="noopener">Supabase Realtime&lt;svg class="hx:inline hx:rtl:rotate-270 hx:align-baseline" height="1em" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
&lt;path d="m9.1716 7.7574h7.0711m0 0v7.0711m0-7.0711-8.4853 8.4853" stroke-linecap="round" stroke-linejoin="round"/>
&lt;/svg>&lt;/a> is a serverless real-time database and messaging service built on PostgreSQL. The &lt;code>SupabaseDriver&lt;/code> allows you to use Supabase Realtime as the signaling transport for Peerix, making it easy to integrate with applications already using Supabase.&lt;/p>
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">
&lt;div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">Peer&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">SupabaseDriver&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="s2">&amp;#34;peerix&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">createClient&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="s2">&amp;#34;@supabase/supabase-js&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">supabase&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">createClient&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;https://your-project.supabase.co&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;public-anon-key&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">driver&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">SupabaseDriver&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">supabase&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">peer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Peer&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">driver&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">await&lt;/span> &lt;span class="nx">peer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">join&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">room&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;my-room&amp;#34;&lt;/span> &lt;span class="p">});&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/div>&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copy code"
aria-label="Copy code"
data-copied-label="Copied!"
>
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;/button>
&lt;/div>
&lt;/div>
&lt;p>Note that you will need to set up a Supabase Realtime subscription on the server side to use this driver.&lt;/p>
&lt;h3>NATS driver update&lt;span class="hx:absolute hx:-mt-20" id="nats-driver-update">&lt;/span>
&lt;a href="#nats-driver-update" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h3>&lt;p>The &lt;code>NatsDriver&lt;/code> now uses &lt;code>nats-core&lt;/code> instead of &lt;code>nats.ws&lt;/code> because of deprecation. This change improves consistency and aligns Peerix with the current direction of the NATS JavaScript ecosystem.&lt;/p>
&lt;h4>NATS (&lt;code>nats-core&lt;/code>) example&lt;span class="hx:absolute hx:-mt-20" id="nats-nats-core-example">&lt;/span>
&lt;a href="#nats-nats-core-example" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h4>&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">
&lt;div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">Peer&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">NatsDriver&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="s2">&amp;#34;peerix&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">wsconnect&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="s2">&amp;#34;@nats-io/nats-core&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">nc&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kr">await&lt;/span> &lt;span class="nx">wsconnect&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">servers&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;wss://demo.nats.io:8443&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">driver&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">NatsDriver&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">nc&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">peer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Peer&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">driver&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">await&lt;/span> &lt;span class="nx">peer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">join&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">room&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;my-room&amp;#34;&lt;/span> &lt;span class="p">});&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/div>&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copy code"
aria-label="Copy code"
data-copied-label="Copied!"
>
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;/button>
&lt;/div>
&lt;/div>
&lt;p>See &lt;a href="https://peerix.dev/docs/drivers/nats/">NATS Driver docs&lt;/a> for full details and migration guidance.&lt;/p>
&lt;h4>Driver cleanup with &lt;code>destroy&lt;/code>&lt;span class="hx:absolute hx:-mt-20" id="driver-cleanup-with-destroy">&lt;/span>
&lt;a href="#driver-cleanup-with-destroy" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h4>&lt;p>When your app unmounts a page, leaves a workspace, or otherwise tears down realtime state, explicitly destroy the driver to avoid dangling subscriptions or listeners:&lt;/p>
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">
&lt;div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">await&lt;/span> &lt;span class="nx">peer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">leave&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">driver&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">destroy&lt;/span>&lt;span class="p">();&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/div>&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copy code"
aria-label="Copy code"
data-copied-label="Copied!"
>
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;/button>
&lt;/div>
&lt;/div>
&lt;h3>Serialization-friendly driver interface&lt;span class="hx:absolute hx:-mt-20" id="serialization-friendly-driver-interface">&lt;/span>
&lt;a href="#serialization-friendly-driver-interface" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h3>&lt;p>The driver interface now favors plain arrays over typed arrays. This improves serialization across environments and transports, especially when messages cross boundaries where typed arrays can be awkward or lossy.&lt;/p>
&lt;h3>Namespace hashing enabled by default&lt;span class="hx:absolute hx:-mt-20" id="namespace-hashing-enabled-by-default">&lt;/span>
&lt;a href="#namespace-hashing-enabled-by-default" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h3>&lt;p>Namespace hashing is now enabled by default. This delivers two practical benefits:&lt;/p>
&lt;ul>
&lt;li>Better privacy by avoiding direct exposure of raw namespace values.&lt;/li>
&lt;li>Better compatibility by preventing issues with unsupported namespace characters.&lt;/li>
&lt;/ul>
&lt;h2>Upgrade notes&lt;span class="hx:absolute hx:-mt-20" id="upgrade-notes">&lt;/span>
&lt;a href="#upgrade-notes" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h2>&lt;p>When moving to &lt;code>v0.2&lt;/code>, review the following:&lt;/p>
&lt;ul>
&lt;li>If you rely on readable namespace values in infrastructure logs or tooling, account for hashing now being enabled by default.&lt;/li>
&lt;li>If your custom drivers depend on typed array assumptions, update them to work with plain arrays.&lt;/li>
&lt;li>If you use NATS signaling, verify your setup against the &lt;code>nats-core&lt;/code> dependency.&lt;/li>
&lt;/ul>
&lt;h2>Thank you&lt;span class="hx:absolute hx:-mt-20" id="thank-you">&lt;/span>
&lt;a href="#thank-you" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h2>&lt;p>Thanks to everyone trying Peerix early and sharing feedback. Your input helps shape the roadmap and improve real-world reliability.&lt;/p>
&lt;p>More updates are coming soon, including continued work on drivers and developer experience.&lt;/p></description></item><item><title>Welcome to Peerix</title><link>https://peerix.dev/blog/welcome-to-peerix/</link><pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate><guid>https://peerix.dev/blog/welcome-to-peerix/</guid><description>
&lt;p>My name is Anton Skshidlevsky (aka meefik), and I&amp;rsquo;m a software engineer with over twenty years of experience in software development and over ten years of experience in building WebRTC web applications. Today, I&amp;rsquo;m excited to announce the first release of Peerix, a JavaScript/TypeScript open-source library designed to simplify building WebRTC peer-to-peer applications. After months of development and testing, Peerix is now available for developers to use and contribute to.&lt;/p>
&lt;h2>Background&lt;span class="hx:absolute hx:-mt-20" id="background">&lt;/span>
&lt;a href="#background" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h2>&lt;p>Over ten years ago, I started building peer-to-peer web applications using WebRTC. It was an exciting but often frustrating journey, as I had to repeatedly solve the same problems around signaling, connection management, and media/data exchange across browsers and devices. However, from time to time, things went wrong. I wanted a simpler way to build these apps without reinventing the wheel every time, but existing libraries didn&amp;rsquo;t quite fit the bill.&lt;/p>
&lt;p>That&amp;rsquo;s exactly why I created Peerix, which brings all of this expertise together. It abstracts away the complexities of WebRTC and provides a clean, minimal API for building peer-to-peer apps quickly and reliably. Whether you&amp;rsquo;re building a simple chat app, a video conferencing service, a collaborative tool, or a more sophisticated multi-stream, multi-channel experience, Peerix aims to make it easier to get up and running without getting bogged down in the details of signaling and connection management. I&amp;rsquo;m excited to share it with the community and see what you build with it!&lt;/p>
&lt;p>Currently, Peerix implements core functionality around WebRTC and peer-to-peer connections. Even now, it supports a wide range of use cases, from simple data sharing to multi-stream media applications. The core API is designed to be minimal and intuitive, with sensible defaults for signaling and connection management. Just a few lines of code are needed to get started. In the future, we plan to add more features and add-ons, as well as additional signaling drivers to further enhance the capabilities of Peerix.&lt;/p>
&lt;h2>Why peer-to-peer&lt;span class="hx:absolute hx:-mt-20" id="why-peer-to-peer">&lt;/span>
&lt;a href="#why-peer-to-peer" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h2>&lt;p>Peer-to-peer communication has many advantages, including lower latency, better privacy, and reduced server costs. By connecting peers directly, you can build applications that are more responsive and scalable, without relying on centralized servers for media processing or routing. Peerix is designed to make it easy to build these kinds of applications, allowing you to focus on building your application&amp;rsquo;s features and user experience, rather than dealing with the complexities of WebRTC and signaling.&lt;/p>
&lt;p>I admit that sometimes a server-side component is necessary, especially for media processing or routing in large-scale applications. However, for many use cases, a pure peer-to-peer architecture can provide significant benefits. With the correct configuration, each peer can connect to dozens of other peers without hitting performance issues, and the server can be reserved for signaling and coordination rather than media processing. You can have thousands of rooms with dozens of peers each, all without needing a single SFU or MCU. You can even do some media processing directly in the browser with add-ons, such as recording or mixing, without needing to route media through a server. So peer-to-peer is powerful and can be the right choice for many applications, and Peerix is here to make it easier to build those applications.&lt;/p>
&lt;h2>Why Peerix&lt;span class="hx:absolute hx:-mt-20" id="why-peerix">&lt;/span>
&lt;a href="#why-peerix" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h2>&lt;p>WebRTC is powerful but complex. Most apps re-implement the same boilerplate: signaling channels, ICE configuration, connection lifecycle, and multi-track negotiation. That complexity slows prototyping, increases bugs, and raises the barrier for developers who just want to share media or data peer-to-peer.&lt;/p>
&lt;p>Interestingly, there are not many libraries that offer a clean, well-documented abstraction over WebRTC, including modern concepts like multi-track, data channels, and pluggable signaling. Many libraries focus on specific use cases (e.g., video calls) or require server-side components (e.g., SFUs). Peerix fills the gap for a flexible, transport-agnostic client-side library that abstracts away the common complexities of WebRTC while still allowing you to build a wide range of peer-to-peer applications.&lt;/p>
&lt;h2>The problem it solves&lt;span class="hx:absolute hx:-mt-20" id="the-problem-it-solves">&lt;/span>
&lt;a href="#the-problem-it-solves" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h2>&lt;p>Typical WebRTC apps face several challenges:&lt;/p>
&lt;ul>
&lt;li>Signaling vendor lock-in: many libraries tie you to a specific signaling method (e.g., WebSockets), making it hard to switch or support multiple transports.&lt;/li>
&lt;li>Hard-to-manage negotiations of multiple media streams and data channels across multiple peers in mesh and room-based topologies.&lt;/li>
&lt;li>STUN/TURN and reconnection complexities that leak into app code.&lt;/li>
&lt;li>Extra boilerplate for additional functionality like E2E encryption, recording, storage, state synchronization, etc.&lt;/li>
&lt;/ul>
&lt;p>Peerix centralizes these concerns in a single &lt;code>Peer&lt;/code> class and a pluggable driver model for signaling, so the same API works locally (MemoryDriver), between tabs (BroadcastChannelDriver), or across the internet (NATS driver or custom drivers). It can be extended with add-ons without modifying your core app logic.&lt;/p>
&lt;h2>Core idea&lt;span class="hx:absolute hx:-mt-20" id="core-idea">&lt;/span>
&lt;a href="#core-idea" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h2>&lt;p>Peerix provides a simple yet powerful abstraction of WebRTC with transport-agnostic architecture and extends its capabilities with add-ons, all while keeping the API minimal.&lt;/p>
&lt;p>At the heart of Peerix is a simple principle: a room-based topology involves a single connection per peer and multiplexes media tracks and data channels over that connection using transport-agnostic drivers. This reduces signaling chatter and avoids creating multiple redundant peer connections for each stream or channel. The pluggable driver model abstracts away the signaling transport, allowing you to choose or implement the one that best fits your use case without changing your app code. The library exposes clear lifecycle events so that apps can react declaratively.&lt;/p>
&lt;h2>Advantages&lt;span class="hx:absolute hx:-mt-20" id="advantages">&lt;/span>
&lt;a href="#advantages" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h2>&lt;p>Let&amp;rsquo;s summarize the key advantages of Peerix:&lt;/p>
&lt;ul>
&lt;li>Minimal API surface: quick to learn and integrate.&lt;/li>
&lt;li>Pluggable signaling drivers: swap BroadcastChannel, NATS, or your own driver over WebSockets.&lt;/li>
&lt;li>Single-connection multiplexing: efficient resource use and simpler negotiation.&lt;/li>
&lt;li>Flexible: share multiple streams, open multiple data channels, and attach add-ons to extend functionality.&lt;/li>
&lt;/ul>
&lt;h2>Who is it for&lt;span class="hx:absolute hx:-mt-20" id="who-is-it-for">&lt;/span>
&lt;a href="#who-is-it-for" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h2>&lt;p>Peerix is for developers building real-time apps such as chat, conferencing, file sharing, collaborative tools, and games who want to avoid reimplementing signaling and connection plumbing, and who need a flexible, extensible foundation for peer-to-peer communication in the browser. If you want to build peer-to-peer web apps without reinventing the wheel, Peerix is for you.&lt;/p>
&lt;h2>When not to use it&lt;span class="hx:absolute hx:-mt-20" id="when-not-to-use-it">&lt;/span>
&lt;a href="#when-not-to-use-it" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h2>&lt;p>If you need server-side media processing or routing, Peerix is not an SFU (Selective Forwarding Unit) or MCU (Multipoint Control Unit).&lt;/p>
&lt;h2>License&lt;span class="hx:absolute hx:-mt-20" id="license">&lt;/span>
&lt;a href="#license" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h2>&lt;p>Peerix is licensed under the Apache License, Version 2.0. This means you can use, modify, and distribute Peerix in your open-source or commercial projects without any restrictions. See the &lt;a href="http://www.apache.org/licenses/LICENSE-2.0"target="_blank" rel="noopener">LICENSE&lt;svg class="hx:inline hx:rtl:rotate-270 hx:align-baseline" height="1em" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
&lt;path d="m9.1716 7.7574h7.0711m0 0v7.0711m0-7.0711-8.4853 8.4853" stroke-linecap="round" stroke-linejoin="round"/>
&lt;/svg>&lt;/a> file for details.&lt;/p>
&lt;h2>Future plans&lt;span class="hx:absolute hx:-mt-20" id="future-plans">&lt;/span>
&lt;a href="#future-plans" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h2>&lt;p>In the future, we plan to expand Peerix with additional features and signaling drivers. Some of the upcoming features include:&lt;/p>
&lt;ul>
&lt;li>Additional signaling drivers: SSE driver, Socket.IO driver, Supabase driver, and more.&lt;/li>
&lt;li>Recording add-on: easily record media streams or data channels.&lt;/li>
&lt;li>State synchronization add-on: keep application state in sync across peers.&lt;/li>
&lt;li>Video/audio mixing add-on: mix multiple media streams together for composite outputs.&lt;/li>
&lt;/ul>
&lt;h2>Get started&lt;span class="hx:absolute hx:-mt-20" id="get-started">&lt;/span>
&lt;a href="#get-started" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h2>&lt;p>A code snippet to illustrate how simple it is to get started with Peerix:&lt;/p>
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">
&lt;div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-js" data-lang="js">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">Peer&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">BroadcastChannelDriver&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="s2">&amp;#34;peerix&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// create a signaling driver
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">driver&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">BroadcastChannelDriver&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// create a Peer instance
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kr">const&lt;/span> &lt;span class="nx">peer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Peer&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">driver&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// listen for events
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">peer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">on&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;channel:open&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">({&lt;/span> &lt;span class="nx">remote&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">label&lt;/span> &lt;span class="p">})&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">remote&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">send&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;hello&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">label&lt;/span> &lt;span class="p">}));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">peer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">on&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;channel:message&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="p">({&lt;/span> &lt;span class="nx">data&lt;/span> &lt;span class="p">})&lt;/span> &lt;span class="p">=&amp;gt;&lt;/span> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;msg&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kr">await&lt;/span> &lt;span class="nx">data&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// open a data channel
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">peer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">open&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">label&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;default&amp;#34;&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// join a room
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">peer&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">join&lt;/span>&lt;span class="p">({&lt;/span> &lt;span class="nx">room&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;room-id&amp;#34;&lt;/span> &lt;span class="p">});&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/div>&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copy code"
aria-label="Copy code"
data-copied-label="Copied!"
>
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4">&lt;/div>
&lt;/button>
&lt;/div>
&lt;/div>
&lt;p>See the Getting Started guide to install Peerix and try it in your browser. Contributions, issues, and driver add-ons are welcome — Peerix aims to be a practical, community-driven toolkit for peer-to-peer web apps.&lt;/p>
&lt;p>Welcome aboard — now let&amp;rsquo;s make peer-to-peer apps easier.&lt;/p>
&lt;div class="hextra-cards hx:mt-4 hx:gap-4 hx:grid not-prose" style="--hextra-cards-grid-cols: 3;">
&lt;a
class="hextra-card hx:group hx:flex hx:flex-col hx:justify-start hx:overflow-hidden hx:rounded-lg hx:border hx:border-gray-200 hx:text-current hx:no-underline hx:dark:shadow-none hx:hover:shadow-gray-100 hx:dark:hover:shadow-none hx:shadow-gray-100 hx:active:shadow-sm hx:active:shadow-gray-200 hx:transition-all hx:duration-200 hx:hover:border-gray-300 hx:bg-transparent hx:shadow-xs hx:dark:border-neutral-800 hx:hover:bg-slate-50 hx:hover:shadow-md hx:dark:hover:border-neutral-700 hx:dark:hover:bg-neutral-900"href="https://peerix.dev/docs/"
>&lt;div class="hx:mt-auto">
&lt;span class="hextra-card-icon hx:flex hx:font-semibold hx:items-start hx:gap-2 hx:pt-4 hx:px-4 hx:text-gray-700 hx:hover:text-gray-900 hx:dark:text-neutral-200 hx:dark:hover:text-neutral-50">&lt;svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true">&lt;path stroke-linecap="round" stroke-linejoin="round" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"/>&lt;/svg>Get Started&lt;/span>&lt;div class="hextra-card-subtitle hx:line-clamp-3 hx:text-sm hx:font-normal hx:text-gray-500 hx:dark:text-gray-400 hx:px-4 hx:mb-4 hx:mt-2">Read the documentation&lt;/div>&lt;/div>&lt;/a>
&lt;a
class="hextra-card hx:group hx:flex hx:flex-col hx:justify-start hx:overflow-hidden hx:rounded-lg hx:border hx:border-gray-200 hx:text-current hx:no-underline hx:dark:shadow-none hx:hover:shadow-gray-100 hx:dark:hover:shadow-none hx:shadow-gray-100 hx:active:shadow-sm hx:active:shadow-gray-200 hx:transition-all hx:duration-200 hx:hover:border-gray-300 hx:bg-transparent hx:shadow-xs hx:dark:border-neutral-800 hx:hover:bg-slate-50 hx:hover:shadow-md hx:dark:hover:border-neutral-700 hx:dark:hover:bg-neutral-900"href="https://api.peerix.dev"
target="_blank" rel="noreferrer">&lt;div class="hx:mt-auto">
&lt;span class="hextra-card-icon hx:flex hx:font-semibold hx:items-start hx:gap-2 hx:pt-4 hx:px-4 hx:text-gray-700 hx:hover:text-gray-900 hx:dark:text-neutral-200 hx:dark:hover:text-neutral-50">&lt;svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true">&lt;path stroke-linecap="round" stroke-linejoin="round" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"/>&lt;/svg>API Reference&lt;/span>&lt;div class="hextra-card-subtitle hx:line-clamp-3 hx:text-sm hx:font-normal hx:text-gray-500 hx:dark:text-gray-400 hx:px-4 hx:mb-4 hx:mt-2">Explore the API reference&lt;/div>&lt;/div>&lt;/a>
&lt;/div>
&lt;h2>Conclusion&lt;span class="hx:absolute hx:-mt-20" id="conclusion">&lt;/span>
&lt;a href="#conclusion" class="subheading-anchor" aria-label="Permalink for this section">&lt;/a>&lt;/h2>&lt;p>Eventually, we hope Peerix will become a go-to library for developers building peer-to-peer web applications, providing a solid foundation that abstracts away the complexities of WebRTC while still allowing for flexibility and extensibility. We look forward to seeing what the community builds with Peerix and welcome contributions to help make it even better.&lt;/p></description></item></channel></rss>