diff --git a/zebra-report/index.html b/zebra-report/index.html index 88ae7f7..8cc7d5e 100644 --- a/zebra-report/index.html +++ b/zebra-report/index.html @@ -394,20 +394,32 @@ const PBKDF2_ITER = 600000; const LOOPBACK = new URLSearchParams(location.search).has('loopback'); -const RTC_CONFIG = { - iceServers: [ - /* our STUN/TURN on proxy.uncloseai.com — coturn deployed via - * git.unturf.com/engineering/unturf/proxy.unturf.com. - * credentials are public by design (baked into a static page); - * rate-limited via coturn quotas, not via secrecy. */ - { urls: ['stun:proxy.uncloseai.com:3478', 'stun:stun.l.google.com:19302'] }, - { - urls: 'turn:proxy.uncloseai.com:3478', - username: 'zebra', - credential: '7a4a2b1c8d9e6f5a' - } - ] +/* STUN is free + needs no auth; TURN credentials are fetched fresh per + * connection from /turn-cred (time-limited HMAC, so each client gets its own + * coturn quota instead of sharing one static login). rtcConfig starts STUN-only + * and is upgraded with TURN once the creds arrive — if the fetch fails we still + * connect directly / via STUN, just without the relay fallback. */ +const TURN_CRED_URL = new URLSearchParams(location.search).get('turncred') + || 'https://cors-proxy.uncloseai.com/turn-cred'; +let rtcConfig = { + iceServers: [{ urls: ['stun:proxy.uncloseai.com:3478', 'stun:stun.l.google.com:19302'] }] }; +async function refreshTurnCred() { + try { + const c = await (await fetch(TURN_CRED_URL, { cache: 'no-store' })).json(); + if (c && c.credential && c.uris) { + rtcConfig = { + iceServers: [ + { urls: c.stun || ['stun:proxy.uncloseai.com:3478'] }, + { urls: c.uris, username: c.username, credential: c.credential } + ] + }; + logLine('sys', 'TURN credentials fetched (relay fallback available)'); + } + } catch (e) { + logLine('sys', 'no TURN credentials — direct/STUN only (' + e.message + ')'); + } +} /* ============================================================== * * tiny utils * @@ -1132,7 +1144,7 @@ const rtcStatus = $('rtc-status'); function ensurePC() { if (pc) return pc; if (!carrierOn) throw new Error('start carrier first'); - pc = new RTCPeerConnection(RTC_CONFIG); + pc = new RTCPeerConnection(rtcConfig); for (const tr of outboundStream.getTracks()) pc.addTrack(tr, outboundStream); /* Force a memoryless codec (G.711 PCMU/PCMA, else G.722) ahead of Opus. * Opus is a perceptual codec that re-quantizes in 20ms frames and smears the @@ -1338,6 +1350,7 @@ async function connectRelay() { logLine('sys', 'carrier auto-started for relay connect'); } catch (e) { relaySet('carrier failed: ' + e.message, 'err'); return; } } + await refreshTurnCred(); /* fetch fresh ephemeral TURN creds before the PC */ try { relayKey = await deriveSignalKey(code); const room = await deriveSignalRoom(code);