Message Delivery Flow

Interaction Type: 🌐 REMOTE (Via Relay)

End-to-end message delivery from card update to acknowledgment.

Participants

  • Alice - User sending card update
  • Alice's Device - Source device
  • Relay - WebSocket relay server
  • Bob's Device - Recipient device
  • Bob - Contact receiving update

Message Sizes & Frequency

Message TypePayloadPadded SizeFrequency
Card delta50-200 B256 B1-5/month per user
Full card500 B - 2 KB1-4 KBInitial exchange only
Acknowledgment32-64 B256 BPer received message
Device sync100-500 B256 B - 1 KBReal-time when active

Complete Delivery Flow

    accTitle: Message Delivery Sequence
    accDescr: Shows end-to-end encrypted message delivery from card update to relay acknowledgment
sequenceDiagram
    autonumber
    participant A as Alice
    participant AD as Alice's Device 📱
    participant R as Relay 🖥️
    participant BD as Bob's Device 📱
    participant B as Bob

    Note over A,B: 🌐 REMOTE: All via relay

    %% Alice edits card
    A->>AD: Edit phone: "555-1111" → "555-2222"
    activate AD
    AD->>AD: Update local card
    AD->>AD: Create CardDelta
    Note right of AD: Delta: ~100 bytes<br/>{"field":"phone","value":"555-2222"}
    deactivate AD

    %% Encryption
    activate AD
    AD->>AD: Check visibility: Bob can see phone? ✓
    AD->>AD: Ratchet send chain forward
    Note right of AD: Chain gen 42 → 43<br/>Message key derived
    AD->>AD: Encrypt delta (XChaCha20-Poly1305)
    AD->>AD: Pad to 256 bytes (bucket)
    AD->>AD: Generate CEK, sign payload
    Note right of AD: Final: ~256 bytes<br/>(v0x02 format)
    deactivate AD

    %% Send to relay
    activate AD
    AD->>R: EncryptedUpdate(recipient=Bob, blob=...)
    Note over AD,R: WebSocket frame:<br/>4-byte length + JSON envelope
    deactivate AD

    %% Relay processing
    activate R
    R->>R: Validate recipient_id format
    R->>R: Check quota (blobs < 1000, storage < 50MB)
    R->>R: Store blob indexed by recipient_id
    R-->>AD: Acknowledgment(status=Stored)
    Note right of R: Blob stored with 30-day TTL
    deactivate R

    %% Delivery
    alt Bob is Online (Connected to Relay)
        activate R
        R-->>BD: Forward EncryptedUpdate
        deactivate R

        activate BD
        BD->>BD: Receive encrypted blob
        BD->>BD: Resolve anonymous sender ID
        Note right of BD: Try each contact's<br/>shared key against<br/>anonymous_id
        BD->>BD: Found: Alice
        BD->>BD: Derive message key (chain gen 43)
        BD->>BD: Decrypt payload
        BD->>BD: Remove padding
        BD->>BD: Verify Ed25519 signature
        BD->>BD: Update Alice's card locally
        deactivate BD

        BD->>B: "Alice updated contact info"

        %% Acknowledgment chain
        activate BD
        BD->>R: Acknowledgment(status=ReceivedByRecipient)
        deactivate BD

        activate R
        R-->>AD: Forward Acknowledgment
        Note over R,AD: Sender notified if<br/>suppress_presence=false
        deactivate R

        AD->>AD: Mark update as delivered

    else Bob is Offline
        Note over R,BD: Blob queued for later

        opt Bob comes online later
            BD->>R: Connect (Handshake)
            activate R
            R-->>BD: Deliver queued blobs
            deactivate R

            activate BD
            BD->>BD: Process all pending updates
            BD->>BD: Updates applied in order
            deactivate BD
        end
    end

    Note over A,B: Bob now sees Alice's new phone

Double Ratchet Message Flow

    accTitle: Message Delivery Sequence (2)
    accDescr: Shows end-to-end encrypted message delivery from card update to relay acknowledgment
