# Wire Format Overview

This section documents the v2 serialisation pipeline that transforms application-level data structures into bytes on the wire. Every byte exchanged between quicprochat clients and the server passes through this pipeline, so understanding it is prerequisite to reading the protocol deep dives or the server and client source code.

---

## Serialisation pipeline

Data flows through three stages on the send path. The receive path reverses the order.

```text
 Stage 1          Stage 2                Stage 3
 --------         --------               --------
 Application      Protobuf               Transport
 data             serialisation          encryption

 RPC call         prost::encode()        QUIC/TLS 1.3
                  + binary frame header

       |               |                        |
       v               v                        v
  Rust structs   10-byte (request) or      Encrypted
  & method       9-byte (response)         ciphertext
  invocations    binary header +           on the wire
                 protobuf payload
```

### Stage 1: Application creates a message or RPC call

At the application layer, the client or server constructs a typed Protobuf message defined in `proto/qpc/v1/*.proto`. Each RPC method has a corresponding request and response message type.

- **Auth methods** (IDs 100-103): see [Auth Schema](auth-schema.md)
- **Delivery methods** (IDs 200-205): see [Delivery Schema](delivery-schema.md)
- **All methods**: see [Method ID Reference](envelope-schema.md)
- **Full RPC reference**: see [RPC Reference](node-service-schema.md)

### Stage 2: Binary framing + Protobuf serialisation

The v2 protocol defines three frame types, each with a compact binary header followed by a Protobuf-encoded payload. All multi-byte integers are **big-endian**.

#### Request frame (client to server, bidirectional stream)

```text
 0       1       2       3       4       5       6       7       8       9
+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
| method_id (u16)       | request_id (u32)                      | len (u32)     |
+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+
| len (cont.)   | protobuf payload ...
+-------+-------+-------...
```

| Offset | Field | Type | Description |
|--------|-------|------|-------------|
| 0-1 | `method_id` | `u16 BE` | RPC method identifier |
| 2-5 | `request_id` | `u32 BE` | Client-generated correlation ID |
| 6-9 | `payload_len` | `u32 BE` | Length of the protobuf payload in bytes |
| 10+ | payload | bytes | Protobuf-encoded request message |

Header size: **10 bytes**.

#### Response frame (server to client, same bidirectional stream)

```text
 0       1       2       3       4       5       6       7       8
+-------+-------+-------+-------+-------+-------+-------+-------+-------+
| status| request_id (u32)                      | len (u32)             |
+-------+-------+-------+-------+-------+-------+-------+-------+-------+
| protobuf payload ...
+-------...
```

| Offset | Field | Type | Description |
|--------|-------|------|-------------|
| 0 | `status` | `u8` | RPC status code (0 = OK) |
| 1-4 | `request_id` | `u32 BE` | Echoes the request correlation ID |
| 5-8 | `payload_len` | `u32 BE` | Length of the protobuf payload in bytes |
| 9+ | payload | bytes | Protobuf-encoded response message |

Header size: **9 bytes**.

#### Push frame (server to client, QUIC uni-stream)

```text
 0       1       2       3       4       5
+-------+-------+-------+-------+-------+-------+
| event_type (u16)      | payload_len (u32)     |
+-------+-------+-------+-------+-------+-------+
| protobuf payload ...
+-------...
```

| Offset | Field | Type | Description |
|--------|-------|------|-------------|
| 0-1 | `event_type` | `u16 BE` | Push event type identifier |
| 2-5 | `payload_len` | `u32 BE` | Length of the protobuf payload in bytes |
| 6+ | payload | bytes | Protobuf-encoded push event message |

Header size: **6 bytes**.

#### Limits

| Constraint | Value |
|------------|-------|
| Maximum payload size | 4 MiB (4,194,304 bytes) |
| Payloads exceeding this limit | rejected with `PayloadTooLarge` error |

Source: `crates/quicprochat-rpc/src/framing.rs`.

### Stage 3: Transport encryption

The framed bytes are encrypted by the QUIC/TLS 1.3 transport layer. QUIC provides stream framing and length delimitation; the RPC framework reads exactly `header_size + payload_len` bytes per frame.

| Transport | Encryption | Authentication |
|-----------|------------|----------------|
| QUIC + TLS 1.3 | AES-128-GCM or ChaCha20-Poly1305 (negotiated by TLS) | Server cert (rustls/quinn) |

The transport layer treats the framed payload as opaque bytes. Serialisation format and transport evolve independently.

---

## QUIC stream model

Each RPC call uses a **dedicated QUIC bidirectional stream**:

1. Client opens a new bidirectional stream.
2. Client sends one request frame and closes the write end.
3. Server reads the request, dispatches by `method_id`, and sends one response frame.
4. Server closes the write end.

Push events (server-initiated) are sent on **QUIC uni-streams** opened by the server. There is no request correlation ID in push frames.

This design allows unlimited concurrent RPCs with no head-of-line blocking.

---

## Connection parameters

| Parameter | Value |
|-----------|-------|
| Protocol | QUIC (RFC 9000) |
| ALPN | `"qpc"` |
| Default port | 5001 |
| TLS version | 1.3 only |
| Certificate | Server presents a TLS certificate; clients verify against a CA cert |

---

## Schema index

Protobuf schemas are defined in `proto/qpc/v1/` and documented on dedicated pages:

| Proto File | Documentation | Purpose |
|------------|---------------|---------|
| `auth.proto` | [Auth Schema](auth-schema.md) | OPAQUE registration and login (IDs 100-103) |
| `delivery.proto` | [Delivery Schema](delivery-schema.md) | Message delivery (IDs 200-205) |
| `keys.proto` | [Delivery Schema](delivery-schema.md) | Key packages and hybrid keys (IDs 300-304, 510-520) |
| `channel.proto` | [RPC Reference](node-service-schema.md) | Channel creation (ID 400) |
| `group.proto` | [RPC Reference](node-service-schema.md) | Group management (IDs 410-413) |
| `moderation.proto` | [RPC Reference](node-service-schema.md) | Content moderation (IDs 420-424) |
| `user.proto` | [RPC Reference](node-service-schema.md) | User resolution (IDs 500-501) |
| `blob.proto` | [RPC Reference](node-service-schema.md) | Blob storage (IDs 600-601) |
| `device.proto` | [RPC Reference](node-service-schema.md) | Device management (IDs 700-702, 710) |
| `recovery.proto` | [RPC Reference](node-service-schema.md) | Account recovery (IDs 750-752) |
| `p2p.proto` | [RPC Reference](node-service-schema.md) | P2P endpoints and health (IDs 800-802) |
| `federation.proto` | [RPC Reference](node-service-schema.md) | Cross-server relay (IDs 900-905) |
| `push.proto` | [RPC Reference](node-service-schema.md) | Push event types (IDs 1000+) |
| `common.proto` | [RPC Reference](node-service-schema.md) | Auth context, account deletion (ID 950) |

Method ID assignment: `crates/quicprochat-proto/src/lib.rs` (`method_ids` module).

---

## Further reading

- [Architecture Overview](../architecture/overview.md) -- system-level view of how services compose
- [Method ID Reference](envelope-schema.md) -- complete table of all 44 RPC methods
- [Auth Schema](auth-schema.md) -- OPAQUE authentication proto definitions
- [Delivery Schema](delivery-schema.md) -- message delivery proto definitions
- [RPC Reference](node-service-schema.md) -- complete proto definitions for all 14 files
