top of page

Cord Technical White Paper

 

Last Updated: August 2026

 

This document describes Cord's production architecture: how identity works without phone numbers, how messages are encrypted, what servers can observe, and where the design still has residual risk. It is a technical companion to the [Privacy Policy](https://www.cordmessaging.com/), not a marketing page and not a substitute for an independent audit.

 

1. Why this page exists

 

Cord is a private-by-design messenger. The interesting claims are architectural, not visual: no phone number, no email, no username; end-to-end encryption for messages, media, and optional profile details; and a server that is built to route ciphertext it cannot read.

 

This paper is the place to say how those guarantees are implemented, and — equally — what they do not cover.

2. Design goals

 

Cord is designed around five constraints:

 

1. No personal-account identity. Core messaging must work without a phone number, email address, or username.

2. Content stays on devices. Plaintext messages, media, and shared profile details are encrypted on the sender's device. Servers store ciphertext.

3. Keys stay on devices.Private keys are generated on-device and held in OS-backed secure storage. There is no key escrow.

4. Minimize what routing requires. Delivery needs some metadata. Cord tries to keep that set small and non-identifying.

5. Be honest about the remainder. Membership, timing, and size are still visible to infrastructure. Those are documented below rather than waved away.

 

3. Identity without accounts

 

There is no Cord account in the usual sense. A device generates its own identifiers locally using the operating system's CSPRNG.

 

| Identifier        | What it is                                  | Where it lives                                    

|-------------------|---------------------------------------------|---------------------------------------------------|

| Device ID         | Random UUID (128-bit)                       | Device Keychain / encrypted storage               

| Cord ID           | Short shareable code for a conversation     | Hashed before use in routing                       

| Invite secret `S` | 256-bit random value carried in the QR /

                      invite token                                | Device only                                       

| Olm identity key  | Curve25519 public key, per cord, per device | Public half published; private half on-device     

| Olm signing key   | Ed25519 public key, per cord, per device    | Used to sign published key material               

 

Cord does not read IMEI, MAC address, advertising ID, or other hardware identifiers. It does not upload contact lists. Anonymous authentication is used only so the API can apply row-level access control — not to bind the app to an email or phone.

 

Joining a new cord requires the full invite (QR or copyable token), not the Cord ID alone. The server stores a one-way hash of the invite secret:

join_capability = SHA-256( UTF-8("cord_join_v1:") || S )

Possession of `S` proves the right to join. The server never stores `S` itself.

 

4. End-to-end encryption

 

Every message is encrypted on the sender's device before it is handed to Cord's servers, and decrypted only on recipient devices.

 

Protocol

 

