Zero-relay, end-to-end encrypted peer-to-peer file transfer β from any device to any device.
No cloud upload. No accounts. No file size limits imposed by a server. Files travel directly between browsers, encrypted before they leave the sender.
- True P2P β WebRTC DataChannel transfers files device-to-device; the signaling server is only a matchmaker and never sees file content
- AES-256-GCM encryption β every chunk encrypted independently before leaving the sender's machine
- ECDH P-256 key exchange β ephemeral keys negotiated per session; no pre-shared secrets
- HKDF-SHA-256 key derivation β raw ECDH bits stretched into a 256-bit AES key
- SHA-256 integrity verification β full file hash checked on receipt
- MITM fingerprint β 8-character session fingerprint both peers can read aloud to verify no interception
- Drag & Drop + Click to browse β always-active file zone,
βOkeyboard shortcut - QR code join β scan to connect a second device instantly; URL encodes the signaling server
- Dark / Light mode β sci-fi dark (default) + clean light theme
- Real-time transfer stats β speed (MB/s), progress bar, CPU %, memory, uptime
- Transfer history β completed and failed transfers with details
- Editable device name β personalise your node label
- Platform independent β runs in any modern browser on Mac, Windows, Linux, iOS, Android
- LAN mode β single
./start-lan.shlaunches everything, zero config - Internet mode β deploy frontend to Netlify + signaling to Railway (both free)
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Browser A β
β ββββββββββββ ββββββββββββββ βββββββββββββββ βββββββββββββ β
β βSignalingCβ βPeerConnect β βChunkTransferβ βCryptoSvc β β
β β (WS) β β (WebRTC) β β (DataChannelβ βAES-256-GCMβ β
β ββββββ¬ββββββ βββββββ¬βββββββ ββββββββ¬βββββββ βββββββββββββ β
βββββββββΌβββββββββββββββΌβββββββββββββββββΌββββββββββββββββββββββββββ
β WebSocket β ICE/SDP β Encrypted chunks (P2P)
βΌ βΌ βΌ
βββββββββββββββββ βββββββββββββββββββββββββββββββββββββββ
β Signaling Srv β β Browser B β
β (Python WS) β β (same stack, receiving side) β
β matchmaker β βββββββββββββββββββββββββββββββββββββββ
β only β no β
β file data β
βββββββββββββββββ
Data path: File βββΊ Encrypt (AES-256-GCM) βββΊ Chunk βββΊ WebRTC P2P βββΊ Decrypt βββΊ File
Server path: Register βββΊ Exchange SDP/ICE βββΊ (server done, P2P begins)
| Layer | Technology |
|---|---|
| Frontend framework | React 18 + TypeScript + Vite |
| Styling | Tailwind CSS + inline design tokens |
| Fonts | Space Grotesk (UI) Β· JetBrains Mono (technical values) |
| P2P transport | WebRTC DataChannel |
| Signaling | Python websockets (async WebSocket server) |
| Encryption | Web Crypto API β ECDH P-256, HKDF-SHA-256, AES-256-GCM |
| QR code | qrcode.react |
| Toasts | sonner |
| Theme | next-themes |
| State management | useSecureDrop custom hook (no external store) |
| Frontend hosting | Netlify (free tier) |
| Signaling hosting | Railway (free tier) / ngrok / LAN |
| NAT traversal | Google STUN + Open Relay TURN (free) |
# 1. Install Python deps (once)
pip3 install websockets zeroconf
# 2. Start everything
./start-lan.sh
# 3. Open on other devices
# β http://192.168.X.X:8080 (printed by the script)
# β Or scan the QR code inside the app# 1. Deploy signaling server to Railway
# railway.app β New Project β GitHub β set Root Dir = server
# Copy your Railway URL, e.g. wss://secure-drop-xyz.railway.app
# 2. Set env var in Netlify dashboard
VITE_SIGNALING_URL=wss://secure-drop-xyz.railway.app
# 3. Deploy frontend to Netlify
# netlify.com β New site β GitHub β build: npm run build, publish: dist
# 4. Open your Netlify URL on any device worldwidenpm install
npm run dev # frontend at http://localhost:8080
python server/main.py # signaling at ws://localhost:8765Take screenshot: open http://localhost:8080 β β+Shift+4
Drag a file into the drop zone and click a peer card to activate the Send button
Transfer in progress showing speed, percentage, and progress bar
The receiving device sees a modal with file info, sender fingerprint, and Accept/Reject buttons
Click the QR button in the header to generate a join URL β scan it on another device
Toggle with the βοΈ button in the header
| Property | Implementation |
|---|---|
| Key exchange | ECDH P-256 β ephemeral per session |
| Key derivation | HKDF-SHA-256 (salt + "secure-drop-v1" info) |
| Encryption | AES-256-GCM β per-chunk, unique 96-bit IV each chunk |
| Integrity | SHA-256 hash of full file, verified on receipt |
| MITM detection | 8-char hex fingerprint derived from raw ECDH shared secret β read aloud to verify |
| Server trust | Signaling server sees only peer IDs + encrypted SDP; never sees keys or file data |
| Forward secrecy | Ephemeral ECDH keys β compromise of one session reveals nothing about others |
secure-drop/
βββ src/
β βββ engine/
β β βββ SignalingClient.ts # WebSocket connection + reconnect backoff
β β βββ PeerConnection.ts # WebRTC offer/answer + ICE negotiation
β β βββ ChunkTransfer.ts # File chunking, backpressure, reassembly
β β βββ CryptoService.ts # ECDH + HKDF + AES-256-GCM + SHA-256
β β βββ IntegrityVerifier.ts # SHA-256 file hash verification
β βββ hooks/
β β βββ useSecureDrop.ts # Central app state + logic orchestrator
β β βββ useSystemMonitor.ts # CPU / memory / network metrics
β β βββ useTheme.ts # Dark/light mode
β βββ pages/
β β βββ LandingPage.tsx # Full UI β all components inline
β βββ lib/
β βββ constants.ts # ICE servers, chunk sizes, crypto params
βββ server/
β βββ main.py # LAN server (WebSocket + mDNS discovery)
β βββ cloud_main.py # Cloud server (Railway/Fly.io)
β βββ signaling.py # Peer registry + message relay
β βββ discovery.py # mDNS advertisement (zeroconf)
βββ start-lan.sh # One-command LAN launcher
βββ netlify.toml # Netlify deploy config
βββ server/railway.toml # Railway deploy config
The signaling server binds to 127.0.0.1 by default and accepts localhost browser origins. LAN or internet deployments must explicitly choose a bind address and pass each trusted web origin with --origin; do not disable origin validation as a convenience.
The signaling layer relays connection metadata and encrypted-session setup messages. It does not authenticate users, prevent malicious peers from connecting, or guarantee that a peer is honest. Verify the displayed session fingerprint out of band when the threat model requires protection against an active man-in-the-middle. Use TLS (wss://) for internet deployments, apply network access controls, and keep the server dependency set current.
MIT β free to use, modify, and distribute.
Star Secure-Drop if private peer-to-peer file transfer, WebRTC data channels, browser cryptography, or local-first privacy tooling is useful to you. Feedback on threat models, deployment hardening, and interoperability is especially welcome.





