chat.html: drop self-echo frames at onRxFrame entry

zebrad reads @DEFAULT_MONITOR@ which mixes every PA stream on
a machine, so each browser tab hears its own carrier echoed
back. without a filter, every outbound message would render
twice in the chat log (once as 'me' at send time, once as
'peer' on echo arrival).

filter: compare frame.sid (carried in DATA + HELLO) against
our own senderIdBytes(). if equal, drop frame before any UI
work. loopback mode unaffected since BroadcastChannel never
echoes to sender by spec.

senderIdBytes returns a stable per-tab id:
  * passphrase mode: random 4 bytes cached for tab lifetime
  * pubkey mode: first 4 of SHA-256(pubkey), stable across reloads
This commit is contained in:
Russell Ballestrini 2026-05-27 16:00:28 -04:00
parent c8a755513f
commit dcb1c8e5a1

View file

@ -772,6 +772,15 @@ async function onRxFrame(bytes) {
logLine('err', `rx: malformed frame (type=${f.type}, ${f.error})`);
return;
}
/* same-machine PA mix echoes our own carrier back to us. drop any frame
* whose sender id matches our own so we don't double-render. loopback
* mode is unaffected because BroadcastChannel never echoes to sender. */
if (f.sid) {
const mine = await senderIdBytes();
let isSelf = true;
for (let i = 0; i < 4; i++) if (f.sid[i] !== mine[i]) { isSelf = false; break; }
if (isSelf) return;
}
if (f.type === T_OFFER) {
logLine('sys', `rx: OFFER from peer — ${f.baud} baud`);
/* respond with READY at handshake baud */