← Back to Home
How ENQ Works
A technical overview of the cryptographic architecture. Zero plaintext is stored at rest.
1. Key Generation & Alias Creation
On first visit, your browser generates a 256-bit userKey via crypto.getRandomValues() and stores it in localStorage. This key never leaves your device.
When you create an alias, the browser generates an ECDH P-256 key pair using the Web Crypto API. The private key is then encrypted client-side:
- A 128-bit random salt is generated
- Your
userKey is fed into PBKDF2-SHA256 (310,000 iterations) with the salt to derive a 256-bit AES wrapping key
- The ECDH private key (JWK) is encrypted with AES-256-GCM using a random 96-bit IV
- Only the public key, the encrypted private key blob, the salt, and a SHA-256 hash of your auth token are sent to the server
The server stores ciphertext. It cannot derive your wrapping key without your userKey.
2. Email Ingestion & Server-Side Encryption
When an inbound email arrives at the Cloudflare Email Worker:
- The worker looks up the alias and retrieves the stored ECDH public key
- An ephemeral ECDH P-256 key pair is generated per-message
- A shared secret is derived via
ECDH deriveBits between the ephemeral private key and the recipient's public key
- The shared secret is passed through HKDF-SHA256 with a random 128-bit salt to produce two 256-bit AES keys — one for the email body (info:
enq-ecdh) and one for metadata (info: enq-ecdh-meta)
- The email body is encrypted with AES-256-GCM (random 96-bit IV)
- Subject and sender are encrypted separately with AES-256-GCM under the metadata key
- The ephemeral private key is discarded. Only the ephemeral public key, HKDF salt, ciphertext, and IVs are stored
The plaintext email body and metadata exist only in worker memory during processing. After encryption, they are unreachable.
3. Client-Side Decryption
When you open an email in your browser:
- The encrypted private key blob and salt are fetched from the server
- Your
userKey + salt are run through PBKDF2 to re-derive the AES wrapping key
- The ECDH private key is decrypted locally via AES-256-GCM
- The shared secret is re-derived using your private key + the stored ephemeral public key
- HKDF produces the same AES keys, and the email body and metadata are decrypted with AES-256-GCM
- Decrypted HTML is rendered inside a
sandbox="" iframe with no network access
All cryptographic operations use the browser's native Web Crypto API. No third-party crypto libraries are loaded.
4. Data Retention & Deletion
- Email ciphertext is deleted after 3 days via scheduled cron
- Aliases with no remaining emails are purged after 7 days of inactivity
- Users can manually delete individual messages, aliases, or all data at any time
- Deletion is permanent — D1 rows are removed, not soft-deleted
5. Authentication Model
There are no passwords or accounts. Authentication is derived from your userKey:
HMAC-SHA256(userKey, "enq-auth-v2") produces a base64url auth token sent as a Bearer header
- The server stores
SHA-256(authToken) as auth_hash — a double hash of your key
- All database queries are scoped to your
auth_hash, enforcing row-level isolation
- The server never sees or stores your
userKey
Threat Model — What This Protects Against:
- Database breach — all stored email bodies and metadata are AES-256-GCM ciphertext
- Server-side snooping — the worker processes plaintext only in ephemeral memory
- Long-term data exposure — automatic deletion bounds the window of risk
- Cross-user access — all queries are bound to
auth_hash
- Script integrity — client-side JavaScript is verified via SRI hashing
Known Limitations:
- SMTP transit — emails are plaintext between the sender's MTA and Cloudflare's edge
- Compromised client — if your browser is compromised, the
userKey in localStorage is exposed
- Timestamps — email receive timestamps are stored unencrypted for retention management
- Worker trust — you must trust that the deployed worker code matches what is described here
ENQ is designed for disposable signups and temporary communication — not for state-level adversary resistance.