Security model

What's end-to-end encrypted. What isn't. No marketing.

Reach uses standard WebRTC for the actual call. Your voice is end-to-end encrypted between the two browsers — we can't decrypt it even if we wanted to. Metadata (who called whom, when, for how long) is server-mediated. This page spells out exactly what means, layer by layer, so there's no marketing confusion.

The layers, in plain English

Layer
End-to-end encrypted?
Why / how
Voice (the audio you hear)
Yes
WebRTC negotiates DTLS-SRTP keys directly between the two browsers using the SDP offer/answer. The keys never appear on our server. The encrypted media flows browser ↔ browser (or browser ↔ TURN ↔ browser when one side is behind symmetric NAT). Our signaling server relays the offer/answer envelope but cannot read the keys inside it. Same security model as Jitsi, FaceTime, and Google Meet 1:1 calls.
Signaling messages (SDP, ICE candidates)
Server-mediated
Our Cloudflare Worker relays the SDP offer/answer and ICE candidates between the two peers. We can see this data while it's in flight. The protocol spec at packages/reach-signaling-cf/PROTOCOL.md commits to never logging SDP or ICE candidate content (they contain local network info that would reveal your network topology). Workers tail logs include only event types and call IDs, not bodies.
TURN relay traffic (the ~15% of calls that need a relay)
Encrypted, but Cloudflare sees the traffic shape
When you and your caller are both behind symmetric NAT (common on corporate networks), the encrypted media is bounced through Cloudflare's TURN servers (Cloudflare Calls). Cloudflare can see who is relaying to whom and how much data, but cannot decrypt the audio (it's encrypted at the SRTP layer with keys it doesn't have). About 85% of calls don't need TURN and never touch Cloudflare's relay.
Call metadata (who called whom, when, duration)
Visible to us
Routing a call from @alice to @bob requires our server to know both handles. The call_log table on the operator dashboard records caller hash, callee hash, started_at, duration, decision (answered / voicemail / declined). It does not record audio or transcript. Compared to Signal, which uses "sealed sender" to hide caller identity from the server — we don't have that yet. It's a real architectural addition planned for a future version.
Identity (your @handle ↔ phone ↔ wallet)
Stored on us
The D1 database holds the handle ↔ phone-hash ↔ wallet-pubkey mapping. Phone numbers are hashed (HMAC-SHA-256) and encrypted at rest (AES-256-GCM) — we can decrypt with our key (kept as a Cloudflare Worker secret). Wallet pubkeys are stored plaintext (already public on-chain). Handles are stored plaintext (they're public). We could be compelled by subpoena to disclose the mapping; that's why phone-binding is opt-in.
Transport (browser ↔ our server)
TLS 1.3
Everything to / from our servers is TLS 1.3 over WSS (signaling) or HTTPS (REST). Cloudflare terminates TLS at the edge. Between Cloudflare and your browser the connection is encrypted; between Cloudflare's edge and our origin Worker, traffic is on Cloudflare's internal network.

Where keys live

What we never log

What we can log (and what we do log)

Compared to Signal

Signal is the gold standard. We are not Signal. Here's the precise gap:

Signal
Reach (today)
Voice media E2E encrypted
Yes
Yes (WebRTC DTLS-SRTP)
Caller identity hidden from server
Yes (sealed sender)
No — we route by handle
Contact discovery private
Yes (private set intersection)
No — public /handles/:h + bloom filter
Forward secrecy on metadata
Yes (per-message ratcheting)
No — TLS to our server
Cover traffic / traffic-shape resistance
Partial
No
Independently audited
Yes, many times
Not yet

If you need Signal-grade metadata privacy today, use Signal. If you want a free, brandable, callable handle that rings your browser — with the same encrypted-voice guarantee you get from a Google Meet 1:1 — Reach is appropriate. Closing the metadata-privacy gap is a real architectural project we'd love to do; it's tracked in our backlog as "sealed-sender + PIR contact discovery."

What this is not

Reproducing the claim

Want to verify "voice is E2E encrypted" yourself rather than take our word for it? In Chrome:

  1. Open chrome://webrtc-internals/ in a tab.
  2. Place a call from reach.inferlane.dev/@whoever.
  3. Find the active RTCPeerConnection; check the iceConnectionState and the SctpTransport's DtlsTransport — you'll see DTLS state: connected with a fingerprint of the remote peer's certificate.
  4. Confirm the chosen ICE candidate pair: if local-candidate type and remote-candidate type are both host or srflx, media is direct browser-to-browser. If relay, it's going through TURN (still encrypted; just relayed).
  5. Inspect the SrtpEncoder stats — you'll see cipherSuite: AEAD_AES_128_GCM (or similar). Those are the keys negotiated directly between you and your peer.

The source for the signaling server is in packages/reach-signaling-cf/. The auth + verify flow for SIWS wallets is in packages/reach-handles/src/wallet/. Read it. File issues for anything you find sketchy.