<?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>Forem: Ahmet YİLDİRİM</title>
    <description>The latest articles on Forem by Ahmet YİLDİRİM (@ahmet_yildirim_fbb7d3cf2d).</description>
    <link>https://forem.com/ahmet_yildirim_fbb7d3cf2d</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%2F3628061%2F8ea8f437-051e-4046-9586-86cf1d858b3e.png</url>
      <title>Forem: Ahmet YİLDİRİM</title>
      <link>https://forem.com/ahmet_yildirim_fbb7d3cf2d</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ahmet_yildirim_fbb7d3cf2d"/>
    <language>en</language>
    <item>
      <title>WebRTC + Signaling Server</title>
      <dc:creator>Ahmet YİLDİRİM</dc:creator>
      <pubDate>Mon, 24 Nov 2025 23:22:34 +0000</pubDate>
      <link>https://forem.com/ahmet_yildirim_fbb7d3cf2d/webrtc-signaling-server-1mi6</link>
      <guid>https://forem.com/ahmet_yildirim_fbb7d3cf2d/webrtc-signaling-server-1mi6</guid>
      <description>&lt;h1&gt;
  
  
  Building a Zero-Storage Peer-to-Peer File Transfer Service: How We Built KA-UPLOAD
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;: We built a peer-to-peer file transfer service using WebRTC that requires zero server storage, supports unlimited file sizes, and works without signup. Files transfer directly from browser to browser. Here's how we did it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Problem with Traditional File Sharing
&lt;/h2&gt;

&lt;p&gt;When you need to send a large file, you're usually stuck with one of these options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Email&lt;/strong&gt;: Limited to 25MB, files get stuck in spam filters&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud Storage&lt;/strong&gt;: Requires upload to a server, privacy concerns, storage costs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Traditional Services&lt;/strong&gt;: WeTransfer, Dropbox Transfer - all require server uploads, file size limits, and signup requirements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What if you could send files directly from your browser to someone else's browser, with &lt;strong&gt;zero server storage&lt;/strong&gt;, &lt;strong&gt;unlimited file sizes&lt;/strong&gt;, and &lt;strong&gt;no signup required&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;That's exactly what we built with &lt;strong&gt;KA-UPLOAD&lt;/strong&gt; - a peer-to-peer file transfer service that uses WebRTC to enable direct browser-to-browser file transfers.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture: WebRTC + Signaling Server
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Core Technology Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend&lt;/strong&gt;: Vanilla JavaScript, Tailwind CSS, WebRTC API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend&lt;/strong&gt;: PHP 7.4+ with MySQL for signaling and authentication&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WebRTC&lt;/strong&gt;: PeerJS library for simplified peer-to-peer connections&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Desktop App&lt;/strong&gt;: Electron for native desktop experience&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payment Processing&lt;/strong&gt;: Stripe for subscription management&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How It Works: The Technical Deep Dive
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Signaling Phase
&lt;/h4&gt;

&lt;p&gt;When a user wants to send a file, they create a transfer link. This link contains a unique code that acts as a "room identifier" for the WebRTC connection.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Sender creates a peer connection&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;peer&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;Peer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;linkCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;peerjs-server.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;443&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;secure&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The signaling server (PeerJS cloud service) helps establish the initial connection between peers, but once connected, all data flows directly between browsers.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Peer Discovery
&lt;/h4&gt;

&lt;p&gt;The receiver visits the share link (e.g., &lt;code&gt;ka-upload.com/d/abc123&lt;/code&gt;). The page loads a WebRTC client that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Connects to the same PeerJS server&lt;/li&gt;
&lt;li&gt;Uses the link code to find the sender's peer ID&lt;/li&gt;
&lt;li&gt;Establishes a direct peer-to-peer connection
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Receiver connects to sender&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt; &lt;span class="o"&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;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;senderPeerId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;connection&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;open&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Direct connection established!&lt;/span&gt;
    &lt;span class="c1"&gt;// No server involved in data transfer&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. File Streaming
&lt;/h4&gt;

&lt;p&gt;Files are streamed in &lt;strong&gt;8KB chunks&lt;/strong&gt; using WebRTC DataChannels:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Sender streams file in chunks&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunkSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 8KB chunks&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fileStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createReadStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="na"&gt;highWaterMark&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;chunkSize&lt;/span&gt; 
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;fileStream&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;chunk&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="c1"&gt;// Convert to base64 for JSON transmission&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;base64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;chunk&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Send via WebRTC DataChannel&lt;/span&gt;
    &lt;span class="nx"&gt;dataChannel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;file-chunk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;chunkIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;chunkIndex&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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;&lt;strong&gt;Why 8KB chunks?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;8KB binary → ~10.67KB base64 → ~12KB JSON&lt;/li&gt;
