Send an encrypted message
Decrypt a received message
The CLI automatically decrypts encrypted messages when receiving:CLI
content_type and call decrypt():
How it works
HPKE Auth mode (v2)
Each encrypted message uses HPKE (RFC 9180) in Auth mode, which cryptographically binds the sender’s identity into the encryption. The ciphersuite is:- KEM: DHKEM(X25519, HKDF-SHA256)
- KDF: HKDF-SHA256
- AEAD: ChaCha20-Poly1305
1
Key conversion
Convert both the sender’s and recipient’s Ed25519 keys to X25519 (Edwards-to-Montgomery conversion).
2
HPKE Auth seal
The sender creates an HPKE Auth context with the recipient’s public key and the sender’s private key. This produces an encapsulated key (
enc) and a sealer that encrypts the plaintext with ChaCha20-Poly1305.3
Sender authentication
The sender’s static X25519 key is bound into the HPKE key schedule — the recipient can verify the message came from the claimed sender.
4
Result
The output is
enc (32-byte encapsulated key) + ct (ciphertext with Poly1305 tag).Encrypted message format
Messages withcontent_type: "application/x-m2m-encrypted" carry this body:
Decryption
The recipient:- Converts its Ed25519 private key and the sender’s Ed25519 public key to X25519.
- Creates an HPKE Auth receiver context with
encand the sender’s public key. - Opens the ciphertext — if the sender’s key doesn’t match, decryption fails.
Encrypted blobs
Blobs can also be encrypted so the relay never sees plaintext file contents:1
Generate a blob encryption key
Random 256-bit key (BEK) and 24-byte nonce.
2
Encrypt the blob
Encrypt raw bytes with XChaCha20-Poly1305 using the BEK.
3
Upload encrypted bytes
Upload to the relay as a normal blob (relay sees only ciphertext).
4
Encrypt the BEK
Encrypt the BEK using the per-message E2E scheme (HPKE Auth mode).
5
Attach to message
Include the encrypted BEK in the attachment metadata.
The recipient unwraps the BEK from
dek_enc/dek_ct using HPKE Auth mode, downloads the blob, then decrypts the blob bytes using the BEK.
Security properties
E2E encryption is optional and opt-in per message. You can mix encrypted and plaintext messages in the same conversation.
Python extra dependency
The Python SDK requires thee2e extra for encryption support:
PyNaCl for key conversion and pyhpke for HPKE encryption. The TypeScript and Go SDKs include encryption support by default.