# End-to-End Data Flow

This page traces the three core data flows through the quicprochat system:
registration, group creation, and message exchange. Each flow is illustrated
with an ASCII sequence diagram showing control-plane (AS) and data-plane (DS)
traffic.

Throughout these flows the server is **MLS-unaware** -- it stores and forwards
opaque byte blobs without parsing their MLS content. All RPC calls use the v2
Protobuf framing protocol over QUIC (ALPN: `qpc`, port 5001).

---

## 1. Registration Flow

Before a client can join any MLS group, it must authenticate with OPAQUE,
generate an Ed25519 identity keypair, and upload at least one KeyPackage to
the Authentication Service.

### Sequence Diagram

```text
  Client (Alice)                           Server (port 5001)
  --------------                           ------------------
        |                                         |
        |  1. OpaqueRegisterStart (100)            |
        |     username, registration_request       |
        | ---------------------------------------->|
        |                                         |
        |     registration_response               |
        | <----------------------------------------|
        |                                         |
        |  2. OpaqueRegisterFinish (101)           |
        |     username, upload, identity_key       |
        | ---------------------------------------->|
        |                                         |  3. Store OPAQUE record +
        |     success                             |     identity key mapping
        | <----------------------------------------|
        |                                         |
        |  4. Generate MLS KeyPackage              |
        |     (GroupMember::generate_key_package)  |
        |     - Creates HPKE init keypair          |
        |     - Embeds Ed25519 pk in credential    |
        |     - Signs leaf node with Ed25519 sk    |
        |     - TLS-encodes the KeyPackage         |
        |                                         |
        |  5. OpaqueLoginStart (102)               |
        |     username, login_request              |
        | ---------------------------------------->|
        |     login_response                      |
        | <----------------------------------------|
        |                                         |
        |  6. OpaqueLoginFinish (103)              |
        |     username, finalization, identity_key |
        | ---------------------------------------->|
        |     session_token                       |
        | <----------------------------------------|
        |                                         |
        |  7. UploadKeyPackage (300)               |
        |     identity_key, package, session_token |
        | ---------------------------------------->|
        |                                         |  8. Validate + store
        |     fingerprint (SHA-256)               |     in KeyPackage queue
        | <----------------------------------------|
        |                                         |
        |  9. Compare local fingerprint with       |
        |     server-returned fingerprint          |
        |     (tamper detection)                   |
        |                                         |
```

### Key Points

- **KeyPackages are single-use** (RFC 9420 requirement). Each `FetchKeyPackage`
  call atomically removes and returns one package. The client should upload
  multiple KeyPackages if it expects to be added to several groups.

- The `identity_key` used as the AS index is the **raw 32-byte Ed25519 public
  key**, not a fingerprint or hash. Peers must know Alice's public key out-of-
  band (QR code, directory lookup via `ResolveUser`, etc.) to fetch her KeyPackage.

- The HPKE init private key generated during `generate_key_package` is stored
  in the client's `DiskKeyStore`. The **same `GroupMember` instance** (or a
  restored instance with the same key store) must later call `join_group` to
  decrypt the Welcome message.

- The optional hybrid public key (`UploadHybridKey`, method 302) can also be
  uploaded during registration for post-quantum envelope encryption.

---

## 2. Group Creation Flow

Alice creates a new MLS group, fetches Bob's KeyPackage from the AS, adds Bob
to the group (producing a Commit and a Welcome), and delivers the Welcome to
Bob via the DS.

### Sequence Diagram