&lt;li&gt;Well under WebRTC's 64KB message limit&lt;/li&gt;
&lt;li&gt;Optimal balance between speed and reliability&lt;/li&gt;
&lt;li&gt;Reduces memory overhead for large files&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  4. Zero Server Storage
&lt;/h4&gt;

&lt;p&gt;The key innovation: &lt;strong&gt;files never touch our servers&lt;/strong&gt;. The PHP backend only handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Link creation and metadata storage&lt;/li&gt;
&lt;li&gt;User authentication&lt;/li&gt;
&lt;li&gt;Signaling coordination&lt;/li&gt;
&lt;li&gt;Payment processing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The actual file data flows directly from sender to receiver via WebRTC.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security Implementation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  End-to-End Encryption
&lt;/h3&gt;

&lt;p&gt;WebRTC provides built-in encryption (DTLS-SRTP) for all data channels. Files are encrypted in transit without any additional code needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional Security Layers
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Password Protection&lt;/strong&gt;: Optional password for transfer links&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Link Expiration&lt;/strong&gt;: Automatic expiration (24 hours free, up to 30 days for Pro)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate Limiting&lt;/strong&gt;: Server-side rate limiting to prevent abuse&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQL Injection Protection&lt;/strong&gt;: PDO prepared statements&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;XSS Prevention&lt;/strong&gt;: Input sanitization and output escaping&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CSRF Protection&lt;/strong&gt;: Token-based protection for forms
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Example: Secure link creation&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;createTransferLink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$linkCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;bin2hex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;random_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// 32-char secure code&lt;/span&gt;
    &lt;span class="nv"&gt;$expiresAt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Y-m-d H:i:s'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;86400&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 24 hours&lt;/span&gt;

    &lt;span class="c1"&gt;// Hash password if provided&lt;/span&gt;
    &lt;span class="nv"&gt;$passwordHash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nb"&gt;password_hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;PASSWORD_BCRYPT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Store only metadata, not file data&lt;/span&gt;
    &lt;span class="nv"&gt;$stmt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$pdo&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;prepare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"
        INSERT INTO transfer_links 
        (link_code, user_id, password_hash, expires_at) 
        VALUES (?, ?, ?, ?)
    "&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$stmt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nv"&gt;$linkCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$passwordHash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$expiresAt&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$linkCode&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;h2&gt;
  
  
  Performance Optimizations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Chunked Streaming
&lt;/h3&gt;

&lt;p&gt;Instead of loading entire files into memory, we stream in 8KB chunks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Efficient chunked reading&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fileStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createReadStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="na"&gt;highWaterMark&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt; 
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows transferring files of any size without memory issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Base64 Encoding
&lt;/h3&gt;

&lt;p&gt;We use base64 encoding for binary data transmission over WebRTC DataChannels, which only support strings. The overhead (~33%) is acceptable for the convenience of direct browser-to-browser transfer.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Progress Tracking
&lt;/h3&gt;

&lt;p&gt;Real-time progress updates without polling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;connection&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &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;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;file-chunk&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;receivedBytes&lt;/span&gt; &lt;span class="o"&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;size&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;progress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;receivedBytes&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;fileSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;updateProgressBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;);&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;h3&gt;
  
  
  4. Connection Resilience
&lt;/h3&gt;

&lt;p&gt;The system handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Network interruptions&lt;/li&gt;
&lt;li&gt;NAT traversal (via STUN/TURN servers)&lt;/li&gt;
&lt;li&gt;Browser compatibility (Chrome, Firefox, Safari, Edge)&lt;/li&gt;
&lt;li&gt;Automatic reconnection attempts&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Desktop App Integration
&lt;/h2&gt;

&lt;p&gt;We built an Electron-based desktop app that integrates seamlessly with the web service:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Always-on Background Mode&lt;/strong&gt;: Runs in system tray&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-start&lt;/strong&gt;: Launches on system boot&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Same WebRTC Technology&lt;/strong&gt;: Uses identical peer-to-peer connection&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Native File Selection&lt;/strong&gt;: Better UX than browser file picker&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The desktop app uses the same 8KB chunk streaming and connects to the same signaling infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  API for Developers
&lt;/h2&gt;

