Found while working on #38/#39 (the federation certificate-pinning fix) — the same gap exists one level deeper, in the default mesh path every bridge uses, not just the optional federation feature.
grep -rn "getPeerCertificate" src/ returns nothing anywhere in the codebase outside #39's own diff. tls-transport.ts's own doc comment claims otherwise:
Do not reject unauthorized — we do our own fingerprint verification after the TLS handshake completes.
That verification doesn't exist. A peer's peerId is established purely by what it self-reports in the introduce/pong wire messages, over a rejectUnauthorized: false TLS connection with no fingerprint check against the actual presented certificate. Nothing stops a connecting socket from claiming any peerId it likes, unrelated to the certificate it's actually presenting.
Why this is separate from #39
Federation is opt-in and, before #39, unreachable in production at all — a real hole, but a contained one. This affects the primary, always-on mesh path (TcpTransport/TlsTransport's startDataServer/connectToCoordinator/connectToPeer), so a fix here touches connection-handling code every bridge depends on, not an optional feature. That's a meaningfully bigger, riskier change to make alongside a scoped federation fix, so it wasn't folded into #39.
Likely fix shape
Same primitive #39 adds for federation — identity.ts's new fingerprintDer() — applied at the point a peer's pong/introduce message is received: verify the claimed peerId against fingerprintDer(socket.getPeerCertificate().raw) before accepting the connection, rather than trusting the self-reported ID. Given the mesh's local-network, no-CA trust model already assumes physical/network proximity as a soft boundary, this is worth scoping carefully — confirm the actual threat model change before implementing, since "any peer ID over an unauthenticated channel" may have been an accepted tradeoff for the LAN case rather than an oversight.
Found while working on #38/#39 (the federation certificate-pinning fix) — the same gap exists one level deeper, in the default mesh path every bridge uses, not just the optional federation feature.
grep -rn "getPeerCertificate" src/returns nothing anywhere in the codebase outside #39's own diff.tls-transport.ts's own doc comment claims otherwise:That verification doesn't exist. A peer's
peerIdis established purely by what it self-reports in theintroduce/pongwire messages, over arejectUnauthorized: falseTLS connection with no fingerprint check against the actual presented certificate. Nothing stops a connecting socket from claiming anypeerIdit likes, unrelated to the certificate it's actually presenting.Why this is separate from #39
Federation is opt-in and, before #39, unreachable in production at all — a real hole, but a contained one. This affects the primary, always-on mesh path (
TcpTransport/TlsTransport'sstartDataServer/connectToCoordinator/connectToPeer), so a fix here touches connection-handling code every bridge depends on, not an optional feature. That's a meaningfully bigger, riskier change to make alongside a scoped federation fix, so it wasn't folded into #39.Likely fix shape
Same primitive #39 adds for federation —
identity.ts's newfingerprintDer()— applied at the point a peer'spong/introducemessage is received: verify the claimedpeerIdagainstfingerprintDer(socket.getPeerCertificate().raw)before accepting the connection, rather than trusting the self-reported ID. Given the mesh's local-network, no-CA trust model already assumes physical/network proximity as a soft boundary, this is worth scoping carefully — confirm the actual threat model change before implementing, since "any peer ID over an unauthenticated channel" may have been an accepted tradeoff for the LAN case rather than an oversight.