```text
  Alice                       Server (AS+DS, port 5001)              Bob
  -----                       -------------------------              ---
    |                                 |                               |
    |  1. create_group("my-group")    |                               |
    |     (local MLS operation --     |                               |
    |      Alice is sole member,      |                               |
    |      epoch 0)                   |                               |
    |                                 |                               |
    |  2. FetchKeyPackage (301)       |                               |
    |     bob_identity_key            |                               |
    | -------------------------------->                               |
    |                                 |  3. Pop bob's KeyPackage      |
    |                                 |     from queue (atomic)       |
    |          bob_kp bytes           |                               |
    | <--------------------------------                               |
    |                                 |                               |
    |  4. add_member(bob_kp)          |                               |
    |     Local MLS operations:       |                               |
    |     a. Deserialise & validate   |                               |
    |        Bob's KeyPackage         |                               |
    |     b. Produce Commit message   |                               |
    |        (adds Bob to ratchet     |                               |
    |         tree, advances epoch)   |                               |
    |     c. Produce Welcome message  |                               |
    |        (encrypted to Bob's      |                               |
    |         HPKE init key, contains |                               |
    |         group secrets + tree)   |                               |
    |     d. merge_pending_commit()   |                               |
    |        (Alice advances to       |                               |
    |         epoch 1 locally)        |                               |
    |                                 |                               |
    |  5. Enqueue (200)               |                               |
    |     recipient=bob_pk, payload=welcome                           |
    | -------------------------------->                               |
    |                                 |  6. Append welcome to         |
    |                                 |     deliveries[bob_pk]        |
    |                                 |                               |
    |                                 |  7. Notify bob_pk waiters     |
    |                                 |     (FetchWait wakes up)      |
    |                                 |                               |
    |                                 |  8. Bob connects and polls    |
    |                                 | <------------------------------
    |                                 |     FetchWait (202)           |
    |                                 |                               |
    |                                 |  9. Drain bob's queue         |
    |                                 |     (returns [welcome])       |
    |                                 |                               |
    |                                 |        [welcome_bytes]        |
    |                                 | ------------------------------>
    |                                 |                               |
    |                                 |                               |  10. join_group(welcome)
    |                                 |                               |      - Decrypt Welcome with
    |                                 |                               |        HPKE init private key
    |                                 |                               |      - Extract ratchet tree
    |                                 |                               |        from GroupInfo ext
    |                                 |                               |      - Initialise MlsGroup
    |                                 |                               |        at epoch 1
    |                                 |                               |
    |                                 |                               |  Bob is now a group member
    |                                 |                               |
```

### Key Points

- The **Commit** message is relevant for groups with more than two members. In
  the two-party case, Alice is the sole existing member and merges the commit
  herself. In a multi-member group, the Commit would be sent to all existing
  members via the DS so they can advance their epoch.

- The **Welcome** message is encrypted to Bob's HPKE init key (derived from
  the KeyPackage). Only the `GroupMember` instance that generated that
  KeyPackage holds the corresponding private key.

- The `use_ratchet_tree_extension = true` MLS config embeds the full ratchet
  tree in the Welcome's `GroupInfo` extension. This means Bob does not need a
  separate tree fetch -- `new_from_welcome` extracts it automatically.

