# v2 Wire Format

The quicproquo v2 protocol uses QUIC (RFC 9000) with TLS 1.3 as the transport layer and Protocol Buffers for message serialization.

## Connection

- **Protocol**: QUIC with TLS 1.3
- **ALPN**: `qpq`
- **Port**: 5001 (default)
- **Certificate**: Server presents a TLS certificate; clients verify against a CA cert

## Frame Format

Every RPC request and response is wrapped in a 10-byte binary header:

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

| Field | Type | Bytes | Description |
|---|---|---|---|
| `method_id` | `u16` | 0-1 | RPC method identifier (network byte order) |
| `req_id` | `u32` | 2-5 | Client-generated request correlation ID (network byte order) |
| `payload_len` | `u32` | 6-9 | Length of the protobuf payload (network byte order) |
| payload | bytes | 10+ | Protobuf-encoded request or response message |

All multi-byte integers are **big-endian** (network byte order).

## Stream Model

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

1. Client opens a new stream
2. Client sends the request frame and marks end-of-stream
3. Server reads the request, processes it, and sends the response frame
4. Server marks end-of-stream

This allows concurrent RPCs without head-of-line blocking.

## Method IDs

### Auth (100-103)

| ID | Method | Request | Response |
|---|---|---|---|
| 100 | `OpaqueRegisterStart` | `OpaqueRegisterStartRequest` | `OpaqueRegisterStartResponse` |
| 101 | `OpaqueRegisterFinish` | `OpaqueRegisterFinishRequest` | `OpaqueRegisterFinishResponse` |
| 102 | `OpaqueLoginStart` | `OpaqueLoginStartRequest` | `OpaqueLoginStartResponse` |
| 103 | `OpaqueLoginFinish` | `OpaqueLoginFinishRequest` | `OpaqueLoginFinishResponse` |

### Delivery (200-205)

| ID | Method | Request | Response |
|---|---|---|---|
| 200 | `Enqueue` | `EnqueueRequest` | `EnqueueResponse` |
| 201 | `Fetch` | `FetchRequest` | `FetchResponse` |
| 202 | `FetchWait` | `FetchWaitRequest` | `FetchWaitResponse` |
| 203 | `Peek` | `PeekRequest` | `PeekResponse` |
| 204 | `Ack` | `AckRequest` | `AckResponse` |
| 205 | `BatchEnqueue` | `BatchEnqueueRequest` | `BatchEnqueueResponse` |

### Keys (300-304)

| ID | Method | Request | Response |
|---|---|---|---|
| 300 | `UploadKeyPackage` | `UploadKeyPackageRequest` | `UploadKeyPackageResponse` |
| 301 | `FetchKeyPackage` | `FetchKeyPackageRequest` | `FetchKeyPackageResponse` |
| 302 | `UploadHybridKey` | `UploadHybridKeyRequest` | `UploadHybridKeyResponse` |
| 303 | `FetchHybridKey` | `FetchHybridKeyRequest` | `FetchHybridKeyResponse` |
| 304 | `FetchHybridKeys` | `FetchHybridKeysRequest` | `FetchHybridKeysResponse` |

### Channel (400)

| ID | Method | Request | Response |
|---|---|---|---|
| 400 | `CreateChannel` | `CreateChannelRequest` | `CreateChannelResponse` |

### Group Management (410-413)

| ID | Method | Request | Response |
|---|---|---|---|
| 410 | `RemoveMember` | `RemoveMemberRequest` | `RemoveMemberResponse` |
| 411 | `UpdateGroupMetadata` | `UpdateGroupMetadataRequest` | `UpdateGroupMetadataResponse` |
| 412 | `ListGroupMembers` | `ListGroupMembersRequest` | `ListGroupMembersResponse` |
| 413 | `RotateKeys` | `RotateKeysRequest` | `RotateKeysResponse` |

### User (500-501)

