Sealed sender
Sealed sender hides the plaintext sender identity from the outer delivery envelope so the relay path does not need a durable A → B field in the clear.
Status (public): Sealed sender for 1:1 messages, sealed controls, 1:1 call invites, and conversation-scoped presence is write-cutover complete (allowLegacyFallback = false). Inbox is sealed-only (non-sealed nodes ignored/cleaned). Legacy receipt_batches listeners stay off unless the emergency fallback flag is re-enabled.
Problem
Even with perfect content E2EE, metadata can reveal:
- Who messaged whom
- How often
- When devices were online enough to receive
Classic leaks on Firebase-style paths include plaintext from on pending_chats, FCM sender_id, receipt trees keyed by peer ids, and typing fields that name the peer.
Design (Buzzio sealed sender v1)
Outer envelope
ephemeralPub || AES-GCM( HKDF( ECDH(eph, recipientIK), info="buzzio_sealed_sender_v1" ), payload )
Payload JSON:
{ "v": 1, "cert": { /* SenderCertificate */ }, "msg": { /* inner RTDB message incl. real from + ratchet ciphertext */ } }
ephemeralPub: uncompressed secp256k1 (65 bytes,0x04 || X || Y)- Only the recipient’s identity private key opens the outer layer
- After open, recipient learns
cert+ innermsg, then runs Double Ratchet decrypt
SenderCertificate
Cloud Functions issue certificates roughly shaped as signed { v, gid, ik, exp } using HMAC-SHA256 with a server CA secret.
- Client authenticates with Firebase Auth to request issuance
- Client caches certs (~24 hours TTL)
- Delivery function verifies cert material for rate limits, blocks, and freemium gates
- Clients cannot write
from: "sealed"directly; CF Admin write only
FCM
Wake payload on sealed path:
- Includes
message_idandsealed: "1" - Omits
sender_id
RTDB
Pending nodes use from: "sealed" placeholder. Inner real sender exists only inside the sealed blob.
Delivery API surface
| Callable | Role |
|---|---|
issueSenderCertificate |
Mint short-lived cert for authenticated ghost/Buzzio identity |
deliverSealedMessage |
Verify, rate-limit, write pending sealed node, send FCM wake |
deliverSealedControl |
Same idea for receipts / Secure View / disappear / once-view / kept |
deliverSealedCallInvite |
Sealed 1:1 and conference call invite (no sender_id_hint); FCM/VoIP via RTDB trigger without caller_id |
Honest ceiling (Firebase)
After sealed sender ships, Google/Firebase infrastructure can still know:
- An account exists, connects, and holds an FCM token
- Recipient R received a sealed wake at time T
- Cloud Functions may learn the sender when verifying certificates for abuse controls
This matches Signal’s idea of sealed sender (hide sender from the relay envelope), not “zero server knowledge” or APAN/Tor access privacy.
Why keep cert checks? Server-enforced block, freemium caps, and abuse limits need a verified sender at deliver time. See Why delivery still learns the sender.
Dual-write / dual-read (production)
| Mode | Behavior |
|---|---|
| Send | Sealed only — legacy plaintext-from writes off |
| Controls | Sealed only |
| Call invite (1:1 + conference) | Sealed only — map+hint / binary string ignored unless emergency flag |
| Inbox parse | Sealed only — non-sealed nodes ignored and deleted |
| Receipt listeners | sealed_controls only; receipt_batches listener not attached unless emergency fallback is re-enabled |
| RTDB rules | Clients may delete pending_chats / sealed_controls / receipt_batches only; creates via Admin/CF |
Ship note: Everyone who installs/updates tomorrow runs this cutover build. Peers cannot leave plaintext envelopes in your inbox (client creates denied by rules). Keep allowLegacyFallback as an emergency flag only — flipping it back also re-attaches legacy receipt listeners and requires redeploying rules that currently deny receipt_batches creates.
Cutover date: 2026-08-05.
Related minimization already shipped
Not sealed sender alone, but part of the same metadata program:
- Strip
sender_idfrom 1:1 FCM wakes - No plaintext
reply_to.snippeton RTDB - Opaque typing tokens instead of peer Buzzio ID
- Conversation-scoped online (
presence_conv/{token}/{a|b}) — no globals/hunder Ghost ID for 1:1 - Stop growing server
messaged_contactsgraph (local mutuals) - Whisper: isolated creator mapping; wake-only FCM; tighter
qr_chatsauth rules