chat.html: WebRTC P2P audio + in-browser AudioWorklet decode

major architectural rework. previously chat.html modulated its own
GainNode and depended on a native zebrad daemon polling local PA
to close the receive loop. that worked single-machine multi-tab
but had no cross-laptop path without bridging external audio (voice
call, virtual sink, etc.).

new architecture: each peer's modulated audio is the outbound track
of a WebRTC peer connection. each peer decodes the inbound RTC
track in-page via an AudioWorklet energy detector + the same UART
state machine zebrad uses. cross-laptop works because WebRTC carries
the modulated audio between machines. native daemon not required.

  outbound:  oscillators (440/441 Hz) → modulation gain (MARK/SPACE)
             → channel merger → MediaStreamDestination → RTCPeer-
             Connection.addTrack().  also → optional local monitor
             gain (muted by default; toggleable for debugging).

  inbound:   RTCPeerConnection.ontrack → MediaStreamSource
             → AudioWorklet (per-quantum peak detection, 128 samples)
             → main thread postMessage → UartDecoder → FrameAssembler
             → onRxFrame (same handler as before).

  signaling: v1 uses manual SDP paste via two textareas. role A
             ("start a new connection") creates an offer, waits for
             gathered ICE, exposes JSON-serialized localDescription
             for copy. role B ("join") pastes that, generates an
             answer, gets pasted back to A.  STUN: stun.l.google.com.
             v2 will add a tiny signaling server for auto-pair.

  threat:    SRTP carries the modulated audio. Wireshark sees only
             encrypted RTP packets; chat content lives in audio
             amplitude transitions inside the encrypted payload.
             additional app-layer AES-GCM (passphrase mode PBKDF2,
             pubkey mode ECDH P-256) preserved unchanged.

zebrad daemon (src/zebrad.c) stays in the repo as the V2 disclosure
demo (a same-UID host process polling PulseAudio sink-input volumes
of an arbitrary same-UID tab) but is no longer required for chat.

preserved unchanged from prior chat.html:
  * crypto modes (passphrase + pubkey), mode toggle
  * sender id (random per-tab in passphrase mode; SHA-256(pubkey)[0:4] in pubkey mode)
  * self-echo filter via sid match
  * frame codec (OFFER/READY at 50 baud, DATA/HELLO with CRC-32)
  * benchmark + handshake state machine
  * peer list, chat log
  * threat model + mitigation panel
  * BroadcastChannel dev loopback via ?loopback=1

file size: 1258 lines, ~46 KB. JS syntax-clean (node --check).
HTML balanced.
This commit is contained in:
Russell Ballestrini 2026-05-27 16:14:51 -04:00
parent dcb1c8e5a1
commit 0dede3d6ca

File diff suppressed because it is too large Load diff