&lt;p&gt;We provide a RESTful API for developers to integrate file transfer into their applications:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Create a transfer link via API&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://ka-upload.com/api/links/create.php&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="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bearer YOUR_API_KEY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;expires_hours&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;optional-password&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rate Limits:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Free tier: 100 requests/hour&lt;/li&gt;
&lt;li&gt;Premium: 1,000 requests/hour&lt;/li&gt;
&lt;li&gt;Pro: 10,000 requests/hour&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Challenges and Solutions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Challenge 1: NAT Traversal
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Many users are behind NATs/firewalls that block direct peer connections.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use PeerJS cloud service (includes STUN/TURN servers)&lt;/li&gt;
&lt;li&gt;Automatic fallback to relay servers when direct connection fails&lt;/li&gt;
&lt;li&gt;Transparent to users - works in most network configurations&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Challenge 2: Large File Handling
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Browsers have memory limits, and loading entire files causes crashes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stream files in 8KB chunks&lt;/li&gt;
&lt;li&gt;Process chunks as they arrive&lt;/li&gt;
&lt;li&gt;Never load entire file into memory&lt;/li&gt;
&lt;li&gt;Works for files of any size&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Challenge 3: Simultaneous Connection Requirement
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Both sender and receiver must be online at the same time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clear messaging to users about this requirement&lt;/li&gt;
&lt;li&gt;Desktop app can stay online in background&lt;/li&gt;
&lt;li&gt;Future: Add queue system for offline receivers&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real-World Performance
&lt;/h2&gt;

&lt;p&gt;In testing, we've achieved:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Transfer speeds&lt;/strong&gt;: Limited only by users' internet connections&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File sizes&lt;/strong&gt;: Successfully tested with files up to 50GB+&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrent transfers&lt;/strong&gt;: Multiple simultaneous transfers supported&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Uptime&lt;/strong&gt;: 99.9%+ reliability with PeerJS cloud infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Future Enhancements
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Resume Support&lt;/strong&gt;: Resume interrupted transfers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiple Files&lt;/strong&gt;: Transfer multiple files in one session&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mobile Apps&lt;/strong&gt;: Native iOS/Android apps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TURN Server&lt;/strong&gt;: Self-hosted TURN server for better NAT traversal&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analytics Dashboard&lt;/strong&gt;: Transfer statistics and insights&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Webhook Support&lt;/strong&gt;: Notify external services on transfer completion&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;WebRTC is powerful but complex&lt;/strong&gt;: The signaling phase requires careful coordination&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chunk size matters&lt;/strong&gt;: 8KB provides optimal balance for most use cases&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User education is key&lt;/strong&gt;: Users need to understand the peer-to-peer model&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fallbacks are essential&lt;/strong&gt;: Always have relay servers for NAT traversal&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Privacy sells&lt;/strong&gt;: Users appreciate zero-storage architecture&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building a zero-storage file transfer service with WebRTC was challenging but rewarding. The result is a service that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Respects user privacy (no server storage)&lt;/li&gt;
&lt;li&gt;✅ Handles unlimited file sizes&lt;/li&gt;
&lt;li&gt;✅ Works without signup&lt;/li&gt;
&lt;li&gt;✅ Provides real-time transfer speeds&lt;/li&gt;
&lt;li&gt;✅ Offers enterprise-grade security&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The technology stack (WebRTC + PHP + Electron) proved to be the right choice for building a production-ready peer-to-peer file transfer service.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it out&lt;/strong&gt;: &lt;a href="https://ka-upload.com" rel="noopener noreferrer"&gt;ka-upload.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API Documentation&lt;/strong&gt;: &lt;a href="https://ka-upload.com/api" rel="noopener noreferrer"&gt;ka-upload.com/api&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;Have you built something with WebRTC? What challenges did you face? Share your experiences in the comments below!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Questions about the implementation?&lt;/strong&gt; Feel free to ask - I'm happy to dive deeper into any part of the architecture.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Tags&lt;/strong&gt;: &lt;code&gt;#webrtc&lt;/code&gt; &lt;code&gt;#javascript&lt;/code&gt; &lt;code&gt;#php&lt;/code&gt; &lt;code&gt;#p2p&lt;/code&gt; &lt;code&gt;#filetransfer&lt;/code&gt; &lt;code&gt;#webdev&lt;/code&gt; &lt;code&gt;#programming&lt;/code&gt; &lt;code&gt;#tutorial&lt;/code&gt; &lt;code&gt;#showdev&lt;/code&gt;&lt;/p&gt;

</description>
      <category>webrtc</category>
      <category>website</category>
      <category>database</category>
    </item>
  </channel>
</rss>