sequenceDiagram
    participant AD as Alice's Ratchet
    participant BD as Bob's Ratchet

    Note over AD,BD: Initial state after X3DH

    rect rgb(240, 248, 255)
        Note over AD: SEND (Message 1)
        AD->>AD: Ratchet send chain: gen 0 → 1
        AD->>AD: Derive message key (gen 0)
        AD->>AD: Encrypt with message key
        AD->>AD: Delete message key
        AD->>BD: [DH_pub, gen=0, idx=0] + ciphertext
    end

    rect rgb(240, 255, 240)
        Note over BD: RECEIVE (Message 1)
        BD->>BD: Verify DH generation matches
        BD->>BD: Ratchet receive chain: gen 0 → 1
        BD->>BD: Derive message key (gen 0)
        BD->>BD: Decrypt
        BD->>BD: Delete message key
    end

    rect rgb(255, 248, 240)
        Note over BD: SEND REPLY (triggers DH ratchet)
        BD->>BD: Generate new ephemeral DH keypair
        BD->>BD: DH ratchet: compute new root key
        BD->>BD: Create new send chain
        BD->>BD: Encrypt with new chain's key
        BD->>AD: [NEW_DH_pub, gen=1, idx=0] + ciphertext
    end

    rect rgb(248, 240, 255)
        Note over AD: RECEIVE (triggers DH ratchet)
        AD->>AD: Detect new DH public key
        AD->>AD: DH ratchet: compute matching root key
        AD->>AD: Create new receive chain
        AD->>AD: Decrypt with new chain's key
    end

Out-of-Order Message Handling

    accTitle: Message Delivery Sequence (3)
    accDescr: Shows end-to-end encrypted message delivery from card update to relay acknowledgment
sequenceDiagram
    participant AD as Alice's Device
    participant R as Relay
    participant BD as Bob's Device

    Note over AD,BD: Messages may arrive out of order

    AD->>R: Message 1 (gen=0, idx=0)
    AD->>R: Message 2 (gen=0, idx=1)
    AD->>R: Message 3 (gen=0, idx=2)

    Note over R: Network delays

    R-->>BD: Message 3 arrives first
    activate BD
    BD->>BD: Expected idx=0, got idx=2
    BD->>BD: Skip chain to idx=2
    BD->>BD: Store skipped keys: [idx=0, idx=1]
    BD->>BD: Decrypt message 3
    deactivate BD

    R-->>BD: Message 1 arrives
    activate BD
    BD->>BD: Lookup skipped key for idx=0
    BD->>BD: Found! Decrypt message 1
    BD->>BD: Delete skipped key
    deactivate BD

    R-->>BD: Message 2 arrives
    activate BD
    BD->>BD: Lookup skipped key for idx=1
    BD->>BD: Found! Decrypt message 2
    BD->>BD: Delete skipped key
    deactivate BD

    Note over BD: All messages processed,<br/>skipped keys cleaned up

Relay Acknowledgment States

    accTitle: Message Delivery Sequence (4)
    accDescr: Shows end-to-end encrypted message delivery from card update to relay acknowledgment
stateDiagram-v2
    direction LR

    [*] --> Sent: Message created
    Sent --> Stored: Relay confirms storage
    Stored --> Delivered: Recipient connected
    Delivered --> ReceivedByRecipient: Recipient acks

    Stored --> Failed: Quota exceeded
    Delivered --> Failed: Decrypt error

    ReceivedByRecipient --> [*]
    Failed --> [*]

Wire Protocol

Envelope Format

┌─────────────────────────────────────────────────────────────┐
│                    MESSAGE ENVELOPE                          │
├─────────────────────────────────────────────────────────────┤
│  4 bytes: Length (big-endian)                               │
│  JSON payload:                                               │
│  {                                                           │
│    "version": 1,                                            │
│    "message_id": "uuid",                                    │
│    "timestamp": unix_secs,                                  │
│    "payload": { ... }                                       │
│  }                                                           │
└─────────────────────────────────────────────────────────────┘

EncryptedUpdate Payload

{
  "type": "EncryptedUpdate",
  "sender_id": "anonymous_id (rotating hourly)",
  "recipient_id": "Bob's public key hash",
  "blob": "base64(encrypted_delta)"
}

Acknowledgment Payload

{
  "type": "Acknowledgment",
  "message_id": "original message uuid",
  "status": "Stored|Delivered|ReceivedByRecipient|Failed",
  "error": null
}

Timing Estimates

PhaseDurationNotes
Encryption + padding1-5 msXChaCha20-Poly1305 is fast
Network latency50-200 msDepends on relay location
Relay storage1-10 msSQLite insert
Forward to recipient50-200 msIf online
Decryption + verify1-5 ms
Total (online)100-400 msEnd-to-end
Total (offline)< 30 daysUntil recipient connects