End-to-End Encryption
All communication between the mobile/web app and your desktop backend is end-to-end encrypted. The Cloudflare relay only forwards opaque encrypted blobs — it cannot read, log, or tamper with your messages.
How It Works
Section titled “How It Works”Mobile App Relay (Cloudflare) Desktop Backend───────── ────────────────── ───────────────Generate X25519 keypair Forwards encrypted blobs Generate X25519 keypair │ │ ├──── e2e_get_bundle ──────────────────────────────────────────►│ │◄─── server_public_key ───────────────────────────────────────┤ │ │ │ Compute shared secret (ECDH + HSalsa20) │ │ │ ├──── e2e_register(device_id, public_key) ────────────────────►│ │ │ Compute same shared secret │ │ │ │ ├════ Encrypted messages ══════╪═══════════════════════════════►│ │◄═══ Encrypted responses ═════╪═══════════════════════════════┤ │ Only sees: {c, n, deviceId} Cannot decrypt anythingCryptographic Details
Section titled “Cryptographic Details”| Component | Algorithm | Implementation |
|---|---|---|
| Key Exchange | X25519 (Curve25519 ECDH) | x25519_dalek (Rust), tweetnacl (JS) |
| Key Derivation | HSalsa20 | Matches NaCl box.before() |
| Encryption | XSalsa20-Poly1305 | Authenticated encryption with 24-byte nonce |
| Nonce | Random 24 bytes per message | OsRng (Rust), nacl.randomBytes (JS) |
| Key Storage | Secure enclave / SecureStore | expo-secure-store on mobile |
Encrypted Payload Format
Section titled “Encrypted Payload Format”Every message over the relay is a JSON envelope:
{ "c": "base64-encoded-ciphertext", "n": "base64-encoded-nonce", "deviceId": "device-uuid"}The plaintext (your actual message) is JSON-serialized, encrypted with XSalsa20-Poly1305, and base64-encoded. The relay forwards this blob without any ability to inspect it.
Key Management
Section titled “Key Management”Mobile/Web App
Section titled “Mobile/Web App”- Keypair generated on first launch and stored in SecureStore (iOS Keychain / Android Keystore)
- Device ID persisted across sessions
- Keys survive app updates but are regenerated on reinstall
Desktop Backend
Section titled “Desktop Backend”- Keypair generated at startup
- Server public key shared during handshake via
e2e_get_bundle - Per-device sessions tracked for multi-device support
Handshake Flow
Section titled “Handshake Flow”- Mobile app calls
e2e_get_bundleto get the server’s X25519 public key - App generates (or loads) its own X25519 keypair
- Both sides compute the shared secret:
ECDH(my_secret, their_public)→ HSalsa20 key derivation - App registers its public key and device ID via
e2e_register - All subsequent messages are encrypted with the shared XSalsa20-Poly1305 key
Security Properties
Section titled “Security Properties”- Confidentiality — XSalsa20-Poly1305 with unique random nonces per message
- Integrity — Poly1305 MAC detects any tampering
- Forward secrecy — Server generates fresh keys on restart; compromising old keys doesn’t expose future sessions
- Zero-knowledge relay — The Cloudflare Worker relay is cryptographically excluded from reading any message content