A federated, end-to-end encrypted mail protocol.
Architecture Specification, Draft v0.3
Status: working draft for discussion. Wire formats are sketched, not final. This document is the starting point for a reference implementation and a more formal protocol specification. Review is wanted on every section, and most of all on routing (Section 7) and admission (Section 10).
Draft v0.2 was never published. Its main flaws are listed here so the reasoning behind v0.3 is visible.
did:key values. Identity keys can now rotate without
changing the address.DEFMS stands for Decentralized Encrypted Free Mail System. Each word is a design claim, not decoration.
Decentralized. There is no central authority, no privileged operator class, and no party whose participation is required for two users to exchange mail. The network is a federation of independent always-on relays. Any user can self-host every role, switch operators without losing their identity, or run the full stack on their own hardware.
Encrypted. Message contents and substantive metadata (subject, threading, attachment names, sender identity at the envelope layer) are end-to-end encrypted by default. Infrastructure operators cannot read mail. Forward secrecy and post-compromise security are provided at the protocol level, not bolted on.
Free. As in freedom, not as in beer. The specification is open and unencumbered. Reference implementations are released under free software licences. Users keep full control over their identity, their data, and their choice of operators. No role in the system is reserved to a paid or proprietary participant. This does not mean the system is free of infrastructure cost: someone always runs the relay, even if that someone is the user on their own hardware.
Mail. The addressable, durable, threaded, asynchronous person-to-person correspondence model that SMTP popularized. Not chat, not pub/sub, not generic messaging.
System. Protocol, reference network, and the implementations on top, taken together. DEFMS is not a service offered by a single provider. It is a specification and the federated network of independent operators implementing it.
Email scaled from a handful of inter-laboratory experiments to a global directory of billions of addresses, but its architecture did not keep pace with modern concerns about privacy, integrity, and operator consolidation. Most mail today flows through a small number of providers who hold message contents in plaintext, can read or analyze them at will, and are both attractive surveillance targets and points of centralized failure. End-to-end encryption for email exists (PGP, S/MIME) but never reached meaningful deployment outside specialized communities, mainly because of poor key-management experience, lack of forward secrecy, and absence of integration with mainstream clients.
DEFMS is a proposed mail system that preserves the user-facing semantics of email, meaning addressable, durable, threaded, asynchronous person-to-person correspondence, while replacing its trust model. Message contents are end-to-end encrypted by default using a modern ratchet protocol that provides forward and post-compromise security. Mail at rest is durable and survives device loss. Routing is federated across independent operators, none of whom can read message contents, and no single operator sees both ends of a conversation. Backward compatibility with SMTP is provided through explicit gateway nodes, with the trust boundary always visible to the user.
This document describes the architecture in enough detail to begin a reference implementation. Wire-format specifics are sketched but not finalized. Expect them to evolve through implementation.
DEFMS protects against the following adversaries to the degrees noted.
| Adversary | Reads content | Forges | Learns sender | Learns recipient | Learns timing |
|---|---|---|---|---|---|
| Passive network observer | No | No | Submitter IP only | Relay host only | Yes, coarsened |
| Active network attacker | No | No | Submitter IP only | Relay host only | Yes |
| Recipient's relay | No | No | Submitter IP, or proxy | Yes | Yes |
| Submission proxy | No | No | Yes | Destination relay only | Yes |
| Transparency log operator | No | Detectable | No | No | No |
| Compromised endpoint | Yes, that user | Yes, that user | Yes | Yes | Yes |
Passive network observer. Cannot read message contents. Sees that some client IP contacted some relay host, and the size class and timing of the exchange. Cannot determine the sender identity, because the sender's identity is inside the sealed payload. Cannot determine the recipient identity, because the relay envelope hides it and only the relay host is visible.
Active network attacker. Cannot forge messages: every message is authenticated inside a session bound to both parties' logged keys. Cannot decrypt. Can deny service to specific relays, which is mitigated by recipients listing several relays.
Recipient's relay. Sees the recipient identity and device (needed to queue), the submitter's IP address or that of a proxy, envelope timing, and size class. Does not see the sender identity, message content, subject, threading, or attachment names. Cannot impersonate the recipient. Cannot substitute keys, because clients accept only keys present in the transparency log.
Submission proxy. The sender's own relay can act as a proxy, as can Tor or a mixnet. A proxy learns who the sender is (it authenticates them) and which relay host the envelope goes to. It does not learn the recipient identity, because the relay envelope is encrypted to the recipient's relay. It does not learn content.
Transparency log operator. Any new key binding must appear in the append-only log, and tree heads are cosigned by independent witnesses. Clients refuse keys absent from the log. A log that presents different views to different clients is detected through witness cosignatures and gossip. A log cannot decrypt anything.
Compromised endpoint. Defeats the system for that user. The blast radius is bounded by per-device subkeys (compromising one device does not yield the identity key), by forward secrecy (past sessions on other devices are not exposed), and by the recovery key (an attacker holding the identity key cannot permanently seize the identity; see Section 5.2).
What no single party learns. The pair (sender identity, recipient identity) is never visible to any infrastructure party. The recipient's relay knows the recipient and an IP address. A proxy knows the sender and a relay host. Correlating them requires collusion between the proxy and the relay, plus timing analysis. This is the property v0.2 lacked.
Coercion. Out of scope at the protocol level. Deletable messages, deniable authentication, and unlinked pseudonymous identities are the only protocol-level provisions.
+-----------------------+
| Transparency Log | keys, devices, relays,
| (with witnesses) | names, admission keys
+-----------+-----------+
^ verify
+-------------------+-------------------+
| |
+----------+----------+ +----------+----------+
| Sender Client | | Recipient Client |
| 1. encrypt, seal | | 5. fetch, decrypt |
| 2. upload blob | | store under MK |
+----------+----------+ +----------+----------+
| ^
| 3. relay envelope |
v |
+----------+----------+ |
| Submission Proxy | optional: own relay, |
| (sees relay host) | Tor, mixnet |
+----------+----------+ |
| |
v |
+----------+---------------------------------------+----------+
| Recipient Relay |
| 4. verify admission, queue per device, hold blobs, |
| publish prekeys, push on connect |
+-------------------------------------------------------------+
Client. Runs on a user's device. Holds the device subkeys in secure storage (secure element, TPM, OS keystore). Encrypts outbound mail, decrypts inbound, manages ratchet state, keeps the mailbox encrypted at rest under the mailbox key, and presents the user interface. Speaks the DEFMS client protocol to relays: its own relays for receiving, the recipient's relays for sending.
Relay. Always-online service that serves a set of identities. For each served identity it queues inbound envelopes per device, holds encrypted blobs, publishes prekey bundles, verifies admission proofs, and pushes queued envelopes when devices connect. It has no special trust. It can be self-hosted, run by a co-op, rented commercially, or operated by an institution. Relays do not need to talk to each other.
Submission proxy. Optional. Anything that forwards an opaque relay envelope to a relay host: the sender's own relay, a Tor circuit, a mixnet. Its purpose is to hide the sender's IP address from the recipient's relay.
Transparency log. Append-only, publicly auditable record of identity state: control keys, device certificates, relay sets, name links, and admission keys. Modelled on Certificate Transparency and Sigsum. Clients refuse to use anything not in the log. Several independent logs may operate. Tree heads are cosigned by witnesses so a log cannot equivocate silently.
Gateway. Bridges DEFMS and SMTP. Inbound, it accepts SMTP for a user's legacy address, encrypts to the user's DEFMS devices, and submits like any correspondent. Outbound, with explicit per-message consent, it delivers a decrypted message over SMTP. Pure DEFMS traffic never touches a gateway.
A DEFMS identity is created by a genesis entry in a transparency log. The identifier is derived from that entry:
did:defms:<base32-lower, unpadded, of the first 20 bytes of SHA-256(genesis entry)>
The identifier is stable for the identity's lifetime. Every key in
the identity, including the identity key, can be rotated through later
log entries without changing the identifier. This is the same
construction as did:plc in the AT Protocol, applied to a
federation of logs rather than a single directory.
The genesis entry carries the initial identity key, the recovery key, at least one device certificate, the initial relay set, the admission key, and optionally a human-readable name link.
Two Ed25519 keys control an identity.
Identity key. Signs device certificates, device revocations, relay set changes, name links, admission key changes, and rotations of itself. It is used for nothing else: not for message signing, not for encryption, not for session establishment. It should live in a secure element on one primary device or in a hardware token.
Recovery key. Signs only rotation entries that replace the identity key, the recovery key, or both. It is never present on a daily-use device. It is held offline or as social-recovery shares (Section 13).
Precedence. A rotation entry signed by the recovery key overrides any entry signed by the identity key within the preceding 72 hours. Clients compute the current identity state by applying this rule, so an attacker who steals the identity key and adds a device is undone when the owner rotates with the recovery key. Entries overridden this way remain in the log as evidence.
Each device generates:
The identity key signs a device certificate binding these keys to the identity, with an expiry and a capability set (send, receive, manage devices). Day-to-day operations use the device keys only.
Adding a device requires the identity key, presented on an already-authorized device with the manage capability, to sign the certificate and append it to the log. Revoking a device appends a revocation entry. Other users' clients refresh device sets periodically and on any verification failure.
Each identity's entries form a hash chain: every entry names the hash of the identity's previous entry. Ordering within an identity is therefore intrinsic and does not depend on which log holds the entry. Entry types:
identity_create: the genesis entry.subkey_add, subkey_revoke: device
certificate lifecycle. Signed by the identity key.identity_rotate: replaces the identity key, the
recovery key, or both. Signed by the identity key or the recovery
key.relay_set: the ordered list of relays serving the
identity, each with its endpoint, its public relay key, and its blob
endpoint. Signed by the identity key.admission_key: the public key used to verify stamps
(Section 10), and the proof-of-work difficulty the identity asks for.
Signed by the identity key.identity_link: binds a human-readable name to the
identity. Signed by the identity key.Each log is an append-only Merkle tree. The log operator signs tree heads. Independent witnesses cosign tree heads after verifying consistency with the previous head they saw, as in Sigsum. Clients accept a tree head only with cosignatures from a configured quorum of witnesses.
Clients verify, for any counterparty: that every entry they rely on has an inclusion proof against a cosigned tree head, that the per-identity chain is unbroken, and that no two entries name the same previous hash. A fork in an identity's chain is treated as compromise: the client applies the recovery precedence rule, refuses to send until the state stabilizes, and alerts the user.
Several logs may operate. An entry counts as published once it is included in a client-configured number of them (default two). Clients submit new entries to several logs and resolve state from the union of entries. Because ordering comes from the per-identity chain, the same entry held by different logs is not a conflict.
Every client periodically audits its own identity's entries in the logs it uses, to detect additions it did not make.
There are two naming layers.
Cryptographic name. The did:defms:
identifier. Globally unique, stable, unreadable. This is the source of
truth for routing and verification.
Human-readable name. Optional.
alice@example.org, bound in both directions:
identity_link entry naming
alice@example.org.https://example.org/.well-known/defms/alice, which returns
the identifier.Both must agree. The log entry is the identity's claim, the well-known document is the domain's confirmation. Neither DNSSEC nor domain-owner keys are required. A domain compromise can point a name at a different identity, and the client detects the change on any later contact because it pins the identifier it first resolved (trust on first use) and displays the name only as a label.
A bare nickname with no domain is self-asserted, has no discovery, and is exchanged directly (QR code, business card, an existing conversation).
To send to alice@example.org:
https://example.org/.well-known/defms/alice to
obtain the identifier.When sending to an identifier directly, step 1 is skipped.
If Alice has never received from this sender, the client also attaches an admission proof (Section 10). The relay enforces its part, and Alice's client applies her policy before showing the message.
Identity state, prekeys, and relay endpoints are cached locally with short lifetimes (one hour by default). The client refreshes on any verification failure. Stale caches degrade gracefully: the sender retries with fresh state.
The sender's client does all of the following itself:
The sender's own relay is not on the path. In v0.2 it was, and as the party that authenticated the sender and read the recipient identifier for routing, it saw the whole social graph of its users. Sealed sender protects only against the recipient's relay, so moving the sender's relay out of the path is what makes the metadata claims in Section 3 true.
The outermost object, visible to proxies and the network:
RelayEnvelope {
version: u8
relay_key_id: bytes(8) // which of the relay's published keys
hpke_enc: bytes(32) // HPKE encapsulated key
ciphertext: bytes // HPKE-sealed SealedEnvelope
}
HPKE (RFC 9180) base mode with DHKEM(X25519, HKDF-SHA256) and
ChaCha20-Poly1305. Relay keys are published in the identity's
relay_set entry, so a sender learns them from the log. A
proxy sees only the destination host and the ciphertext length.
Visible to the recipient's relay after it opens the relay envelope:
SealedEnvelope {
version: u8
recipient_did: bytes(20)
device_id: bytes(8) // which of the recipient's devices
envelope_id: bytes(16) // random; deduplication
size_class: u8 // 0 = 32 KiB, 1 = 256 KiB
deadline: u64 // unix ms; queue eviction
admission: union { Stamp, PoW, None }
sealed_payload: bytes // HPKE-sealed SealedPayload, to the device
padding: bytes // to the size-class boundary
}
The relay verifies the admission proof, deduplicates by envelope id, and queues the envelope for the named device. It learns the recipient, the device, the size class, the timing, and the submitter's address. It learns nothing else.
The sealed payload is HPKE-sealed to the device's X25519 key. This layer hides the sender's identity from the relay before any ratchet processing happens. It is not post-quantum; the ratchet ciphertext inside is, through PQXDH. Post-quantum sealing is an open question.
Visible only to the recipient device:
SealedPayload {
version: u8
sender_did: bytes(20)
sender_device: DeviceCert
timestamp: u64
thread_id: bytes(16)
admission_detail: union { NamedToken, Introduction, Attestation, None }
body: union {
Inline { ratchet_header: bytes, ciphertext: bytes },
External { ratchet_header: bytes, ciphertext: bytes, refs: list<StorageRef> }
}
signature: optional bytes(64) // Section 9.6
}
The recipient verifies the sender's device certificate against the log, processes the ratchet header, decrypts the body, and applies admission policy.
Sealed envelopes are padded to one of two sizes: 32 KiB or 256 KiB. Most personal mail fits the small class inline. Anything larger goes to external storage, and the envelope carrying the reference is small. A relay cannot distinguish a one-line message from a 30 KiB message, nor a short message from one that references a blob. Blob uploads themselves are visible to the relay in size buckets (Section 8).
Relays add a random delay, uniform between zero and thirty seconds by default, before pushing an envelope to a device. This breaks the tightest correlations between submission and delivery. It does not defeat an observer who watches both the submitter and the recipient over time. Users who need that protection use the mixnet mode (Section 12). The delay is configurable per user.
An identity's relay_set is ordered. Senders try relays
in order and stop at the first that returns a submission receipt. Relays
do not gossip. A recipient with several relays fetches from all of them
and deduplicates by envelope id. Redundancy comes from senders retrying,
not from relays copying envelopes to each other.
On accepting an envelope, the relay returns a signed receipt:
SubmissionReceipt {
envelope_id: bytes(16)
received_at: u64
relay_key_id: bytes(8)
signature: bytes(64) // relay key
}
The sender keeps the receipt until the message's deadline. It gives the sender proof that a relay accepted the message and a basis for retrying elsewhere if none did.
Delivery and read receipts are end-to-end control messages, optional, and off by default for cold contact.
If a device cannot decrypt an envelope because its ratchet state has
fallen too far behind (Section 9.5), it sends a
ResendRequest control message naming the envelope id. The
sender's client re-encrypts from its own sent copy. Mail is never
silently dropped.
Transport is HTTPS, with HTTP/3 preferred. There is no relay-to-relay protocol.
Unauthenticated operations, available to anyone: fetch a prekey bundle, submit an envelope, upload a blob (both with admission proofs).
Authenticated operations, for devices of served identities: fetch queued envelopes, acknowledge and delete, fetch blobs, publish prekeys, replenish prekeys, read and write the own-device sync queue (Section 13). Authentication is a challenge signed by the device signing key. The relay checks the device certificate against the log.
Push is a long-lived HTTP/3 stream or WebSocket. Mobile background delivery is an open question.
Messages whose encrypted size fits a size class travel inline. This is the common case and avoids the extra round trip and metadata of an external fetch.
Larger messages and attachments are encrypted under a fresh symmetric key, padded to a power-of-two bucket of at least 256 KiB, and uploaded to the recipient's blob endpoint before the envelope is submitted. The envelope carries the reference:
StorageRef {
endpoint: string // the recipient's blob endpoint, from relay_set
blob_id: bytes(32) // SHA-256 of the ciphertext
fetch_cap: bytes(16) // returned by the store on upload; required to fetch
key: bytes(32)
nonce: bytes(24)
length: u64 // plaintext length, for progress display
digest: bytes(32) // plaintext SHA-256
}
The reference travels only inside the sealed payload. The store never learns the key and cannot tie a blob to a sender.
By default the blob endpoint is the recipient's relay. Uploading requires the same admission proof as submitting an envelope, so storage cannot be filled anonymously. The store holds a blob until every device of the recipient has fetched it or its deadline passes, whichever comes first.
Once fetched, the recipient's client owns the attachment: it stores the plaintext in the mailbox under the mailbox key (Section 9.7) and may keep the ciphertext for own-device sync. Long-term persistence is the recipient's responsibility, which mirrors how email works today.
An identity may name any store in its relay_set as its
blob endpoint, as long as the store implements the upload,
fetch-with-capability, and expiry operations. Content-addressed networks
such as IPFS can play this role, with one caveat: an IPFS node
advertises every content identifier it holds to the DHT, so a blob's
existence and size become public. Confidentiality survives because the
blob is encrypted, but the metadata claim in Section 8.4 does not. For
that reason IPFS is an option, not the reference.
The recipient's blob store learns the size bucket of each blob, the time of upload and of each fetch, and which recipient device fetched it. This is the same class of metadata the relay already sees. It does not learn the sender or the content.
When a sender first contacts a recipient device, or after either side rotates, a PQXDH handshake establishes a shared root key. PQXDH is Signal's post-quantum extension of X3DH. Each recipient device publishes, through its relays, a prekey bundle:
Every prekey is signed by the device signing key. The sender combines them with its own device keys and an ephemeral key per the PQXDH specification. The resulting root key depends on both parties' long-term keys, so the session is mutually authenticated without any signature on the message.
If a device's one-time prekeys are exhausted, senders fall back to the signed prekey and the last-resort KEM key. Forward secrecy of the first message is then weaker until the next ratchet step, which is the same trade-off Signal makes. Relays report remaining prekey counts so clients replenish early.
Messages inside a session use the Double Ratchet, as specified by Signal: a Diffie-Hellman ratchet for post-compromise security and a symmetric-key ratchet for per-message keys and forward secrecy. Signal's triple ratchet (SPQR) adds a post-quantum ratchet step and is the obvious candidate for a later draft.
A sender with one device and a recipient with N devices has N sessions. A message is encrypted N times and sent as N sealed envelopes. The sender's other devices learn about sent mail through own-device sync (Section 13), not through the ratchet. N is small in practice, typically one to four.
The ratchet tolerates reordering and gaps by retaining skipped message keys for a bounded window, one thousand messages per session by default. Beyond the window, the device requests a resend (Section 7.8) instead of dropping the message.
By default a message carries no signature. Its authenticity comes from the session: the root key was derived from both parties' logged keys, and the AEAD tag proves knowledge of it. The recipient knows who sent the message. A third party cannot be shown proof, because the recipient could have produced the same ciphertext. This is the deniability property of Signal, adopted here as the default for mail.
When the sender wants non-repudiation, the client signs the message with the device signing key over the plaintext digest, timestamp, thread id, and recipient identifier, and places the signature in the sealed payload. The recipient's client shows signed messages distinctly. Business correspondence, contracts, and notices are the intended uses.
The ratchet protects mail in transit. It is not a storage format. After decryption, the client stores the message and its attachments in the mailbox, re-encrypted per object under the mailbox key, a per-identity symmetric key. Sent mail is stored the same way from the sender's plaintext.
Ratchet state stays on the device that created it and is never backed up. The mailbox key is backed up and shared with every authorized device (Section 13). This split gives forward secrecy for transport and durability for mail, which v0.2 conflated.
Without trusted intermediaries reading mail, content-based spam filtering is unavailable. The protocol has to make unsolicited bulk mail unattractive while allowing legitimate cold contact: a job offer, a doctor's office, a school, customer service.
Admission is enforced at two layers with different visibility.
The relay enforces cheap checks that must not let it link envelopes from one sender. It sees only what is in the sealed envelope.
The recipient's client enforces policy. It sees the sender and everything in the sealed payload.
Stamp. A single-use credential issued by the recipient:
Stamp {
recipient_did: bytes(20)
epoch: u32 // validity month
nonce: bytes(16)
signature: bytes(64) // recipient's admission key
}
The relay verifies the signature against the admission key in the log, rejects a nonce it has already seen in that epoch, and queues the envelope. Stamps from different correspondents are indistinguishable to the relay: it sees random nonces under one key. The recipient does not need blind issuance to get this property, because the recipient learns the sender anyway when it opens the message.
Stamps are distributed two ways. Inside an existing conversation, the
recipient's client sends batches of fresh stamps as end-to-end control
messages and replenishes them automatically. For first contact, the
address the recipient hands out carries a few stamps:
alice@example.org?s=<stamps>. Whoever holds that
string can reach Alice a few times. Whoever she gave it to can then be
kept or revoked through the named token described below. This is the
capability model: an address is not a public fact but a grant.
The admission key is a per-identity keypair, shared across the
identity's devices through own-device sync. Its compromise lets an
attacker issue stamps to themselves, which is a nuisance and not a
breach, and is fixed by an admission_key entry.
Proof of work. A sender with no stamp attaches a hashcash-style proof over the envelope id, the recipient identifier, and the epoch, at the difficulty the recipient published in the log. The relay verifies it and caps unstamped envelopes per recipient per day, twenty by default, then answers retry-later. Proof of work is a rate limiter, not a spam filter: a botnet makes it cheap. Its job is to keep the cold-contact folder small enough to read.
None. Rejected by default. A recipient may configure the relay to accept unproven envelopes into quarantine under a tighter cap.
Everything below lives inside the sealed payload and is invisible to the relay.
Named token. A per-correspondent identifier the recipient issued alongside the stamps. It tells the recipient which of her handed-out addresses was used, so she can revoke the one a service leaked without touching the others.
Introduction. A current contact of the recipient signs a statement naming the new sender, the recipient, a timestamp, and an expiry. The recipient's client verifies the introducer's device against the log and treats the sender as a near-contact, subject to rate limits and to the introducer's local reputation.
Attestation. The sender's identity carries an
identity_link to a domain the recipient allowlists, such as
her bank or a government domain. The client verifies the link both ways
(Section 5.5). A domain cannot vouch for a sender it has not actually
linked.
Policy. The client sorts by evidence: known correspondent to inbox; introduced or attested to inbox with a label; stamped from an unrecognized named token to inbox with a label naming the address that was used; proof-of-work only to the unknown folder.
The approval prompt for cold contact stays adversarial. A user shown a message from an unknown sender, with no prior context, has little to decide on. The mitigations are that cold contact costs the sender effort, that cold messages are batched in one folder rather than interrupting, and that legitimate cold contact from institutions is expected to arrive through attestation or introduction. Whether that expectation holds requires user study. See Section 17.
A user may hold several identities with no linkage between them: one for cold contact, one for friends, one for work. Nothing in the protocol links them unless the user publishes a link.
Inbound interop, SMTP into DEFMS, preserves the recipient's security properties from the gateway onward, and nothing before it. Outbound interop, DEFMS to SMTP, necessarily downgrades, because the SMTP recipient cannot decrypt.
legacy sender --SMTP--> MX for example.org --> DEFMS gateway
|
encrypt to Alice's devices,
attach gateway stamp + named token
|
v
Alice's relay --> Alice's client
label: "arrived via SMTP"
Alice registers with a gateway the way she registers with any
correspondent: she gives it stamps and a named token. The gateway
accepts SMTP for alice@example.org, encrypts headers and
body to each of Alice's devices, and submits like any sender. Alice's
client shows the message with a clear label: arrived via SMTP, contents
were visible to the gateway operator and to every upstream mail
server.
The gateway holds plaintext only transiently and can run as a stateless transformation. Self-hosting the gateway is recommended for users with their own domain. Co-op and commercial gateways serve users without one.
When Alice writes to bob@legacy-provider.com and no
DEFMS identity resolves for that address, her client presents a
downgrade choice:
The client remembers the choice per recipient.
Gateways are explicit. A DEFMS-to-DEFMS message never traverses a gateway. The trust boundary is always visible.
End-to-end encryption protects content. Metadata (who, when, how much, to whom) is handled as follows.
No party sees both ends. The recipient's relay sees the recipient. A proxy, if any, sees the sender. Nobody sees the pair (Section 3).
Sealed sender. The sender's identity is encrypted to the recipient device and invisible to relays and proxies.
Sender address. A client that submits directly exposes its IP address to the recipient's relay. The client may submit through its own relay, which then knows the sender and the destination relay host but not the recipient. It may submit through Tor, which hides the address from everyone. The default is direct submission, with a per-identity setting to require a proxy.
Padding. Envelopes pad to 32 KiB or 256 KiB. Blobs pad to power-of-two buckets.
Timing. Relays jitter delivery by up to thirty seconds. This is a mild measure and is described honestly as such in Section 7.6.
Multiple relays. A recipient with several relays spreads inbound envelopes across them, so no single relay sees the full timing profile.
Mixnet mode. For strong metadata requirements, an opt-in mode routes relay envelopes through a mixnet (Nym or a Loopix-style network) before the recipient's relay. Latency is seconds to minutes. The mode is per conversation.
Residual exposure. The recipient's relay learns that some envelope for that recipient arrived, its size class, its timing, and the submitter's address or that of a proxy. This cannot be eliminated without giving up offline delivery.
Loss of every device must not lose the identity or the mail. It does lose ratchet sessions, which is intended: sessions re-establish, and messages in flight during the loss are recovered through resend requests.
Two secrets therefore need backup: the recovery key (for the identity) and the mailbox key (for the mail). The identity key does not need backup, because the recovery key can replace it.
The recovery key is split with Shamir's Secret Sharing into N shares with threshold K. Each share is encrypted to a trustee's DEFMS identity and delivered as a normal message. Trustees cannot read shares and cannot collude below K.
To recover, the user contacts K trustees out of band, proves identity
to each by whatever policy the trustee applies (a call, a meeting), and
receives the shares. Reconstruction happens on the new device. The user
then signs an identity_rotate entry with the recovery key,
which installs a new identity key and a new device, and generates a
fresh recovery key to re-share.
The recovery key and the mailbox key are encrypted under a high-entropy passphrase or a hardware token and stored where the user chooses: paper, a USB key, a safe. No third party is involved.
A service holds an encrypted copy of the recovery secrets and releases it after legal-identity verification. This trades sovereignty for convenience. It is supported because some users will choose it regardless. The protocol does not depend on it, and it is discouraged.
The mailbox key is wrapped under an Argon2id-derived key and stored as an opaque blob at the user's relay, or anywhere else. The relay cannot open it. The same wrapped key is handed to each newly authorized device during device add.
Devices of one identity share the mailbox, not the ratchet sessions. The relay provides a per-identity append-only sync queue readable and writable by authenticated devices. Devices append mailbox objects (messages, attachments, flags, folder moves) encrypted under the mailbox key, and each device consumes the queue to converge. Flags use last-writer-wins by timestamp. Message objects are immutable and idempotent by id.
Sent mail is synced this way, so every device shows what the identity sent, even though only one device held the outbound session.
This is a client-level protocol layered on one relay primitive. Its conflict rules, storage growth, and interaction with mixnet mode are open questions. Signal took years to make its equivalent reliable, and that history is a warning about scope.
Recovery restores the identity and the mailbox. It does not restore ratchet state, so an attacker who obtains the recovery secrets learns past mail from the mailbox backup but gains nothing about transport sessions. Users who want past mail to be unrecoverable simply do not back up the mailbox key, at the price of losing mail with their devices.
All structures are CBOR maps with small integer keys, encoded
deterministically. The symbolic names below are for readability.
Signatures cover the deterministic encoding of the structure with the
signature field absent, prefixed with an ASCII domain separator such as
defms/v1/devicecert.
DeviceCert {
identity_did: bytes(20)
device_id: bytes(8) // SHA-256 prefix of device_pub_sign
device_pub_sign: bytes(32) // Ed25519
device_pub_dh: bytes(32) // X25519
device_pub_kem: bytes(1184) // ML-KEM-768
capabilities: u32
issued_at: u64
expires_at: u64
identity_sig: bytes(64)
}
LogEntry {
identity_did: bytes(20)
prev_hash: bytes(32) // zero for identity_create
op: enum { CREATE, SUBKEY_ADD, SUBKEY_REVOKE, ROTATE,
RELAY_SET, ADMISSION_KEY, LINK }
payload: bytes // op-specific CBOR
signatures: list<bytes(64)> // identity key, or recovery key for ROTATE
}
RelaySetPayload {
relays: list<{
endpoint: string
relay_key_id: bytes(8)
relay_pub: bytes(32) // X25519, for HPKE
blob_endpoint: string
}>
}
AdmissionKeyPayload {
admission_pub: bytes(32) // Ed25519
pow_difficulty: u8
}
Log operators wrap entries in their own Merkle tree with signed and witness-cosigned tree heads, as Sigsum does. The tree format is not part of this document.
PrekeyBundle {
device: DeviceCert
signed_prekey: { pub: bytes(32), sig: bytes(64), expires_at: u64 }
last_resort_kem: { pub: bytes(1184), sig: bytes(64) }
one_time_dh: optional { id: u32, pub: bytes(32), sig: bytes(64) }
one_time_kem: optional { id: u32, pub: bytes(1184), sig: bytes(64) }
}
RelayEnvelope, SealedEnvelope,
SealedPayload, StorageRef, and
SubmissionReceipt are defined in Sections 7 and 8.
Stamp is defined in Section 10.3.
PoW {
envelope_id: bytes(16)
recipient_did: bytes(20)
epoch: u32
nonce: bytes(16)
// valid if SHA-256(envelope_id || recipient_did || epoch || nonce)
// has at least pow_difficulty leading zero bits
}
NamedToken {
token_id: bytes(16)
}
Introduction {
introducer_did: bytes(20)
introducer_dev: bytes(8)
introduced_did: bytes(20)
recipient_did: bytes(20)
issued_at: u64
expires_at: u64
signature: bytes(64) // introducer device key
}
Attestation {
domain: string // matched against the sender's identity_link
}
Control messages travel as ordinary ratchet-encrypted bodies with a distinct content type.
StampGrant { stamps: list<Stamp>, token_id: bytes(16) }
ResendRequest { envelope_id: bytes(16) }
DeliveryReceipt{ envelope_id: bytes(16), at: u64 }
The first implementation should prove the trust model, not the feature list. A v0 profile:
One log with a single operator and a single witness, exposing entry submission, entry lookup by identifier, inclusion proofs, and cosigned tree heads.
One relay implementing: device authentication against the log, per-device envelope queues, relay envelope decryption, stamp verification with a per-epoch spent set, proof-of-work verification with a per-recipient daily cap, prekey publication and replenishment, a blob store with fetch capabilities and expiry, submission receipts, and the own-device sync queue.
One command-line client implementing: identity creation, device add and revoke, log verification, PQXDH and the Double Ratchet, sealed payloads, padding, stamp issuance and automatic replenishment, named tokens, mailbox storage under the mailbox key, and resend requests.
Deferred: proxies and mixnet mode, gateways, introductions, attestations, social recovery, multi-relay, mobile push, and any graphical interface.
Required from day one: deterministic CBOR, the exact primitives in Section 9.1, and published test vectors for every structure in Section 14. Two independent implementations that exchange mail through the reference relay are the exit criterion for this draft.
Signal. Source of X3DH, PQXDH, the Double Ratchet, sealed sender, and the deniability default. DEFMS takes all of them. Signal is a single-operator service with phone-number identities and chat semantics. DEFMS is federated, uses log-anchored identities, and targets durable mail.
SimpleX. No user identifiers at all: each contact pair uses its own unidirectional queues on servers the parties choose, so no server sees a user's full correspondence. DEFMS stamps and named tokens borrow the per-correspondent capability idea. DEFMS keeps a durable, discoverable address, which SimpleX deliberately does not have. The trade is discussed in Section 17.
Delta Chat and Autocrypt. End-to-end encryption over ordinary SMTP with opportunistic key exchange. Excellent interop, but every SMTP server on the path sees headers and metadata, and there is no forward secrecy. DEFMS's gateway is the bridge to that world, not a replacement for it.
Matrix. Federated homeservers with Olm and Megolm encryption. Homeservers see room membership, timing, and sender for every event they route, and room state is replicated to every participating server. DEFMS is closer to mail than to rooms and is designed so that no server sees both ends.
Nostr. Relays plus signed events with public keys as identities. Simple and censorship-resistant, but events are public by default, direct messages leak metadata to relays, and keys cannot rotate. DEFMS's relay set in the log resembles Nostr's relay lists; the log-anchored identifier fixes the rotation problem.
AT Protocol, did:plc. A directory-anchored identifier whose keys rotate through a signed operation log with recovery-key precedence. DEFMS adopts the construction and federates the directory across transparency logs.
Certificate Transparency, Sigsum, IETF Key Transparency. Append-only logs, witness cosigning, and the auditing model. DEFMS uses them for identity state and name bindings. The Key Transparency work in the IETF may eventually replace the per-identity chain described here with a standardized structure.
Privacy Pass. Unlinkable single-use tokens verified by a party other than the issuer. DEFMS stamps are the same shape without blind issuance, which is unnecessary when the issuer is the recipient.
Pond, Cwtch, Briar. Metadata-resistant messaging over Tor. Pond's per-contact groups and fixed-size padding, Cwtch's hosted groups, and Briar's offline-first design are all relevant. All three sacrifice the always-on relay that mail needs for offline delivery.
Proton Mail, Tuta. Hosted end-to-end encrypted mail. Good user experience, but a single operator holds the mailbox, sees all metadata, and interoperates with the outside world in plaintext. DEFMS is what such a service could federate into.
JMAP. A modern client-to-server mail protocol. Not adopted here, but its object model is a reasonable reference for the mailbox and own-device sync layer.
MLS (RFC 9420). Group key agreement. The intended path for group mail and lists, deferred to a later draft.
These are unresolved and should be the focus of early implementation work and review.
Cold-contact experience. A recipient shown an approval prompt for an unknown sender has little to decide on. The design assumes legitimate cold contact arrives through introductions or attestations. That assumption needs a user study.
Mailbox addressing. Envelopes name the recipient identity, so the recipient's relay knows whose mail it holds. SimpleX shows the alternative: per-correspondent queues that reveal nothing about the owner. Queues would remove the recipient identifier from the relay's view, at the cost of address durability and a heavier client. Whether mail can afford that is undecided.
Log governance. Who operates logs and witnesses, how new ones join, and how clients converge on a quorum is underspecified. Certificate Transparency's model of a small set of well-known logs blessed by browser vendors transposes imperfectly to a federation with no vendor.
Post-quantum sealing and ratchet. The sealed payload uses classical HPKE, so sender identity is exposed to a future quantum adversary who recorded traffic. Candidates are a PQ HPKE suite such as X-Wing and Signal's SPQR for the ratchet.
Own-device sync. Conflict rules, queue growth, and the interaction with pseudonymous identities are sketched, not specified.
Groups and mailing lists. One-to-one and small fan-out only. Large groups need MLS and a different admission model. Deferred to v0.4.
Mobile background delivery. iOS and Android constrain background networking, and push services see something. A privacy-preserving wake-up, perhaps through OHTTP or a relay-mediated signal, needs prototyping.
Introduction spam. A compromised contact can flood a user with introduced senders. Rate limits and reputation decay on the introduction graph are sketched, not specified.
Relay abuse. Blob quotas, spent-stamp set growth per epoch, proof-of-work tuning, and what a relay does when a recipient never comes back.
Bonds. v0.2 proposed a refundable payment escrowed against a cold-contact message. The mechanism is attractive on paper and pulls payments and escrow into a mail protocol. It stays out of the core until someone shows it working.
Cheap identities. Every identity requires a log
entry. A lighter identity type for throwaway pseudonyms, perhaps a bare
did:key with no rotation, may be worth the complexity.
Storage permanence at scale. "Recipient fetches and owns" works for individuals. High-volume institutional inboxes need a storage tier and a migration path.
Admission key. A per-identity signing key whose public half is in the log. Signs stamps.
AEAD. Authenticated Encryption with Associated Data. A symmetric cipher mode providing confidentiality and integrity.
CBOR. Concise Binary Object Representation (RFC 8949). The wire encoding.
Deniability. Authentication that convinces the recipient but cannot be shown to a third party.
DID. Decentralized Identifier. DEFMS uses
did:defms, derived from the genesis log entry.
Double Ratchet. Signal's protocol combining a Diffie-Hellman ratchet with a symmetric-key ratchet for forward and post-compromise security.
Forward secrecy. Compromise of long-term keys does not decrypt past traffic.
HPKE. Hybrid Public Key Encryption (RFC 9180). Used to seal payloads to devices and envelopes to relays.
Identity key. The key that signs an identity's device certificates and log entries.
Mailbox key. A per-identity symmetric key protecting mail at rest and shared among the identity's devices.
ML-KEM. The NIST post-quantum key encapsulation mechanism (FIPS 203). Used at 768-bit parameter set.
MLS. Messaging Layer Security (RFC 9420). Group key agreement.
Named token. A per-correspondent identifier inside the sealed payload that tells the recipient which handed-out address was used.
Post-compromise security. After a key compromise, security is restored once the ratchet heals.
PQXDH. Signal's post-quantum extended key agreement, combining X25519 and ML-KEM.
Prekey. A public key published in advance to allow asynchronous session establishment.
Recovery key. An offline key that can replace the identity key and takes precedence over it.
Relay. The always-on service that queues mail for an identity's devices.
Sealed sender. The sender's identity is encrypted to the recipient and hidden from infrastructure.
Stamp. A single-use, recipient-signed admission credential verified by the relay.
Submission proxy. Any party that forwards an opaque relay envelope on the sender's behalf.
TOFU. Trust on first use. Pin a binding on first contact and detect later changes.
Transparency log. An append-only, witness-cosigned record of identity state.
Witness. An independent party that cosigns a log's tree heads after checking consistency.
End of Draft v0.3.