zebra-report: deploy silent-listener fix + listener-bye-on-bfcache fix

Pulls in two upstream zebra-report commits:

  0ce1339 prefix-match in flushSfuStreams (kills the silent
          fedora-chrome listener: ontrack races peer-joined, cache
          keyed by 16-char streamID prefix, flush's strict-equality
          matcher never resolved short→full, listener heard nothing
          from anyone already in the room).

  5e8cdad send 'bye' on pagehide for listeners even when
          persisted=true (kills the closed-firefox-phone roster
          zombies sitting through the 120s readTimeout window).

Server-side backstop (60s listenerAliveTTL) ships separately in
proxy.unturf.com main; the client fix above stands on its own for
the common bye-arrives case.

Fox 2026-06-06: "rebooting fedora chrome blanka because it never
could hear any speakers for days now".
This commit is contained in:
russell@unturf.com 2026-06-06 14:48:09 -04:00
parent 0444bd1297
commit 3a0a30807e
No known key found for this signature in database
5 changed files with 60 additions and 32 deletions

View file

@ -507,9 +507,9 @@ handle /zebra-spaces-sfu* { reverse_proxy localhost:8092 { flush_interval -1
</p>
<footer style="max-width:820px;margin:2.2rem auto 0;font-size:0.65rem;color:#999;line-height:1.7;word-break:break-all;font-family:monospace">
<span style="color:#777">page integrity</span> &nbsp;·&nbsp; built <span class="stamp-date">2026-06-04</span><br>
md5 <span class="stamp-md5">e4e8e782260db19486c05c042f4f1afc</span><br>
sha256 <span class="stamp-sha">0d6678f32e1c4dcc0dd3adcb052b4b0cc5dc86fb6f2dbc5c9c79775e3feb683e</span><br>
<span style="color:#777">page integrity</span> &nbsp;·&nbsp; built <span class="stamp-date">2026-06-06</span><br>
md5 <span class="stamp-md5">f3900aa0c841baa7c61599ff247f8fe1</span><br>
sha256 <span class="stamp-sha">496b0dee4967745d5502d549c30278370cbd228ae75c3020025f9912fa9773d7</span><br>
<span style="color:#bbb">hashes are of this page with these two fields zeroed — to verify, blank them and re-hash</span><br>
<span style="color:#bbb">one self-contained file — <strong>save a copy</strong> and verify it against these hashes; point it at your own servers with ?signal= and ?turncred=</span>
</footer>

View file

@ -341,9 +341,9 @@ try {
</p>
<footer style="max-width:820px;margin:2.2rem auto 0;font-size:0.65rem;color:#999;line-height:1.7;word-break:break-all;font-family:monospace">
<span style="color:#777">page integrity</span> &nbsp;·&nbsp; built <span class="stamp-date">2026-06-04</span><br>
md5 <span class="stamp-md5">9870bd4e4dae3439f3238f6c7bfbb0e1</span><br>
sha256 <span class="stamp-sha">7bf54074bc06b6a9378d75cfc678aadf626c2c2e921149db5610a7e0ed86ef6a</span><br>
<span style="color:#777">page integrity</span> &nbsp;·&nbsp; built <span class="stamp-date">2026-06-06</span><br>
md5 <span class="stamp-md5">ea02f55584d8042b03fcbd999e1da240</span><br>
sha256 <span class="stamp-sha">c7cb5f8be8241d4634cca84acd3343c1fbfd0c7053b260bcee05084aa7bd83aa</span><br>
<span style="color:#bbb">hashes are of this page with these two fields zeroed — to verify, blank them and re-hash</span><br>
<span style="color:#bbb">one self-contained file — <strong>save a copy</strong> and verify it against these hashes; point it at your own servers with ?signal= and ?turncred=, or <a href="host-your-own.html" style="color:#999">host your own community</a></span>
</footer>

View file

@ -1747,9 +1747,9 @@ logLine('sys', 'chat content lives in encrypted SRTP audio. no IP packets carry
})();
</script>
<footer style="max-width:820px;margin:2.2rem auto 0;font-size:0.65rem;color:#999;line-height:1.7;word-break:break-all;font-family:monospace">
<span style="color:#777">page integrity</span> &nbsp;·&nbsp; built <span class="stamp-date">2026-06-04</span><br>
md5 <span class="stamp-md5">b7b1b1b9c612636a17adf94eca5835b5</span><br>
sha256 <span class="stamp-sha">7dff2ae15621b8c56d4c0c5a50afe5b3305317a161b281d66c2417c3b1cbf1a6</span><br>
<span style="color:#777">page integrity</span> &nbsp;·&nbsp; built <span class="stamp-date">2026-06-06</span><br>
md5 <span class="stamp-md5">e9fb81e6d445184e8a4967dfe56883f9</span><br>
sha256 <span class="stamp-sha">a44a19223961e6a8e4f6010d073ff1e95246d908eb3b27d322fcd7bb17e5ad39</span><br>
<span style="color:#bbb">hashes are of this page with these two fields zeroed — to verify, blank them and re-hash</span><br>
<span style="color:#bbb">one self-contained file — <strong>save a copy</strong> and verify it against these hashes; point it at your own servers with ?signal= and ?turncred=, or <a href="host-your-own.html" style="color:#999">host your own community</a></span>
</footer>

View file

@ -747,9 +747,9 @@ else wirePuppet();
</section>
<footer style="max-width:560px;margin:2.2rem auto 0;font-size:0.65rem;color:#999;line-height:1.7;word-break:break-all;font-family:monospace">
<span id="pi-seal" style="color:#777;cursor:default;user-select:none" title="">page integrity</span> &nbsp;·&nbsp; built <span class="stamp-date">2026-06-04</span><br>
md5 <span class="stamp-md5">704bc6f8c294b01704add1b59ec23d69</span><br>
sha256 <span class="stamp-sha">06b739c56ebdfab90591ea4c5e8d8cb6b5cd854c02f526753f724857c518a64e</span><br>
<span id="pi-seal" style="color:#777;cursor:default;user-select:none" title="">page integrity</span> &nbsp;·&nbsp; built <span class="stamp-date">2026-06-06</span><br>
md5 <span class="stamp-md5">ba6a5714f597469b0aa61b32657f50f4</span><br>
sha256 <span class="stamp-sha">78b5fb863ec406ec39081a0231fb909eef3abf332e84c0a5f25942f62f49de1b</span><br>
<span style="color:#bbb">hashes are of this page with these two fields zeroed — to verify, blank them and re-hash</span><br>
<span style="color:#bbb">one self-contained file — <strong>save a copy</strong> and verify it against these hashes; point it at your own servers with ?signal= and ?turncred=, or <a href="host-your-own.html" style="color:#999">host your own community</a></span>
</footer>

View file

@ -3507,15 +3507,30 @@ function flushSfuStreams(){
* fresh-attach on the same uuid and producing audible overlap.
* Fox 2026-06-05 telemetry: "fxhp-android-firefox is hearing two
* streams now as listener" — fix the wrong guard, not double-audio
* via dedup. */
* via dedup.
*
* Match by prefix: the cache key from handleRemoteSfuTrack is whatever
* resolution returned at ontrack time — full pubhex if the member
* roster already had the publisher, or the 16-char streamID prefix
* otherwise (ontrack races peer-joined; SFU sub PC ontrack can fire
* before signal-server announces existing speakers). Strict === on
* the full-decoded mm pubkey would never match the prefix key, so a
* listener that joined a room with N existing speakers stayed
* permanently silent. Symmetry with the startsWith resolution in
* handleRemoteSfuTrack (line ~4640). fox 2026-06-06: "fedora chrome
* is flawless besides not able to hear any mics, was working a few
* days back, only zebra code changed." */
for (const [pubHex, stream] of sfuStreamsByPubHex){
for (const [uuid, mm] of members){
try {
if (mm.pubkey && hex(unb64(mm.pubkey)) === pubHex){
if (!remoteAudio.has(uuid) && !listenerAudioNodes.has(uuid)){
attachSfuTrack(uuid, stream);
if (mm.pubkey){
const fh = hex(unb64(mm.pubkey));
if (fh.startsWith(pubHex)){
if (!remoteAudio.has(uuid) && !listenerAudioNodes.has(uuid)){
attachSfuTrack(uuid, stream);
}
break;
}
break;
}
} catch(_){}
}
@ -7556,20 +7571,33 @@ refreshSpeakerList();
* this, a closed tab dies silently → hiccup grace → 8s of trailing
* audio. Fox 2026-06-03.
*
* Critical guard: pagehide ALSO fires when the page goes into the
* back-forward cache (mobile app-switch / lock screen / minimise),
* with event.persisted=true. We must NOT send bye in that case —
* the page is still alive, just paused; on pageshow it resumes with
* the same WS / SFU / mesh state. Sending bye here would force the
* server to evict the SFU PCs, and when the phone comes back the
* resumed audio path stays muted (Fox 2026-06-03: "now the phone
* leaving and coming back cannot hear the music").
* Critical guard for SPEAKERS: pagehide ALSO fires when the page
* goes into the back-forward cache (mobile app-switch / lock screen
* / minimise), with event.persisted=true. A speaker on bfcache
* MUST NOT send bye — finalizeLeave would post evictFromSFU on
* their publisher AND subscriber PCs, and when the phone comes
* back the resumed audio path stays muted (Fox 2026-06-03: "now
* the phone leaving and coming back cannot hear the music").
*
* The JS heartbeat handles the bfcache case independently — while
* the page is bfcached, setInterval is paused, so the server's
* aliveTTL fires after 45s if the user doesn't come back. */
* LISTENERS take the opposite branch: send bye even on persisted.
* finalizeLeave already exempts listeners from evictFromSFU
* (signal-server main.go line ~1089), so a listener bye is roster-
* only — peer-left broadcast, member entry deleted, no eviction.
* On pageshow the listener re-handshakes (sub PC + WS join) like
* a cold join, same path that already works. The cost is a brief
* "rejoin" appearance in other clients' rosters; the benefit is
* the immediate disappearance of closed-Firefox-on-phone zombies
* who otherwise wait out the 120s readTimeout + 8s hiccup grace
* because listeners are exempt from the server's aliveJanitor.
* Fox 2026-06-06: "closed firefox on both phones [they] are both
* still in the list".
*
* The JS heartbeat handles the speaker bfcache case independently
* — while the page is bfcached, setInterval is paused, so the
* server's aliveTTL fires after 45s if the user doesn't come back. */
function sendByeIfRealClose(ev){
if (ev && ev.persisted) return; // bfcache — page is napping, not dying
/* speaker on bfcache: keep the PCs warm, ride out the nap */
if (ev && ev.persisted && myRole !== 'listener') return;
try {
if (ws && ws.readyState === WebSocket.OPEN){
ws.send(JSON.stringify({ type: 'bye' }));
@ -7875,9 +7903,9 @@ logLine('', 'ready — pick a handle, type a rendezvous code, enter the space');
</script>
<footer style="margin:2.2rem auto 0;font-size:0.65rem;color:#999;line-height:1.7;word-break:break-all;font-family:monospace">
<span id="pi-seal" style="color:#777;cursor:default;user-select:none" title="">page integrity</span> &nbsp;·&nbsp; built <span class="stamp-date">2026-06-05</span><br>
md5 <span class="stamp-md5">96bc1aa9b23545fe92f4dfe02dfda74c</span><br>
sha256 <span class="stamp-sha">df6d1806da9b5844d6b9779ce84ae41fc21feeb4e2352a747a09662741bab08f</span><br>
<span id="pi-seal" style="color:#777;cursor:default;user-select:none" title="">page integrity</span> &nbsp;·&nbsp; built <span class="stamp-date">2026-06-06</span><br>
md5 <span class="stamp-md5">973996244966a40f7370e23bec5dcbb5</span><br>
sha256 <span class="stamp-sha">66f3d4eebd028f7ea16ea58df624ed0d2223505ff5c99715730379757e613630</span><br>
<span style="color:#bbb">hashes are of this page with these two fields zeroed — to verify, blank them and re-hash</span><br>
<span style="color:#bbb">one self-contained file — <strong>save a copy</strong> and verify against these hashes; point at your own servers with ?signal= and ?turncred=, or <a href="host-your-own.html" style="color:#999">host your own community</a></span>
</footer>