Zero-access servers: what Enchant's backend can and cannot see
A tour of the server architecture that claims to be cryptographically blind — and an honest map of the exact boundaries where that statement holds and where it is still being tightened.
Every chat product claims privacy. Few products architect it so that the claim is enforceable by math rather than by policy. Enchant's servers are designed to be zero-access: the backend is built so that even its own operators cannot read the content it carries. But "zero-access" is a strong statement, and the honest version of it has contours — things the servers provably cannot see, and a small, carefully bounded set of things they still can. This post draws the map, and it draws it from the LibEnchant code, not from a marketing page.
The three-server topology
Enchant's backend is not one server with many jobs. It is three roles, each with the minimum of information its job requires — and each missing the information its job doesn't:
| Role | Holds | Does not hold |
|---|---|---|
| Directory | Public pre-key bundles, device IDs | Private keys, identities beyond public material |
| Relay | Ciphertext envelopes, delivery metadata | The ability to open a single envelope |
| Gate | Session credentials, rate-limit state | Credential contents, session keys |
The directory is a public bulletin board: it stores pre-key bundles (signed pre-keys, one-time pre-keys, kyber pre-keys — see the X3DH post) that are public. Publishing them is the entire point, and every client verifies the bundle's signature against the sender's identity key on-device before using it. The relay is a dumb post office: envelopes in, envelopes out, opened never. The gate issues session credentials and counts guesses — the enforcement point for the SVR3 rate limits described in the OPRF post. None of the three holds anything that, alone or together, decrypts a message.
The four layers of blindness
Zero-access is not one feature; it is a stack. Every layer removes a different thing the operator could otherwise see.
1. Content — sealed by the session ciphertext. Every message rides inside an XChaCha20-Poly1305 ciphertext under keys that exist only on the participants' devices (the Triple Envelope). A server compromise yields ciphertext, and ciphertext is a string of uniformly random bytes. There is no key escrow, no server-held copy, no "encryption with a backdoor by design." The relay cannot open the envelope even in principle — the mathematics of the handshake simply gives it nothing to work with.
2. Identity — hidden by zero-knowledge credentials. To reach the gate, a client must prove it holds a valid session credential. The proof system (POKSHO, in the zkgroup layer) is built so the gate learns exactly one bit: this client holds a valid credential. Not which credential, not which user, not any attribute. The gate cannot correlate two connections from the same user, because the user is never identified to it. (Full treatment in the OPRF and zero-knowledge post.)
3. Sender — hidden by the Veil. The relay sees envelopes and destination device IDs. It does not see senders. Veil envelopes carry the sender's identity only in encrypted form, recoverable solely by the recipient; per-message randomness ensures the relay cannot even link two envelopes from the same sender. This is the strongest layer and the newest one — the Veil post derives both versions.
4. Backups — hidden by OPRF blinding. The backup server holds the user's encrypted master key and a share of an evaluation key. It cannot test a password guess offline, because offline guessing requires the very thing it is missing; it cannot watch a restore, because the password arrives blinded. Its rate limit is not a promise — it is the mechanism, and the SVR3 construction makes the server's ignorance structural rather than behavioral.
Key transparency: the directory cannot lie
A directory that cannot read messages could still do enormous damage by substituting keys — handing Alice a forged bundle for Bob so that a session lands in an attacker's hands. Classical messaging relies on out-of-band verification of safety numbers to catch this (see fingerprints and safety numbers). But verification after the fact is not prevention. Enchant's directory is built as a transparency log: a tamper-evident, append-only Merkle tree that makes substitution detectable, automatically.
The construction lives in LibEnchant's key-transparency module:
- Append-only Merkle log. Every key change is appended as a leaf
(
compute_leaf_hash,append) into a Merkle tree (TransparencyLog), with the root hash published and signed by the server's Ed25519 key as aSignedTreeHead. The server can no longer rewrite the past: any revision would change the root, and the signature would no longer verify. - Consistency proofs. Anyone can request a proof that the log of
size is a prefix of the log of size — the standard
"you cannot quietly rewind history" check.
get_consistency_proof/verify_consistency_proofare exact Merkle-range proofs: recompute the old root from the new log's path, and compare. - Prefix proofs for keys. A
PrefixTreeprovides proofs of membership and non-membership for individual keys — "my key was present at revision R" or "no key for this user existed at R" — so the directory cannot silently drop or insert a user's key. - VRF-based lookup privacy. Lookups run through a verifiable
random function (
enchant_key_transparency_vrf_prove/enchant_key_transparency_vrf_verify): the server proves it evaluated the lookup honestly, and the output is deterministic yet indistinguishable from random, so a watchful operator cannot correlate which keys are being looked up.
The threat model is thereby inverted: instead of trusting the directory not to substitute keys, clients can now detect any substitution from a signed, append-only, publicly verifiable history — and the safety number comparison remains the final backstop when it matters most.
The honest boundary: what the servers still see
Zero-access has limits, and pretending otherwise would be a disservice to the architecture. The servers still learn:
- Delivery events. The relay must know a message arrived for device X to route it. Veil removes the sender, but the delivery itself and its size are observable. A global traffic analyst can still measure that someone sent something to a given device, at a given time — the "metadata" floor of any network.
- Public-key material. The directory holds public pre-keys by design. Public is the point; they are not secrets.
- Network-level metadata. IP addresses, connection timing, connection frequency, packet sizes — the standard baggage of any internet service. Enchant's server-side logging is deliberately minimal and short-lived, but the network is not something the backend controls.
- Backup attempt counts. The SVR3 servers count failed guesses — the rate limit that makes offline guessing hopeless. The count itself is visible to the operator (this is exactly what makes it an effective limit).
Layer 3 is the one honest caveat: a determined global adversary can still build traffic patterns. What they cannot do — what no amount of traffic analysis reconstructs — is the content, the sender, or the keys. The four blindness layers above are cryptographic, not contractual. A server compromise does not "leak user data"; it leaks a database of sealed envelopes, blind proofs, and public keys. The threat model of each layer holds even under total server control, because each layer's math is written so that control is not enough.
Why this is the actual product
The design philosophy of the whole stack can be stated in one sentence: the server is treated as an adversary with root access, and the protocol is written so that it still cannot win. Every component — the handshake, the Envelope, the Veil, the OPRF, the transparency log — is a direct answer to the question "what happens if the server is malicious?" And each one answers it with equations that anyone can audit in LibEnchant's source, not with an operator's promise. That is the difference between a privacy policy and a privacy architecture: the policy can be revoked with a memo, while the architecture can only be broken by breaking the math.
Continue reading