- The DS routes solely by `recipient_key` (Bob's Ed25519 public key). It does
  not parse the Welcome, the Commit, or any MLS structure.

---

## 3. Message Exchange Flow

After both Alice and Bob are group members, they exchange MLS Application
messages through the DS.

### Sequence Diagram

```text
  Alice                       Server (DS, port 5001)                 Bob
  -----                       ----------------------                 ---
    |                                 |                               |
    |  -- Alice sends a message to Bob --                            |
    |                                 |                               |
    |  1. send_message("hello bob")   |                               |
    |     MLS create_message():       |                               |
    |     - Derive message key from   |                               |
    |       epoch secret + gen counter|                               |
    |     - Encrypt plaintext with    |                               |
    |       AES-128-GCM               |                               |
    |     - Produce MlsMessageOut     |                               |
    |       (PrivateMessage variant)  |                               |
    |     - TLS-encode to bytes       |                               |
    |                                 |                               |
    |  2. Enqueue (200)               |                               |
    |     recipient=bob_pk, payload   |                               |
    | -------------------------------->                               |
    |                                 |  3. Store in bob's queue      |
    |                                 |  4. Notify bob_pk waiters     |
    |                                 |     (or push PushNewMessage)  |
    |                                 |                               |
    |                                 |          (time passes)        |
    |                                 |                               |
    |                                 |  5. Bob polls for messages    |
    |                                 | <------------------------------
    |                                 |     FetchWait (202)           |
    |                                 |                               |
    |                                 |  6. Drain bob's queue         |
    |                                 |        [ciphertext]           |
    |                                 | ------------------------------>
    |                                 |                               |
    |                                 |                               |  7. receive_message(ct)
    |                                 |                               |     MLS process_message():
    |                                 |                               |     - Identify sender from
    |                                 |                               |       PrivateMessage header
    |                                 |                               |     - Derive decryption key
    |                                 |                               |       from epoch secret
    |                                 |                               |     - Decrypt AES-128-GCM
    |                                 |                               |     - Return plaintext:
    |                                 |                               |       "hello bob"
    |                                 |                               |
    |  -- Bob replies to Alice --                                    |
    |                                 |                               |
    |                                 |                               |  8. send_message("hello alice")
    |                                 |                               |     (same MLS encrypt flow)
    |                                 |                               |
    |                                 |  9. Enqueue (200)             |
    |                                 |     recipient=alice_pk        |
    |                                 | <------------------------------
    |                                 | 10. Store + notify            |
    |                                 |                               |
    | 11. Fetch (201)                 |                               |
    | -------------------------------->                               |
    |         [ciphertext]            |                               |
    | <--------------------------------                               |
    |                                 |                               |
    | 12. receive_message(ct)         |                               |
    |     -> "hello alice"            |                               |
    |                                 |                               |
```

### Key Points

- **MLS provides forward secrecy**: each message is encrypted with a key
  derived from the current epoch secret and a per-sender generation counter.
  Compromising a future key does not reveal past messages.

- **The DS is a dumb relay**: it does not decrypt, inspect, or reorder
  messages. It stores opaque byte blobs in a FIFO queue keyed by recipient.

- **Long-polling** via `FetchWait` (202) avoids the need for persistent
  connections or WebSocket-style push. The client specifies a timeout in
  milliseconds; the server blocks up to that duration using
  `tokio::sync::Notify`. Push events (method 1000 `PushNewMessage`) deliver
  real-time notifications on a separate QUIC uni-stream.

- **Channel-aware routing** is supported: the `channel_id` field in `Enqueue`
  and `Fetch` allows scoping queues by channel (e.g., a UUID for a 1:1
  conversation or group). When `channel_id` is empty, messages go to the
  default queue.

---

## Control-Plane vs. Data-Plane Summary

```text
+---------------------------------------------------------------------+
|                        Control Plane (AS)                           |
|                                                                     |
|  UploadKeyPackage (300)  ---->  Store KeyPackage for identity       |
|  FetchKeyPackage (301)   <----  Pop and return one KeyPackage       |
|  UploadHybridKey (302)   ---->  Store hybrid PQ public key         |
|  FetchHybridKey (303)    <----  Return hybrid PQ public key        |
|  FetchHybridKeys (304)   <----  Return hybrid keys for N identities|
|                                                                     |
|  Traffic: Infrequent. Once per group join (upload before,           |
|           fetch during group add).                                  |
+---------------------------------------------------------------------+

+---------------------------------------------------------------------+
|                        Data Plane (DS)                              |
|                                                                     |
|  Enqueue (200)          ---->  Append payload to recipient queue   |
|  Fetch (201)            <----  Drain and return all queued payloads|
|  FetchWait (202)        <----  Long-poll drain with timeout        |
|  Peek (203)             <----  Inspect without removing            |
|  Ack (204)              ---->  Acknowledge and remove by seq num   |
|  BatchEnqueue (205)     ---->  Enqueue multiple payloads at once   |
|                                                                     |
|  Traffic: High-frequency. Every MLS message (Welcome, Commit,       |
|           Application) flows through the DS.                        |
+---------------------------------------------------------------------+
```

The separation means the AS can be rate-limited or placed behind stricter
access controls without affecting message throughput on the DS.

---

## State Transitions

The following diagram summarises the client-side state machine across all three
flows:

```text
                      +--------------+
                      |   No State    |
                      +------+-------+
                             |
              OPAQUE register + login
                             |
                             v
                      +--------------+
                      |  Authenticated |  session_token obtained
                      |               |  No identity yet
                      +------+--------+
                             |
                 IdentityKeypair::generate()
                 + UploadKeyPackage (300)
                             |
                             v
                      +--------------+
                      |  Registered   |  KeyPackage on AS
                      |               |  HPKE init key in DiskKeyStore
                      +------+-------+
                             |
              +--------------+--------------+
              |                             |
      create_group()                join_group(welcome)
              |                             |
              v                             v
       +-------------+              +--------------+
       | Group Owner  |              | Group Member  |
       | (epoch 0)    |              | (epoch N)     |
       +------+------+              +------+-------+
              |                             |
       add_member()                         |
              |                             |
              v                             v
       +------------------------------------------+
       |          Active Group Member              |
       |                                          |
       |  send_message() -> Enqueue (200)         |
       |  receive_message() <- Fetch/FetchWait    |
       |                    or PushNewMessage      |
       |                                          |
       |  Epoch advances on each Commit           |
       +------------------------------------------+
```

---

## Further Reading

- [Architecture Overview](overview.md) -- system diagram and two-service model
- [Service Architecture](service-architecture.md) -- RPC method details and push events
- [GroupMember Lifecycle](../internals/group-member-lifecycle.md) -- detailed MLS state machine
- [KeyPackage Exchange Flow](../internals/keypackage-exchange.md) -- single-use semantics and AS internals
- [MLS (RFC 9420)](../protocol-layers/mls.md) -- key schedule, ratchet tree, and ciphersuite details
- [Forward Secrecy](../cryptography/forward-secrecy.md) -- how MLS provides forward secrecy
- [Post-Compromise Security](../cryptography/post-compromise-security.md) -- group healing after key compromise
