From 5836424baffdccb20c0b77872e93a362be6bd058 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mats=20T=C3=B6rnberg?= Date: Tue, 22 Sep 2026 12:10:12 +0200 Subject: [PATCH] =?UTF-8?q?Force=20TURN=20relay=20on=20WebRTC=20retry=20?= =?UTF-8?q?=E2=80=94=20works=20around=20same-LAN=20NAT/isolation=20issues?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A connection failing specifically when both peers are on the same LAN (as opposed to over the open internet) usually means direct/STUN candidates are being silently dropped by the router — either AP/router client isolation (common on home/guest Wi-Fi) or no NAT hairpin/ loopback support for two LAN devices reaching each other via their own public IP. On the first retry after a connection failure, set iceTransportPolicy: 'relay' so the rebuilt RTCPeerConnection only offers TURN-relayed candidates, bypassing the LAN path entirely. Confirmed the coturn secret and pod are up in-cluster so relay auth will actually succeed. Only one side needs to do this — since it only offers relay candidates, whatever pairs with them is forced through the relay regardless of what the other side additionally tried. Co-Authored-By: Claude Sonnet 5 --- public/app.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/public/app.js b/public/app.js index da96622..b1e66f8 100644 --- a/public/app.js +++ b/public/app.js @@ -387,7 +387,13 @@ function createOrphanPeer(id) { function ensurePC(peer, initiator) { if (peer.pc) return peer; - const pc = new RTCPeerConnection({ ...effectiveIce(), iceCandidatePoolSize: 4 }); + const config = { ...effectiveIce(), iceCandidatePoolSize: 4 }; + // After a failed retry, force everything through TURN relay — sidesteps + // AP/router client isolation and NAT-hairpin issues that are common when + // both peers are on the same LAN (direct/STUN candidates get silently + // dropped by the router in that case, so give up trying them). + if (peer.resetAttempts > 0) config.iceTransportPolicy = 'relay'; + const pc = new RTCPeerConnection(config); peer.pc = pc; peer.polite = S.myId > peer.id;