| ID | Method | Request | Response |
|---|---|---|---|
| 500 | `ResolveUser` | `ResolveUserRequest` | `ResolveUserResponse` |
| 501 | `ResolveIdentity` | `ResolveIdentityRequest` | `ResolveIdentityResponse` |

### Blob (600-601)

| ID | Method | Request | Response |
|---|---|---|---|
| 600 | `UploadBlob` | `UploadBlobRequest` | `UploadBlobResponse` |
| 601 | `DownloadBlob` | `DownloadBlobRequest` | `DownloadBlobResponse` |

### Device (700-702)

| ID | Method | Request | Response |
|---|---|---|---|
| 700 | `RegisterDevice` | `RegisterDeviceRequest` | `RegisterDeviceResponse` |
| 701 | `ListDevices` | `ListDevicesRequest` | `ListDevicesResponse` |
| 702 | `RevokeDevice` | `RevokeDeviceRequest` | `RevokeDeviceResponse` |

### P2P / Health (800-802)

| ID | Method | Request | Response |
|---|---|---|---|
| 800 | `PublishEndpoint` | `PublishEndpointRequest` | `PublishEndpointResponse` |
| 801 | `ResolveEndpoint` | `ResolveEndpointRequest` | `ResolveEndpointResponse` |
| 802 | `Health` | `HealthRequest` | `HealthResponse` |

### Federation (900-905)

| ID | Method | Request | Response |
|---|---|---|---|
| 900 | `RelayEnqueue` | `RelayEnqueueRequest` | `RelayEnqueueResponse` |
| 901-905 | Reserved | -- | -- |

### Account (950)

| ID | Method | Request | Response |
|---|---|---|---|
| 950 | `DeleteAccount` | `DeleteAccountRequest` | `DeleteAccountResponse` |

## Protobuf Definitions

All message types are defined in `proto/qpq/v1/*.proto`:

| File | Services |
|---|---|
| `auth.proto` | OPAQUE registration and login |
| `common.proto` | Auth context, account deletion |
| `delivery.proto` | Message enqueue, fetch, peek, ack |
| `keys.proto` | MLS key packages, hybrid keys |
| `channel.proto` | Channel creation |
| `user.proto` | User/identity resolution |
| `group.proto` | Group management |
| `blob.proto` | Binary object storage |
| `device.proto` | Multi-device management |
| `p2p.proto` | P2P endpoints, health |
| `federation.proto` | Cross-server relay |
| `push.proto` | Push notifications |
| `recovery.proto` | Account recovery |
| `moderation.proto` | Content moderation |

## Authentication Flow

Authentication uses the OPAQUE protocol (asymmetric PAKE):

```
Client                              Server
  │                                    │
  │  OpaqueRegisterStart(username,     │
  │    registration_request)           │
  │ ──────────────────────────────────►│
  │                                    │
  │  OpaqueRegisterStartResponse(      │
  │    registration_response)          │
  │ ◄──────────────────────────────────│
  │                                    │
  │  OpaqueRegisterFinish(username,    │
  │    upload, identity_key)           │
  │ ──────────────────────────────────►│
  │                                    │
  │  OpaqueRegisterFinishResponse(     │
  │    success)                        │
  │ ◄──────────────────────────────────│
  │                                    │
  │  OpaqueLoginStart(username,        │
  │    login_request)                  │
  │ ──────────────────────────────────►│
  │                                    │
  │  OpaqueLoginStartResponse(         │
  │    login_response)                 │
  │ ◄──────────────────────────────────│
  │                                    │
  │  OpaqueLoginFinish(username,       │
  │    finalization, identity_key)     │
  │ ──────────────────────────────────►│
  │                                    │
  │  OpaqueLoginFinishResponse(        │
  │    session_token)                  │
  │ ◄──────────────────────────────────│
```

The `session_token` is used for subsequent authenticated RPCs.

## Error Handling

The server returns protobuf-encoded error responses on the same stream. Error conditions include:

- Invalid method ID: stream reset
- Authentication failure: error response with details
- Rate limiting: error response with retry-after hint
- Internal errors: generic error response
