End-to-End Encryption (v4)
HeySummon uses Signal/Olm-inspired cryptography for true end-to-end encryption. The server never sees plaintext — it only relays encrypted blobs.
Key Generation
Generate two keypairs using the included crypto script:
node scripts/crypto.mjs keygen ~/.hitlaasThis creates 4 files:
| File | Algorithm | Purpose |
|---|---|---|
sign_public.pem | Ed25519 | Verify your message signatures |
sign_private.pem | Ed25519 | Sign outgoing messages |
encrypt_public.pem | X25519 | Diffie-Hellman key exchange |
encrypt_private.pem | X25519 | Derive shared encryption secret |
Keep *_private.pem files secret. Only public keys are shared.
How It Works
1. Submit Request
POST /api/v1/helpSend your Ed25519 sign and X25519 encrypt public keys with the request. The server stores them for the provider.
2. Key Exchange
When a provider picks up your request, they perform a key exchange:
POST /api/v1/key-exchange/:requestIdThe provider sends their public keys. Both sides now have each other’s X25519 public keys.
3. Shared Secret (Diffie-Hellman)
Both consumer and provider independently compute the same shared secret using X25519 Diffie-Hellman:
sharedSecret = X25519(myPrivateKey, theirPublicKey)This secret is never transmitted — both sides derive it locally.
4. Per-Message Encryption
Each message gets a unique AES key derived via HKDF:
messageKey = HKDF(sharedSecret, salt=messageId)Messages are encrypted with AES-256-GCM and signed with Ed25519.
5. Message Format
{
"encrypted": {
"iv": "<base64>",
"ciphertext": "<base64>",
"tag": "<base64>",
"ephemeralPublicKey": "<base64>"
},
"signature": "<base64>",
"messageId": "<uuid>"
}Encrypting a Message
node scripts/crypto.mjs encrypt \
"Your message text" \
/path/to/recipient/encrypt_public.pem \
/path/to/your/sign_private.pem \
/path/to/your/encrypt_private.pem \
[messageId]Outputs a JSON payload ready to POST to /api/v1/message/:requestId.
Decrypting a Message
node scripts/crypto.mjs decrypt \
'<payload JSON>' \
/path/to/sender/encrypt_public.pem \
/path/to/sender/sign_public.pem \
/path/to/your/encrypt_private.pemCrypto Primitives
| Component | Algorithm | Purpose |
|---|---|---|
| Key Exchange | X25519 (Curve25519) | Diffie-Hellman shared secret |
| Signing | Ed25519 | Message authentication & anti-tampering |
| Encryption | AES-256-GCM | Symmetric message encryption |
| Key Derivation | HKDF-SHA256 | Per-message unique keys from shared secret |
Why Not RSA?
v1-v3 used RSA-OAEP + AES-GCM. v4 switched to X25519 DH because:
- Both sides compute the shared secret independently — never transmitted
- Smaller keys (32 bytes vs 2048+ bits)
- Forward secrecy potential with ephemeral keys
- Closer to Signal protocol design
Why Not Full Olm/Megolm?
Olm’s Double Ratchet, one-time prekeys, and multi-device support are overkill for HeySummon’s 72-hour conversation model. We took the best parts:
- ✅ X25519 DH key exchange
- ✅ Ed25519 signatures on every message
- ✅ Per-message key derivation (HKDF)
- ❌ Double Ratchet (unnecessary for short-lived sessions)
- ❌ One-time prekeys (not needed without async messaging)