Architecture

How SecureChat fits together.

Three layers, each with one job, and a clear protocol on each boundary. The relay is the only network-attackable surface; the iOS app is the only attackable surface at rest.

The three layers

iOS app

Source-available. Owns the Curve25519 identity, the per-message symmetric keys, the encrypted local store, and the Safety Number verification workflow. Never talks to a server except the configured relay.

Relay (Fastify)

Source-available. Stateless packet dropbox. Receives sealed, signed envelopes; persists them for at most MAX_TTL_SECONDS; delivers on request. Cannot read the payload; cannot forge a sender.

Storage

File store (FileRelayStore) or in-memory (InMemoryRelayStore). Production uses file. State is opaque envelopes plus acknowledged tombstones; the relay has no separate history.

Protocol: what crosses the wire

The wire protocol is JSON over HTTPS. The relay accepts a versioned envelope:

{
  "protocolVersion": 2,
  "id": "uuid-v4",
  "senderID": "64-hex peer id",
  "recipientID": "64-hex peer id",
  "sealedPayloadBase64": "base64 of AES-GCM ciphertext",
  "signatureBase64": "base64 of Ed25519 signature",
  "createdAt": "RFC 3339 with offset",
  "expiresAt": "RFC 3339 with offset"
}

The sealedPayloadBase64 is the AES-GCM ciphertext produced by the iOS app after:

  1. Generating a per-message symmetric key.
  2. Encrypting the message body to that key with AES-GCM.
  3. Encrypting the symmetric key to the recipient's Curve25519 public key (X25519 + HKDF + AES-GCM, per Apple's CryptoKit combined-seal API).
  4. Signing the whole envelope (sender, recipient, ciphertext, timestamps) with the sender's Ed25519 signing key.

The relay never has the recipient's private key, so it cannot decrypt the symmetric key, so it cannot decrypt the body. The relay only stores and forwards the opaque envelope.

Failure modes

If the relay is compromised

An attacker with full control of the relay can:

  • Drop or delay envelopes (denial of service).
  • Inspect the metadata visible to any internet server (your IP, the relay's IP, packet timing).
  • Cannot read message bodies, forge senders, or replay old packets beyond MAX_TTL_SECONDS.

If your phone is compromised

An attacker with access to the device can:

  • Read all locally stored messages (they are encrypted at rest, but the keys are in the same Keychain).
  • Send messages as you.
  • Cannot read messages on a *second* device that uses a different identity.

If the iOS app is backdoored

The iOS app is the cryptographic root of trust. If a malicious build of the app is installed, it can exfiltrate the device's private key and the local message store. Pin the build to a known commit (see releases) and verify the SHA-256 after download.

If DNS is spoofed

The iOS app pins the relay's TLS certificate via standard system trust. A successful CA compromise or a misissued certificate is the only realistic DNS-spoofing vector. The signed-envelope guarantee is independent of the TLS transport: even a successful MITM cannot forge a sender.

Operational surface

For the canonical list of every endpoint, header, file, and distribution channel, see docs/CURRENT-ENDPOINTS.md.