diff --git a/web/zebra-spaces.html b/web/zebra-spaces.html index d662bf3..196e1c5 100644 --- a/web/zebra-spaces.html +++ b/web/zebra-spaces.html @@ -563,7 +563,11 @@ async function sfuSubscribe(){ /* server-initiated offer: POST /subscribe (empty body) — SFU answers with * an SDP offer containing one m-line per current publisher. We answer it * and POST the answer back, which completes the initial handshake. */ - const offerRes = await fetch(SFU_BASE + '/subscribe?room=' + encodeURIComponent(roomID), { + /* pass our pubkey as `sub` so the SFU can evict us if we're booted — + * server-side boot also calls SFU /internal/block which kicks any + * subscriber whose sub pubkey matches */ + const subUrl = SFU_BASE + '/subscribe?room=' + encodeURIComponent(roomID) + '&sub=' + myKeys.pubHex; + const offerRes = await fetch(subUrl, { method:'POST', headers:{'Content-Type':'application/json'}, body: '{}' }); if (!offerRes.ok){ pc.close(); throw new Error('sfu subscribe http '+offerRes.status); } @@ -914,10 +918,16 @@ async function handleSignal(raw){ if (mm) logLine('', mm.handle+' was removed by '+(by?by.handle:'a mod')); if (m.uuid === myUUID){ /* server will close our socket; surface a clear notice and prevent - * the WS reconnect loop from auto-rejoining into a boot loop */ + * the WS reconnect loop from auto-rejoining into a boot loop. Tear + * down our SFU + mesh PCs too so we actually stop hearing / + * broadcasting — closing the WS alone leaves the WebRTC paths up. */ wantConnected = false; showNotice('You were removed from this space'+(by?' by '+by.handle:'')+'.', 'warn'); logLine('err','you were removed from this space'); + for (const u of [...peers.keys()]) tearPeer(u); + sfuUnpublish().catch(()=>{}); + sfuUnsubscribe().catch(()=>{}); + dropMic(); } members.delete(m.uuid); tearPeer(m.uuid); handraise.delete(m.uuid); renderRoom(); @@ -1288,8 +1298,8 @@ logLine('', 'ready — pick a handle, type a rendezvous code, enter the space');