Security · Intermediate
End-to-End Encrypted Chat
Build a 1-to-1 messenger where keys live only on devices and the server stores nothing but ciphertext it cannot read.
You will build a 1-to-1 chat application where every message is encrypted on the sender's device and decrypted only on the recipient's device, using the X3DH key agreement protocol for session establishment and the Double Ratchet algorithm for forward-secure message encryption. The server relays opaque ciphertext and stores it in SQLite, but holds no keys and cannot read any message content. This project teaches you how modern end-to-end encryption actually works under the hood, the same core design used by Signal and WhatsApp. It is worth building because most developers never implement cryptographic protocols directly and this project demystifies them while producing a functional, auditable system.
What you build
- X3DH (Extended Triple Diffie-Hellman) key exchange to establish a shared session secret between two users who may be offline at different times
- Double Ratchet algorithm providing forward secrecy and break-in recovery so compromising one message key cannot decrypt past or future messages
- WebSocket-based real-time delivery of encrypted message payloads via a thin relay server
- Per-device key generation using libsodium or the browser WebCrypto API with keys persisted only in local storage or on-device SQLite
- Prekey bundle upload so a sender can initiate a session even when the recipient is offline
- A minimal chat UI showing sent and received messages in plaintext after local decryption, with a visible "sealed" indicator for undelivered messages
What it teaches
- X3DH key agreement protocol including the role of identity keys, signed prekeys, and one-time prekeys in establishing a shared secret with offline recipients
- Double Ratchet algorithm mechanics including the symmetric-key ratchet, Diffie-Hellman ratchet, and how they together provide forward secrecy and future secrecy
- Practical use of libsodium or WebCrypto for asymmetric key generation, signing, and authenticated encryption (crypto_box / AEAD)
- Designing a server that is architecturally incapable of reading user content, also called a zero-knowledge relay
- Safe local key storage patterns using IndexedDB or a device-local SQLite file to avoid key leakage
- WebSocket message framing and how to carry protocol metadata such as ephemeral public keys and ratchet headers alongside ciphertext
How it works
- 1
Key Setup
- Sender and recipient each generate identity, signed prekey, and one-time prekeys
- Public prekey bundles are uploaded to the server
- 2
X3DH Handshake
- Sender fetches recipient prekey bundle
- Four DH operations produce a shared master secret
- Initial encrypted message carries sender ephemeral public key
- 3
Double Ratchet
- Both sides derive per-message keys from ratchet state
- Each message advances the ratchet, old keys are deleted
- 4
Relay
- Server receives opaque ciphertext envelope
- Routes by recipient ID and stores in SQLite, cannot decrypt
- 5
Decryption on Device
- Recipient receives envelope over WebSocket
- Local ratchet state decrypts plaintext, key discarded after use
Sign in to open the build guide
Free account. Get the step-by-step build and every resource link.
Take it further
- Add multi-device support using a key distribution protocol so a user's second device can decrypt messages sent to their identity after scanning a QR code to transfer the ratchet state
- Implement sealed sender so the relay server cannot infer who is messaging whom, using a mechanism similar to Signal's sealed sender envelope format
- Add disappearing messages with a client-enforced TTL that purges local plaintext and instructs the remote client to delete its copy after a configurable duration


