# Build Your Own SDK

This guide explains how to implement a quicproquo client SDK in any language.

## Two Approaches

### Approach 1: C FFI Wrapper (recommended)

The simplest path. Wrap `libquicproquo_ffi` using your language's FFI mechanism (e.g., Python CFFI, Ruby FFI, JNI, Swift C interop).

**Pros**: Full Rust crypto stack (MLS, OPAQUE, hybrid KEM) with zero reimplementation.
**Cons**: Requires shipping a native library, synchronous API only.

See [C FFI docs](c-ffi.md) for the function signatures.

### Approach 2: Native QUIC + Protobuf

Implement the QUIC transport and protobuf serialization natively in your language.

**Pros**: No Rust dependency, full async support, smaller binary.
**Cons**: Must handle crypto externally (OPAQUE, MLS key packages).

## Native Implementation Checklist

### 1. QUIC Transport

Open a QUIC connection to the server:

- **ALPN**: `qpq`
- **TLS**: 1.3 with server certificate verification
- **Port**: 5001 (default)

Libraries by language:
| Language | Library |
|---|---|
| Go | `github.com/quic-go/quic-go` |
| Python | `aioquic` |
| Java/Kotlin | `netty-quic` or `Kwik` |
| Swift | `Network.framework` (built-in) |
| C/C++ | `quiche`, `msquic`, or `ngtcp2` |
| Rust | `quinn` |

### 2. Wire Framing

Each RPC uses a dedicated QUIC bidirectional stream with a 10-byte header:

```
[method_id:u16][req_id:u32][payload_len:u32][protobuf bytes]
```

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

Implementation:

```python
# Encode
header = struct.pack("!HII", method_id, req_id, len(payload))
frame = header + payload

# Decode
method_id, req_id, length = struct.unpack("!HII", data[:10])
payload = data[10 : 10 + length]
```

### 3. Protobuf Messages

Generate or hand-write protobuf encode/decode for the message types in `proto/qpq/v1/*.proto`.

Minimum required messages for a basic client:

| Proto file | Messages |
|---|---|
| `auth.proto` | `OpaqueLoginStart{Request,Response}`, `OpaqueLoginFinish{Request,Response}` |
| `delivery.proto` | `EnqueueRequest`, `EnqueueResponse`, `FetchRequest`, `FetchResponse` |
| `user.proto` | `ResolveUserRequest`, `ResolveUserResponse` |
| `channel.proto` | `CreateChannelRequest`, `CreateChannelResponse` |
| `p2p.proto` | `HealthRequest`, `HealthResponse` |
| `common.proto` | `DeleteAccountRequest`, `DeleteAccountResponse` |

### 4. RPC Flow

```
Client                              Server
  │                                    │
  │  1. Open QUIC bidirectional stream │
  │  2. Send framed request            │
  │  3. Close send side (end-stream)   │
  │ ──────────────────────────────────►│
  │                                    │
  │  4. Read framed response           │
  │  5. Close receive side             │
  │ ◄──────────────────────────────────│
```

### 5. Implementation Checklist

Core operations (implement in order):

- [ ] **Connect**: Open QUIC connection with TLS 1.3 + ALPN `qpq`
- [ ] **Health**: Send `HealthRequest` (method 802), verify server is running
- [ ] **Register**: OPAQUE registration (methods 100-101)
- [ ] **Login**: OPAQUE login (methods 102-103), store session token
- [ ] **Upload Key Package**: Upload MLS key package (method 300)
- [ ] **Resolve User**: Look up peer's identity key (method 500)
- [ ] **Create Channel**: Create DM channel with peer (method 400)
- [ ] **Send**: Enqueue encrypted message (method 200)
- [ ] **Receive**: Fetch pending messages (method 201 or 202 for long-poll)
- [ ] **Ack**: Acknowledge received messages (method 204)
- [ ] **Disconnect**: Close QUIC connection gracefully

### 6. OPAQUE Authentication

OPAQUE is a 4-message protocol. You need an OPAQUE library for your language:

| Language | Library |
|---|---|
| Rust | `opaque-ke` |
| Go | `github.com/cloudflare/circl/opaque` |
| Python | `opaque` (pure Python) or bind to Rust via FFI |
| JavaScript | `@nicholasgasior/opaque-wasm` or FFI |

The SDK only needs to transport the OPAQUE bytes -- the crypto is handled by the OPAQUE library. The flow is:

1. Generate `RegistrationRequest` locally
2. Send via `OpaqueRegisterStart` (method 100)
3. Process server's `RegistrationResponse` locally to produce `Upload`
4. Send via `OpaqueRegisterFinish` (method 101)
5. Generate `LoginRequest` locally
6. Send via `OpaqueLoginStart` (method 102)
7. Process server's `LoginResponse` locally to produce `Finalization`
8. Send via `OpaqueLoginFinish` (method 103)
9. Store the returned `session_token`

### 7. Error Handling

The server returns protobuf-encoded error responses. Your SDK should:

- Distinguish authentication errors from transport errors
- Implement timeouts on all RPC calls
- Handle QUIC connection loss and reconnection

## Reference Implementations

Study these SDKs for patterns:

- **Go** (`sdks/go/`): Native QUIC + Cap'n Proto, full OPAQUE flow
- **Python** (`sdks/python/`): Native QUIC + Protobuf v2 wire format
- **TypeScript** (`sdks/typescript/`): WebSocket bridge, WASM crypto
- **C FFI** (`crates/quicproquo-ffi/`): Synchronous wrapper

## Testing

Test your SDK against a local server:

```sh
# Start the server
cargo run -p quicproquo-server

# Run your SDK's tests
```

Verify at minimum:
1. Health check returns status "ok"
2. OPAQUE register + login succeeds
3. Send + receive roundtrip works
4. Error handling for invalid credentials
