zebra-audio: stop displaying peer IP/port in path status — no dox

reportPath used to print 'you A:B ↔ peer C:D' in path-status and the log,
exposing both participants' real IP addresses to anyone glancing at the
screen. Show DIRECT vs RELAYED and the candidate types
(host/srflx/relay) only; addresses are gone. CLAUDE.md gains a
'Web UI privacy — never display peer IPs' rule so this does not regress.
This commit is contained in:
Russell Ballestrini 2026-05-29 17:43:33 -04:00
parent 5e1c4402b2
commit 17ba25f9cb
No known key found for this signature in database
2 changed files with 21 additions and 8 deletions

View file

@ -120,6 +120,16 @@ Handshake frame: `[0x5A 0x42 0x01 baud_lo baud_hi xor_cksum]` — 6 bytes at 50
- userland only — survives without root
- **Operation Voyeur**: all terminal output is public — never pass secrets through these channels unencrypted. The web page does ECDH key exchange + AES-256-GCM before TX.
### Web UI privacy — never display peer IPs
`chat.html` and `zebra-audio.html` must never print peer IP addresses or ports
in the page UI or in any visible log. Our users do not run Wireshark — if it
is not on the screen, peers cannot dox each other. Candidate types
(`host`/`srflx`/`relay`) from `pc.getStats()` are abstract and fine to show
(they tell you direct vs relayed); `loc.address` / `loc.port` /
`rem.address` / `rem.port` are not. The WebRTC stack already obfuscates host
candidates via mDNS by default — do not undo that work in the UI.
### Web page integrity stamping
Each deployed page (`web/chat.html`, `web/zebra-audio.html`, `web/how-it-works.html`,

View file

@ -317,13 +317,16 @@ async function reportPath(){
if (!sel) stats.forEach(r=>{ if(r.type==='candidate-pair'&&r.state==='succeeded'&&(r.nominated||r.selected)) sel=r; });
if (!sel){ setPath('path: connecting…', null); return; }
const loc=stats.get(sel.localCandidateId)||{}, rem=stats.get(sel.remoteCandidateId)||{};
const la=(loc.address||loc.ip||'?')+':'+(loc.port||'?'), ra=(rem.address||rem.ip||'?')+':'+(rem.port||'?');
const relayed = loc.candidateType==='relay'||rem.candidateType==='relay';
/* show the real endpoints so it's verifiable: host = the two devices' IPs;
* relay = the TURN server's IP (142.93.73.64) appears on at least one side. */
setPath('path: '+(relayed?'RELAYED via TURN':'DIRECT P2P')+' — you '+la
+' ↔ peer '+ra+' ('+(loc.candidateType||'?')+'/'+(rem.candidateType||'?')+')', relayed?'err':'ok');
logLine('', 'active pair: '+la+' ['+(loc.candidateType||'?')+'] ↔ '+ra+' ['+(rem.candidateType||'?')+']');
/* deliberately do NOT surface loc.address/loc.port/rem.address/rem.port —
* printing them would dox the peer to anyone who glances at the screen.
* candidate types (host/srflx/relay) are abstract enough to show: they
* tell you whether you're direct or relayed without leaking an address. */
setPath('path: '+(relayed
? 'RELAYED via TURN — a server forwards your (encrypted) audio'
: 'DIRECT peer-to-peer — nobody between you')
+' ('+(loc.candidateType||'?')+'/'+(rem.candidateType||'?')+')', relayed?'err':'ok');
logLine('', 'transport: '+(relayed?'relayed via TURN':'direct peer-to-peer')+' ('+(loc.candidateType||'?')+'/'+(rem.candidateType||'?')+')');
} catch(_){}
}
@ -641,8 +644,8 @@ else wirePuppet();
<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-05-29</span><br>
md5 <span class="stamp-md5">1e587958aedf93270535fb510aa946e7</span><br>
sha256 <span class="stamp-sha">ba1975bd8011a139f8dec8a868cc218e2aeb09a969831343f02564524412f726</span><br>
md5 <span class="stamp-md5">70f28512b6bc9af9121f6c533676024d</span><br>
sha256 <span class="stamp-sha">7ca19b46a6738039f4e4d10e026019a142c769d1a96017419f562f7d701be6ac</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>