End-to-End Encrypted Messaging Application
A zero-knowledge messaging platform implementing the Signal Protocol for end-to-end encryption, with real-time delivery via Socket.IO and Redis, and cross-platform mobile support through Capacitor.
Overview
A privacy-first messaging application where the server has zero knowledge of message content. Messages are encrypted on the sender’s device using the Signal Protocol and can only be decrypted by the intended recipient. The server relays opaque ciphertext and manages key distribution — it never has access to plaintext.
The application runs as a responsive web app with a WhatsApp-style interface, and is Capacitor-ready for native iOS and Android deployment with access to platform-level secure storage.
Signal Protocol Implementation
The encryption layer uses @privacyresearch/libsignal-protocol-typescript, implementing the full Signal Protocol stack:
- X3DH key agreement — Extended Triple Diffie-Hellman for asynchronous session establishment, allowing messages to be sent to offline recipients using pre-key bundles
- Double Ratchet Algorithm — forward secrecy and break-in recovery with every message exchange, meaning a compromised key cannot decrypt past or future messages
- Pre-key management — clients generate and upload batches of one-time pre-keys to the server; senders consume these to initiate new sessions without the recipient being online
- TOFU identity verification — Trust On First Use with automatic detection and security alerts when a contact’s identity key changes, flagging potential man-in-the-middle attacks
Key material is generated during registration and stored exclusively on the client device. On native platforms, Capacitor’s secure storage writes keys to the iOS Keychain or Android KeyStore. On web, keys are encrypted with AES-GCM before storage in IndexedDB.
Encrypted Backup and Recovery
Users can create encrypted backups of their private keys and message history, protected with AES-256-GCM derived from a user-chosen password via PBKDF2. Backups can be restored on a new device, re-establishing all encryption sessions without the server ever handling unencrypted key material.
A key recovery modal guides users through the restoration process if they lose access to their device.
Real-Time Message Relay
Socket.IO handles real-time transport with JWT authentication on the WebSocket handshake:
- Message delivery — encrypted payloads are pushed immediately to connected recipients
- Offline queuing — messages for disconnected users are queued in Redis with a thirty-day TTL, delivered when the recipient reconnects
- Presence tracking — online/offline status tracked in Redis sets, broadcast to contacts on state change
- Rate limiting — sixty messages per minute and ten connections per minute per user, preventing abuse
- Horizontal scaling — Redis pub/sub adapter allows multiple Socket.IO server instances to share connections, with messages routed to the correct node regardless of which server a client is attached to
Backend Architecture
The Express server handles four concerns: authentication, message relay, key distribution, and contact management.
- Authentication — Argon2id password hashing with JWT access tokens and refresh token rotation stored in PostgreSQL
- Key distribution — pre-key bundle endpoints for session establishment, with the server storing only public keys
- Message relay — encrypted payloads stored in PostgreSQL and forwarded via Socket.IO
- Database — PostgreSQL with connection pooling, storing users, pre-keys, and session metadata (never plaintext messages)
Contact Exchange
New contacts are added through two methods, both designed to verify identity keys and prevent interception:
- QR code scanning — the app generates a QR code containing the user’s username and public identity key; scanning with the device camera verifies the key matches the server’s record, preventing man-in-the-middle substitution
- Contact codes — a manual alternative in the format
@username#FINGERPRINT(last eight characters of the identity key), allowing verification when QR scanning is not practical
There is no user directory or search functionality — this is a deliberate privacy decision. Users must exchange contact information directly.
Frontend
The Vue 3 frontend uses the Composition API throughout, with Pinia stores managing conversation state, encryption sessions, and connection lifecycle:
- Chat view — responsive layout with a conversation list sidebar and message panel, sliding to a single-column view on mobile
- Message rendering — decrypted messages displayed with timestamps and encryption status indicators
- Settings — backup creation and restoration, account information, and security warnings for key changes
The interface follows a WhatsApp-style pattern: conversation list with last-message previews on the left, active conversation on the right, with smooth transitions between views on mobile devices.