The live call — what protects it, and what you can check
The other pages here describe the async Space: a sealed ledger, a content-blind platform, a decrypt-once render. A live call is a different guarantee with a different shape, and conflating the two is the easiest mistake to make about Witbitz. This page is the call's half.
One line: a call's audio, video and chat are end-to-end encrypted between browsers and no server in the path can decode them — but the way you'd verify that is currently not exposed in Witbitz Call's UI (§4), and unlike upstream Kibitz, media here goes through a relay by default (§2).
Witbitz Call is the Kibitz engine, rebranded — a pinned dependency, not a fork. The mechanisms below are Kibitz's; the configuration is ours, and it differs in two ways that matter. Engine-level detail: kibitz.chat/security.
1. Media — end-to-end encrypted
Calls use WebRTC. Audio and video are encrypted with DTLS-SRTP, and the keys are negotiated directly between the participating browsers — they never leave the devices. There is no SFU or media server that decodes, mixes or records anything. Chat, shared links and directed messages travel the same way, over direct DTLS-encrypted data channels between each pair; no participant relays another's content.
The single assumption is that those keys are exchanged honestly. That happens in signalling (§3), and §4 is how you'd check it.
2. ⚑ The relay is the default here, not the exception
Upstream Kibitz uses a TURN relay only when a direct connection is impossible, and says most calls never need
one. Witbitz Call sets FORCE_RELAY_DEFAULT = true (kibitz/src/core/callMedia.ts) — direct-first was
tried and regressed three separate times on real networks, so relay-on is the deliberate default. A ?relay
URL parameter overrides it.
What that means, stated plainly:
- The relay cannot read the call. It forwards packets that are already DTLS-SRTP encrypted; it sees ciphertext, IP addresses and traffic volume. The end-to-end property in §1 is unaffected.
- Participants no longer see each other's IP addresses — the relay's address stands in. On a direct WebRTC connection each peer learns the other's IP; routing through the relay removes that. This is a privacy gain over the direct path, and it is the upstream "hide my IP" behaviour turned on for everyone.
- The relay is ours.
/api/turn, same-origin, on Witbitz infrastructure — not Kibitz's. So Witbitz sees the metadata a relay sees: who connected, from what address, for how long, and how much traffic. Not content. - It costs a little latency, which is the trade accepted for calls that connect reliably.
3. Signalling — introductions only, and the one party that could attack
Two browsers cannot find each other unaided. A signalling server briefly exchanges setup data: a temporary peer id, the room name from the link, and ICE candidates (which contain IP addresses). It sees connection metadata, never call content, and stores none of it.
Witbitz Call uses Kibitz's signalling broker (signal.kibitz.chat) — Witbitz has no equivalent service. So
for the live-call path there is a third party in the introduction step, and it is worth naming rather than
glossing.
It matters because signalling is also where the browsers publish their certificate fingerprints. A compromised or compelled signalling server is the one party that could attempt to sit in the middle of a future call by substituting a key during setup. A relay cannot — it only ever sees ciphertext. That is exactly the attack §4 exists to catch.
4. ⚑ Verifying it — the safety code, and why you cannot use it here today
Every WebRTC "end-to-end" claim ultimately trusts the signalling channel to deliver the right keys. The mechanism that removes that trust is a safety code: a short emoji code derived from the two browsers' actual DTLS certificates — the ones really used on the wire, not the ones named in signalling. Same idea as Signal's safety numbers. Matching codes mean nobody is in the middle; differing codes mean somebody is.
The engine implements this fully, including re-warning if a peer reconnects with different key material, and an SSH-style alarm when a previously verified name reappears with a different key.
It is not reachable in Witbitz Call. The brand sets VITE_BRAND_HIDE_PRIVACY_CHROME=1, which suppresses the
shield button that opens Verify (kibitz/src/widget/SecondaryControls.tsx). The call is still end-to-end
encrypted — the flag hides UI, it does not change cryptography — but the affordance that would let you check
that claim is currently switched off.
That is the honest position: on this path you are trusting the signalling server, where upstream Kibitz would let you verify it. If the call matters to you at that level, use kibitz.chat, where the shield is present. Exposing it here is a UI decision, not a protocol one, and it is the single change that would make this page's §1 claim checkable rather than asserted.
5. Offline — a room on one Wi-Fi, with no internet
For a plane, a cabin, a venue with dead cell: one Android device runs a LAN hub, everyone else opens the app on the same Wi-Fi, and the room works with no internet at all. The download and setup are at witbitz.chat/call/host.
Like the internet broker, the hub is a coordination point — it sees who is in the room and carries the handshakes. Content stays end-to-end encrypted: browser-to-browser when they can connect directly, or relayed through the hub's own LAN TURN as ciphertext where they cannot (on phone Wi-Fi, iOS hides each device behind an mDNS name the other cannot resolve). The hub never reads it, and it is a box you run on your own network.
6. What this protects — and what it does not
Protected: the content of the call, from the network and from Witbitz. It cannot be recorded server-side because the media never reaches a server in readable form.
Not protected, and not hidden:
- That a call happened, its timing, duration and traffic volume — visible to the relay, which is now every call (§2).
- Your IP address from Witbitz. Hidden from other participants by the relay; visible to the relay itself.
- The room is open by link unless gated. By default anyone with the link can join. A knock-to-admit lobby, and verified/gated rooms (Google identity, mailed code, signed invites, a name list) are set at creation — see the verified room, which is the same credential model the async Space uses.
- The signalling channel (§3), until the safety code is exposed (§4).
- An agent's model backend. When an AI agent joins, what it perceives leaves the encrypted room to reach a model — that is what an agent is for. It is disclosed to everyone before they join.
7. The agent in the call
An agent joins as a participant, and the capability layer governs it: every peer carries a grant of what it
may perceive (media, chat, roster, directed data) and act. Humans default to full, agents to read-only.
It is engine-enforced per peer — a withheld peer never receives the bytes — so a read-only agent gets chat but
no audio or screen share and can post nothing until granted. The same Grant vocabulary crosses to the async
Space unchanged (the verified room §7).
8. How this differs from a Space — do not conflate them
| Live call | Async Space | |
|---|---|---|
| Guarantee | E2EE between browsers; no server decodes | content-blind at rest; one decrypt-once render |
| Who could read it | nobody in the path — but see §3, §4 | the render, transiently, while a member is present |
| Verified by | a safety code — not currently exposed (§4) | four checks you run in a terminal (verify) |
| Persistence | ephemeral; the room vanishes when everyone leaves | a sealed ledger that persists |
| The moat claim | standard WebRTC, done carefully | the double blind |
The platform's verifiable-privacy argument is about the Space. The call is a well-built E2EE call — a different and more conventional guarantee. Saying so plainly is the point of this page.
9. Where the code is
| Concern | Where |
|---|---|
Media, relay policy (FORCE_RELAY_DEFAULT) |
kibitz/src/core/callMedia.ts |
| Safety code / verify UI (hidden by the brand flag) | kibitz/src/widget/SecondaryControls.tsx |
| Capability grants | kibitz/src/core/capabilities.ts |
| TURN credentials (ours, same-origin) | witbitz infra/turn.tf → /api/turn |
| The engine, open source (Apache-2.0) | github.com/kibitz-chat/kibitz |
Machine-readable source: the-live-call.md · every doc in one fetch: llms-full.txt · ← the-double-blind · delegated-authority →