End-to-end peer-to-peer file sharing. Files stream directly between browsers via WebRTC — nothing is ever stored on the server.
Sender Browser ──── WebRTC DataChannel ────► Receiver Browser
│ │
└──────── WebSocket (signaling only) ────────┘
- Sender selects files and clicks Generate link — a session is created on the API (metadata only: file names, sizes, MIME types)
- Both peers connect to the signaling server via WebSocket to exchange WebRTC offer/answer/ICE
- A direct DataChannel is established between the two browsers
- Files are chunked (64 KB) and streamed with backpressure control
- Session is destroyed the moment the sender closes the tab
The signaling server never sees file content. Only session metadata (file names, sizes) and WebRTC handshake messages pass through it.
| Layer | Always on | Details |
|---|---|---|
| Transport | ✅ | DTLS — built into WebRTC, always active |
| Application | 🔒 Optional | AES-256-GCM on file bytes, enabled when sender sets a password |
When a password is set: the sender derives an AES-256 key via PBKDF2 (100k iterations, SHA-256) using a random IV, encrypts each file individually, and includes the IV in the in-channel manifest. The receiver derives the same key from the password and decrypts locally. The password and key never leave the browser.
The backend stores only a bcrypt hash of the password for the /verify endpoint — it cannot decrypt files.
Frontend
| Framework | Next.js 16 (App Router) |
| Language | TypeScript |
| Styling | Tailwind CSS v3 |
| Compression | fflate (ZIP, level 6) |
| Crypto | Web Crypto API (AES-256-GCM, PBKDF2) |
| Transport | WebRTC DataChannel (native) |
application/
├── nextjs/ # Frontend
│ └── src/
│ ├── app/
│ │ ├── page.tsx # Sender UI
│ │ └── r/[code]/
│ │ └── ReceiveClient.tsx # Receiver UI
│ ├── components/
│ │ ├── HexBackground.tsx
│ │ └── TopNav.tsx
│ └── services/
│ ├── api.ts # REST client
│ ├── signaling.ts # WebSocket signaling
│ ├── sender.ts # WebRTC sender + chunking
│ ├── receiver.ts # WebRTC receiver + reassembly
│ └── crypto.ts # AES-256-GCM, PBKDF2
Environment variables
| Variable | Default |
|---|---|
NEXT_PUBLIC_API_BASE_URL |
https://api.ssdp.cc |
NEXT_PUBLIC_WS_BASE_URL |
wss://api.ssdp.cc |
The default ICE config uses openrelay.metered.ca as a public TURN fallback for peers behind symmetric NAT. This is fine for development but you should bring your own TURN server for any production deployment.
MIT
