The limit that matters isn't in my code: what I learned building a P2P file transfer app
Description and use
By turning TURN into a diagnostic and enforcing direct connections, Peerino eliminated client‑side abuse of the relay, reduced unnecessary traffic, and clarified the failure path. The redesign also highlighted the need for out‑of‑band identity verification and proper connection teardown.
Problem
Peerino’s initial design allowed abuse of the TURN relay because client‑side limits could be bypassed and credentials were exposed in plain text, enabling third‑party traffic and undermining the intended 100 MB transfer cap.
Outcome
By turning TURN into a diagnostic and enforcing direct connections, Peerino eliminated client‑side abuse of the relay, reduced unnecessary traffic, and clarified the failure path. The redesign also highlighted the need for out‑of‑band identity verification and proper connection teardown.
Workflow
- 1. Peerino establishes a WebRTC connection with ICE servers including TURN.
- 2. Sender retrieves connection stats via pc.getStats().
- 3. Sender iterates over stats to find selected or nominated candidate‑pair reports.
- 4. If any candidate is of type "relay", the sender sends an error message and closes the connection.
- 5. Receiver shows a user‑friendly message: "Your connection requires a relay server. To share this file, connect to WiFi."
Evidence
When I started, Peerino used TURN as a fallback. If two peers couldn't connect directly, the file was relayed through a TURN server. Cloudflare's, in my case. Files up to 100 MB went through it.
I saw the TURN credentials sitting there in plain text. Username and password, right in the receiver's browser.
I turned TURN into a diagnostic instead of a transport. If the direct path isn't available, ICE can select a relay candidate pair and the connection establishes through it. But then a gate on the sender checks the selected candidate pair and refuses to send the file.
The gate reads the WebRTC stats and looks for a relay candidate.
If the pair is relay, the sender sends an error message and the transfer stops. The user sees: "Your connection requires a relay server. To share this file, connect to WiFi."
The redesign didn't stop someone with the credentials from using the relay. They still can. The credentials still go to the browser.
To answer the question the redesign raises: what protects the account today? Rate limiting on the Worker that issues credentials — 40 requests per hour per source IP, with 15-minute credentials.
I re-read my own code before publishing this. The design doc said "the gate refuses the file." The code did exactly that. It sent an error message and returned. But it didn't close the connection.
Signaling sees peer IDs and ICE candidates. TURN sees an IP pair and a handshake when a relay path is attempted. Neither sees file content. But the signaling server isn't authenticated out-of-band, so a compromised signaling server could, in theory, MITM the connection.
- Record date: 2026-10-07T13:40:58.067Z