Encryption modes
Two deployment modes share one API shape and one envelope on the wire. What changes is who encrypts — the relay (Mode A) or the sender (Mode B).
Mode A: server-assisted (self-hosted default)
- The sender posts plaintext over
HTTPS. - The relay encrypts to the device's public key at receipt.
- The relay reads the contents in memory while delivering; nothing is persisted, and delivery records expire via the janitor.
- Suited to self-hosted deployments on hardware you control.
Mode B: sender-encrypted (zero-knowledge)
- The sender encrypts locally to the device's public key before the request
(
X25519ephemeral sender key +ChaCha20-Poly1305, protocol v2). - The relay validates the envelope's structure and routing fields only:
version,
message_id,device_id,key_version,sender_key,nonce,ciphertext,created_at,ttl,priority. - Prelay Cloud rejects plaintext with
403 zero_knowledge_required; Mode A is a self-hosted default, never a Cloud one. - The private half of the device key lives only in the device's
Android Keystore.
What the relay sees in Mode B
| Field | Mode A (server sees) | Mode B (plain on the wire) |
|---|---|---|
title, message, actions | yes (encrypts them itself) | sealed inside ciphertext |
device_id, routing | yes | yes |
priority | yes | yes — delivery metadata, not content; it only lifts FCM priority |
created_at, ttl | yes | yes (server-side validation inputs, §8) |
key_version, sender_key, nonce | relay-generated | yes |
What the server enforces in Mode B anyway
The zero-knowledge boundary does not turn the relay into a dumb pipe. It still validates everything it can without reading:
message_idmust be a canonical lowercaseUUIDv7and unique — the replay-protection anchor (§8); a duplicate gets409 duplicate_message.created_atmust sit inside the server's clock-skew window (PRELAY_CLOCK_SKEW); attlabove the 604800-second (7-day) maximum is rejected with400.key_versionmismatch gets409 stale_key, so a key rotation sends the sender back to fetch fresh keys.- unknown
version/algorithmvalues get400.
Mode B senders validate their own contents
The server validates Mode A contents; in Mode B it cannot. So the
sender-side rules — message size caps, valid actions (open_url needs an
https URL, labels ≤ 24 chars), notification-counting — move to the sender.
The device trusts its own keypair, the relay trusts nothing: an
out-of-spec Mode B envelope either fails the server's schema checks
unsigned, or opens on the phone looking broken.
Which one?
- Your own hardware:
Mode Ais fine and simpler. - A server you don't fully trust:
Mode B.Prelay Cloudalways speaksMode B.