How Eve works. All of it.
Eve is built so that reading your notes is not something we choose not to do. It is something we are unable to do. This page describes every moving part, so you can check that claim instead of trusting it.
01Identity comes from your wallet
Wallets cannot decrypt data for a website, so Eve derives a separate set of keys from something only your wallet can make: a signature. When you unlock, your wallet signs a fixed message. Standard wallets produce the same signature for the same message every time, which makes it a stable secret.
signature = personal_sign("Eve: unlock your private keys ... Wallet: 0x...")
seed = HKDF-SHA256(signature.r || signature.s, salt "eve/v1/identity", info address)
x25519 key = seed[0..32] encryption
ed25519 key = seed[32..64] signingThe signature and both private keys stay in your browser tab. They are held in memory, cached in session storage until the tab closes, and never transmitted. Open Eve on another device, sign the same message, and the same keys appear. There is nothing to back up and nothing for us to lose.
02Public keys are verified, not trusted
The first time you unlock, your wallet signs a second, public statement that names your two public keys. We store that statement in a directory so senders can find it.
A directory is the classic weak point of encrypted messaging: whoever runs it could hand out their own key in place of yours. Eve closes that hole. Before encrypting, the sender's browser recovers the signer of the statement with ecrecover and requires it to equal the recipient's address. A swapped key fails the check and the note is not sent. The same check runs when a reader verifies who a note is from.
03Encrypting a note
- The browser generates a random 256-bit content key for this note only.
- The text is padded to a multiple of 1,024 bytes, then sealed with
XChaCha20-Poly1305. The note id, sender and recipient are bound in as associated data, so a ciphertext cannot be replayed under another address. - The content key is wrapped for each reader: a fresh ephemeral
X25519key agrees a secret with the reader's public key,HKDFturns it into a wrapping key, and that seals the content key. One wrap for the recipient, one for you so your Sent tab still opens. - The envelope is signed with your
Ed25519key. Readers verify it against your wallet-verified signing key. - Ciphertext, wraps and signature are uploaded. The plaintext and the content key are not.
A short preview is encrypted separately under the same content key so your inbox can show a snippet without downloading every note in full.
04Sealed notes: writing to a wallet that has no keys yet
If the recipient has never used Eve there is no public key to wrap to. Eve still lets you send. The note is encrypted exactly as above, but only your own wrap is created. The note is stored against the recipient's address in a sealed state. The content key then reaches them by one of two routes, and neither goes through us.
- Private link. The link ends in
#key. Browsers never include the fragment in requests, so the key does not reach our servers, logs or CDN. When the recipient opens it they connect, derive and publish their keys, and prove control of the address. Only then do we release the ciphertext. Their browser checks that the key fits, re-wraps it to their own public key and uploads that wrap, so the note lives in their inbox and the link is no longer needed. - Automatic handoff. If no link is shared, the note simply waits. Once the recipient publishes verified keys, the sender's browser notices the next time it has Eve open, unwraps the content key with its own keys, wraps it to the recipient and uploads the result.
Treat a private link like a key. Someone who intercepts it still cannot fetch the ciphertext without the recipient's wallet, but the link should travel over a channel you trust. The handoff route has no such caveat.
05What the server holds
Two tables. This is the complete list of columns.
keys address, x_pub, ed_pub, binding_sig, created_at
notes id, sender, recipient,
nonce, ciphertext, preview_nonce, preview,
sender_wrap, recipient_wrap, sig,
burn, created_at, read_atEverything in bold is ciphertext. Everything else is an address, a public key, a signature, a flag or a timestamp. API requests are authenticated with a signature from your Eve signing key over the method, path, time and body, so there are no passwords or session cookies to steal either. Burning or deleting a note removes its row. Our database host may keep its own short-lived backups, and those hold the same ciphertext and nothing more.
06Honest limits
- Metadata exists. We can see which address wrote to which address, when, and roughly how long the note was. Content is hidden. The social graph is not.
- Your wallet is the root of trust. Anyone who can sign with your wallet can derive your Eve keys. Only sign the unlock message on the real Eve site.
- You run our JavaScript. Like every web app that encrypts in the browser, you trust the code you are served at the moment you load it. Nothing stored on our side can read a note, but a site that shipped malicious code tomorrow could. The client is small and readable for that reason, and it loads no third party scripts.
- A private link is a key. For a sealed note, anyone holding both the link and a copy of our database could open that one note. The link never reaches us, so keep it that way: send it somewhere you trust. Notes delivered by automatic handoff, or to wallets that already had keys, have no such link.
- No recovery. Lose the wallet, lose the notes. There is no reset because there is no one who could perform it.
- Standard accounts only. Smart contract wallets and signers that randomise signatures cannot derive stable keys and are not supported yet.