LiveKit ICE candidate lottery: connections intermittently establish with ZERO media (affects prod audio) #91
Labels
No labels
bug
duplicate
enhancement
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
dark/nether-voicebridge#91
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Symptom
LiveKit connections intermittently deliver zero media while signaling stays healthy: the publisher believes it is publishing (local track published, no errors, self-paced feed consumed), the subscriber stays subscribed with no errors — but no RTP arrives. Captured audio is pure digital silence (rms 0.0, 0% nonzero samples). Per-connection coin flip: on 2026-07-11 evening, ~1/3 of e2e connections carried media, ~2/3 delivered nothing — uniformly across unrelated scenarios (baseline
audio_m2d_unenc, recovery scenarios), flipping pass/fail run-to-run within minutes at low host load, clean ICMP, LiveKit HTTP healthy.Likely mechanism (evidence)
Speaker-side
livekit=debugcaptured LiveKit's SDP answer advertising six ICE candidate families from the SFU (nether-red):97.107.138.76(public Linode IPv4) — udp 50146 + tcp 7881100.64.0.2(tailscale IP) — udp + tcp10.20.1.1(LAN/incus bridge IP) — udp + tcp2600:3c03::…(public IPv6),fd7a:115c:a1e0::2(tailscale IPv6) — udp + tcpEach new connection from cubuntu (bridge, ghosts, harness actors) runs an ICE lottery across these. If a pair passes connectivity checks but doesn't actually carry RTP (asymmetric tailscale ACL — the ACL was modified 2026-07-11; unreachable LAN IP; IPv6 asymmetry), the connection establishes with zero media. Matches all observations: per-connection randomness, signaling up, silence.
Why it matters — PROD
Prod's subscriber + ghosts connect to LiveKit the same way from the same host. A broken-but-checks-passing path means real calls with dead air at the same per-connection rate. This may also explain historical intermittent "can't hear / everyone dropped" reports previously attributed to CPU contention alone.
Suggested fix
Constrain LiveKit's advertised candidates to paths that verifiably carry media from cubuntu:
rtc.node_ip/use_external_ipin livekit.yaml to advertise only the public IP, orThen re-verify with the e2e baseline in a loop (
audio_m2d_unenc×5 — should pass 5/5; today it passes ~1/3).Context
Found while live-verifying the
recovery_no_sparee2e scenario (#81 follow-up); blocked its confirmation. Evidence:/opt/nvb-staging/logs/run-{gate3,confirm2,diag2,ns2}.log+ WAV RMS analysis in/opt/nvb-staging/e2e-artifacts/. Related: #90 (build/prod isolation).RESOLVED — root cause confirmed and fixed 2026-07-11 evening.
Root cause: livekit-server migrated from an incus container to the nether-red host on 2026-06-19 (systemd
active sincematches the old container's tailscale node going offline 22d ago, to the day). Running on the host, LiveKit enumerated ALL host interfaces and advertised them as ICE candidates — includingtailscale0(100.64.0.2 / fd7a::) and the incus bridge (10.20.1.1). The tailscale path is ACL-filtered: nether-red's own tailscaled journal shows it dropping LiveKit traffic (Drop: TCP{100.64.0.33 → 100.64.0.2:7881},Drop: UDP{… → 100.64.0.2:501xx}) — including bursts at 06:08 (during the nightly e2e) and 18:53 (the exact second of a failing test run). Per-connection ICE selection then landed on a poisoned route ~2/3 of the time → connection established, zero media, pure-silence audio. The lottery existed since June 19; the tailscale ACL/tag changes made today were unrelated (drops predate them).Fix (applied by dark on nether-red,
/etc/livekit/livekit.yaml): restrict candidate gathering to the public interface —(+
node_ip: 97.107.138.76kept as belt-and-suspenders; notenode_ipalone did NOT stop interface enumeration.)systemctl restart livekit-server.Verified: post-restart SDP advertises ONLY eth0's public IPv4 + IPv6 (tailscale/LAN candidates gone; IPv6 media preserved). Acceptance:
audio_m2d_unenc5/5 PASS (was ~1/3 before the fix), thenexternal_disconnect_recoveryPASS 0.918 +recovery_no_sparePASS 0.923. Prod's media path is fixed by the same change. Cleanup note: the stale offlinelivekittailscale node (100.64.0.25, the old container's) can be deleted from headscale.