Building a Zero-Storage Peer-to-Peer File Transfer Service: How We Built KA-UPLOAD
TL;DR: 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.
The Problem with Traditional File Sharing
When you need to send a large file, you're usually stuck with one of these options:
- Email: Limited to 25MB, files get stuck in spam filters
- Cloud Storage: Requires upload to a server, privacy concerns, storage costs
- Traditional Services: WeTransfer, Dropbox Transfer - all require server uploads, file size limits, and signup requirements
What if you could send files directly from your browser to someone else's browser, with zero server storage, unlimited file sizes, and no signup required?
That's exactly what we built with KA-UPLOAD - a peer-to-peer file transfer service that uses WebRTC to enable direct browser-to-browser file transfers.
The Architecture: WebRTC + Signaling Server
Core Technology Stack
- Frontend: Vanilla JavaScript, Tailwind CSS, WebRTC API
- Backend: PHP 7.4+ with MySQL for signaling and authentication
- WebRTC: PeerJS library for simplified peer-to-peer connections
- Desktop App: Electron for native desktop experience
- Payment Processing: Stripe for subscription management
How It Works: The Technical Deep Dive
1. Signaling Phase
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.
// Sender creates a peer connection
const peer = new Peer(linkCode, {
host: 'peerjs-server.com',
port: 443,
path: '/',
secure: true
});
The signaling server (PeerJS cloud service) helps establish the initial connection between peers, but once connected, all data flows directly between browsers.
2. Peer Discovery
The receiver visits the share link (e.g., ka-upload.com/d/abc123). The page loads a WebRTC client that:
- Connects to the same PeerJS server
- Uses the link code to find the sender's peer ID
- Establishes a direct peer-to-peer connection
// Receiver connects to sender
const connection = peer.connect(senderPeerId);
connection.on('open', () => {
// Direct connection established!
// No server involved in data transfer
});
3. File Streaming
Files are streamed in 8KB chunks using WebRTC DataChannels:
// Sender streams file in chunks
const chunkSize = 8 * 1024; // 8KB chunks
const fileStream = fs.createReadStream(filePath, {
highWaterMark: chunkSize
});
fileStream.on('data', (chunk) => {
// Convert to base64 for JSON transmission
const base64 = chunk.toString('base64');
// Send via WebRTC DataChannel
dataChannel.send(JSON.stringify({
type: 'file-chunk',
data: base64,
chunkIndex: chunkIndex++,
size: chunk.length
}));
});
Why 8KB chunks?
- 8KB binary → ~10.67KB base64 → ~12KB JSON
- Well under WebRTC's 64KB message limit
- Optimal balance between speed and reliability
- Reduces memory overhead for large files
4. Zero Server Storage
The key innovation: files never touch our servers. The PHP backend only handles:
- Link creation and metadata storage
- User authentication
- Signaling coordination
- Payment processing
The actual file data flows directly from sender to receiver via WebRTC.
Security Implementation
End-to-End Encryption
WebRTC provides built-in encryption (DTLS-SRTP) for all data channels. Files are encrypted in transit without any additional code needed.
Additional Security Layers
- Password Protection: Optional password for transfer links
- Link Expiration: Automatic expiration (24 hours free, up to 30 days for Pro)
- Rate Limiting: Server-side rate limiting to prevent abuse
- SQL Injection Protection: PDO prepared statements
- XSS Prevention: Input sanitization and output escaping
- CSRF Protection: Token-based protection for forms
// Example: Secure link creation
function createTransferLink($userId, $password = null) {
$linkCode = bin2hex(random_bytes(16)); // 32-char secure code
$expiresAt = date('Y-m-d H:i:s', time() + 86400); // 24 hours
// Hash password if provided
$passwordHash = $password ? password_hash($password, PASSWORD_BCRYPT) : null;
// Store only metadata, not file data
$stmt = $pdo->prepare("
INSERT INTO transfer_links
(link_code, user_id, password_hash, expires_at)
VALUES (?, ?, ?, ?)
");
$stmt->execute([$linkCode, $userId, $passwordHash, $expiresAt]);
return $linkCode;
}
Performance Optimizations
1. Chunked Streaming
Instead of loading entire files into memory, we stream in 8KB chunks:
// Efficient chunked reading
const fileStream = fs.createReadStream(filePath, {
highWaterMark: 8 * 1024
});
This allows transferring files of any size without memory issues.
2. Base64 Encoding
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.
3. Progress Tracking
Real-time progress updates without polling:
connection.on('data', (data) => {
if (data.type === 'file-chunk') {
receivedBytes += data.size;
const progress = (receivedBytes / fileSize) * 100;
updateProgressBar(progress);
}
});
4. Connection Resilience
The system handles:
- Network interruptions
- NAT traversal (via STUN/TURN servers)
- Browser compatibility (Chrome, Firefox, Safari, Edge)
- Automatic reconnection attempts
Desktop App Integration
We built an Electron-based desktop app that integrates seamlessly with the web service:
- Always-on Background Mode: Runs in system tray
- Auto-start: Launches on system boot
- Same WebRTC Technology: Uses identical peer-to-peer connection
- Native File Selection: Better UX than browser file picker
The desktop app uses the same 8KB chunk streaming and connects to the same signaling infrastructure.
API for Developers
We provide a RESTful API for developers to integrate file transfer into their applications:
// Create a transfer link via API
const response = await fetch('https://ka-upload.com/api/links/create.php', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
expires_hours: 24,
password: 'optional-password'
})
});
Rate Limits:
- Free tier: 100 requests/hour
- Premium: 1,000 requests/hour
- Pro: 10,000 requests/hour
Challenges and Solutions
Challenge 1: NAT Traversal
Problem: Many users are behind NATs/firewalls that block direct peer connections.
Solution:
- Use PeerJS cloud service (includes STUN/TURN servers)
- Automatic fallback to relay servers when direct connection fails
- Transparent to users - works in most network configurations
Challenge 2: Large File Handling
Problem: Browsers have memory limits, and loading entire files causes crashes.
Solution:
- Stream files in 8KB chunks
- Process chunks as they arrive
- Never load entire file into memory
- Works for files of any size
Challenge 3: Simultaneous Connection Requirement
Problem: Both sender and receiver must be online at the same time.
Solution:
- Clear messaging to users about this requirement
- Desktop app can stay online in background
- Future: Add queue system for offline receivers
Real-World Performance
In testing, we've achieved:
- Transfer speeds: Limited only by users' internet connections
- File sizes: Successfully tested with files up to 50GB+
- Concurrent transfers: Multiple simultaneous transfers supported
- Uptime: 99.9%+ reliability with PeerJS cloud infrastructure
Future Enhancements
- Resume Support: Resume interrupted transfers
- Multiple Files: Transfer multiple files in one session
- Mobile Apps: Native iOS/Android apps
- TURN Server: Self-hosted TURN server for better NAT traversal
- Analytics Dashboard: Transfer statistics and insights
- Webhook Support: Notify external services on transfer completion
Lessons Learned
- WebRTC is powerful but complex: The signaling phase requires careful coordination
- Chunk size matters: 8KB provides optimal balance for most use cases
- User education is key: Users need to understand the peer-to-peer model
- Fallbacks are essential: Always have relay servers for NAT traversal
- Privacy sells: Users appreciate zero-storage architecture
Conclusion
Building a zero-storage file transfer service with WebRTC was challenging but rewarding. The result is a service that:
- ✅ Respects user privacy (no server storage)
- ✅ Handles unlimited file sizes
- ✅ Works without signup
- ✅ Provides real-time transfer speeds
- ✅ Offers enterprise-grade security
The technology stack (WebRTC + PHP + Electron) proved to be the right choice for building a production-ready peer-to-peer file transfer service.
Try it out: ka-upload.com
API Documentation: ka-upload.com/api
Discussion
Have you built something with WebRTC? What challenges did you face? Share your experiences in the comments below!
Questions about the implementation? Feel free to ask - I'm happy to dive deeper into any part of the architecture.
Tags: #webrtc #javascript #php #p2p #filetransfer #webdev #programming #tutorial #showdev
Top comments (0)