Cord uses Olm: a triple Diffie–Hellman session setup followed by a Double Ratchet for subsequent messages. The implementation is [vodozemac](https://github.com/matrix-org/vodozemac), Matrix's audited Apache-2.0 library, called from the client through a thin native binding.

 

Each device in a cord publishes a public bundle:

 

- Curve25519 identity key

- Ed25519 signing key

- A signed fallback key, and (when available) one-time keys

 

Published one-time and fallback keys are Ed25519-signed. Recipients verify that signature before creating a session, so a malicious or compromised server cannot silently substitute its own key material.

 

Session setup and ratcheting

 

- The first message to a peer establishes an Olm session (a pre-key message). One-time keys are consumed when used; a signed fallback key is used when a one-time key is not available.

- Later messages use the Double Ratchet. Compromising a current session key does not expose earlier messages.

- Session state is pickled, encrypted with a per-install key, and stored in OS-backed secure storage (iOS Keychain / Android encrypted preferences). Private keys are not uploaded.

 

What this gives you

 

- Confidentiality. Only devices in the cord can read the plaintext.

- Forward secrecy. Earlier messages remain protected if a later key is compromised.

- Authenticated key material. Bundles are signed; safety numbers (below) let you check for a man-in-the-middle.

 

Olm message authentication is deniable in the usual Double Ratchet sense: a recipient can be confident of the sender, but the transcript is not a cryptographic proof designed for showing to third parties.

 

5. Message envelopes (sealed-sender-lite)

 

Ciphertext is stored as an envelope. Cord's current production design is sealed-sender-lite:

 

- The outer envelope (what the server stores) carries routing fields and an opaque ciphertext blob. It does not store a sender identity field.

- The inner payload (encrypted) includes the sender's device ID, the message body, and any attachments metadata. Recipients learn who sent the message only after decryption.

 

Delivery is currently routed by recipient device ID so messages remain reachable if a device has been offline. That is a deliberate reliability choice. It means the infrastructure can see which device a given ciphertext is for. It does not see the plaintext, and it does not see the sender in the stored envelope.

 

A stronger “true sealed sender” mode (hiding the sender from the server even at send time) and rotating pairwise routing identifiers exist as experimental work. They are not what production builds use today.

 

6. Media

 

Images, video, audio, and files are encrypted on the device before upload.

 

- Algorithm: XChaCha20-Poly1305 (256-bit random file key, 192-bit nonce)

- Filename: replaced with an opaque UUID; the object is stored as `application/octet-stream`

- Images: compressed with EXIF stripped before encryption, so location and camera metadata are not in the uploaded bytes

- File keys: travel inside the end-to-end-encrypted message payload, not as server-readable metadata

 

The storage provider therefore sees an unreadable blob with a random name. It does not see the original filename, MIME type, or image metadata.

 

7. Profile details

 

Aliases and avatars are optional. When you set them, they are encrypted with a per-cord key derived from the invite secret `S`, not from the shareable Cord ID:

K_meta = HKDF-SHA-256(

ikm = S, // 256-bit invite secret

salt = 32 zero bytes,

info = UTF-8("cord_meta_v2:" || purpose),

L = 32

)

 

`purpose` is domain-separated (`cord_display_name`, `cord_avatar`, `cord_profile_avatar`). The server stores only ciphertext. Brute-forcing the short Cord ID does not yield `K_meta`, because `S` never leaves the invite and the participants' devices.

 

8. Safety numbers

 

Each participant has a Cord safety number derived from the two Olm identity public keys in that cord. The construction is a 60-digit decimal fingerprint (two 30-digit halves, sorted so both people see the same number), produced by iterated SHA-256 over the raw Curve25519 key bytes.

 

Compare the number in person, or scan the in-app QR, to confirm that the session you have is the session you think you have. Until you verify, Cord is trust-on-first-use: a network attacker who could swap keys at first contact would produce a different safety number.

 

If you previously verified someone and the number changes, treat that as a warning and compare again.

 

9. Transport, push, and infrastructure

 

| Layer                                          | Role 

|------------------------------------------------|------

| TLS 1.3                                        | Protects the client–server channel 

| Supabase (Postgres + storage + edge functions) | Holds encrypted envelopes, public key bundles,

                                                   hashed identifiers, and push tokens under row-level security 

| Edge functions                                 | Join, enqueue, leave, device revoke — routing and membership, not decryption 

| APNs / FCM                                     | Wake the device. Pushes are delivery signals (envelope id / “new message”).

                                                   They do not carry plaintext. 

 

Push tokens are required for background delivery on iOS and Android. They identify a device to Apple or Google; they are not a Cord account, and they are not sent inside message bodies.

 

10. What servers can and cannot see

 

Cannot see (by design):

 

- Message plaintext

- Media plaintext, original filenames, or image EXIF

- Shared aliases and avatars

- Private keys, ratchet state, or the invite secret `S`

- Phone numbers, emails, usernames, contact books, or hardware IDs (not collected)

 

Can see (necessary for delivery, or inherent to the current routing mode):

 

- Encrypted envelopes and their approximate size

- Recipient device IDs used for routing

- Which devices have published a bundle in a cord (membership)

- Hashed Cord IDs and the join-capability hash

- Envelope / message identifiers and delivery-state metadata

- Timestamps (when an envelope was enqueued)

- APNs / FCM device tokens

- Limited operational/security telemetry

 

That list is the honest remainder. End-to-end encryption does not hide traffic patterns. A party who can watch the database or the network can still see that devices in a cord are exchanging envelopes, when, and roughly how large.

 

11. Cryptographic primitives

 

| Function                     | Algorithm 

|------------------------------|-----------

| Session protocol             | Olm (triple-DH + Double Ratchet) via vodozemac 

| Identity / key agreement     | Curve25519 (X25519) 

| Signatures on published keys | Ed25519 

| Media encryption             | XChaCha20-Poly1305 

| Profile/metadata keys        | HKDF-SHA-256 

| Join capability              | SHA-256 with domain separation 

| Safety numbers               | Iterated SHA-256 over identity keys 

| Randomness                   | OS CSPRNG 

| At-rest key storage          | iOS Keychain / Android encrypted storage 

 

No custom or proprietary ciphers. The interesting choices are protocol composition and what is not collected, not a new primitive.

12. Threat model (summary)

 

A compromised or curious server gets ciphertext, membership, timing, and size. It does not get message content, profile plaintext, or private keys. It cannot forge a published one-time/fallback key without breaking Ed25519, assuming clients verify signatures (they do).

 

A network eavesdropper sees TLS to Cord's infrastructure. Under TLS it does not see envelope contents. Traffic analysis (timing, volume) remains possible.

 

A malicious peer you invited can read that cord — that is the product. They cannot read other cords, and they cannot decrypt envelopes that were not sent to them.

 

A stolen unlocked device can read local history and use the device's keys until you revoke it from another device. OS disk encryption and a lock screen are part of the model; Cord cannot protect an unlocked phone.

 

Trust-on-first-use until safety numbers are compared. Verify when the threat model warrants it.

 

Not currently claimed: resistance to global traffic analysis, mix-net routing, or post-quantum key exchange. Those are future work, not present guarantees.

 

13. What this paper is not

 

- It is not a formal cryptographic proof.

- It is not a third-party audit report. Cord's implementation is structured so it can be reviewed; a public audit summary belongs here if and when one is completed.

- It is not a promise that metadata does not exist. Section 10 is the list.

 

The [Privacy Policy](https://www.cordmessaging.com/) is the legal description of collection and retention. This paper is the engineering description of the same system.

Last Updated: August 2026

bottom of page