Rename all project references from quicproquo/qpq to quicprochat/qpc across documentation, Docker configuration, CI workflows, packaging scripts, operational configs, and build tooling. - Docker: crate paths, binary names, user/group, data dirs, env vars - CI: workflow crate references, binary names, artifact names - Docs: all markdown files under docs/, SDK READMEs, book.toml - Packaging: OpenWrt Makefile, init script, UCI config (file renames) - Scripts: justfile, dev-shell, screenshot, cross-compile, ai_team - Operations: Prometheus config, alert rules, Grafana dashboard - Config: .env.example (QPQ_* → QPC_*), CODEOWNERS paths - Top-level: README, CONTRIBUTING, ROADMAP, CLAUDE.md
167 lines
5.4 KiB
Markdown
167 lines
5.4 KiB
Markdown
# Build Your Own SDK
|
|
|
|
This guide explains how to implement a quicprochat client SDK in any language.
|
|
|
|
## Two Approaches
|
|
|
|
### Approach 1: C FFI Wrapper (recommended)
|
|
|
|
The simplest path. Wrap `libquicprochat_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**: `qpc`
|
|
- **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/qpc/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 `qpc`
|
|
- [ ] **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/quicprochat-ffi/`): Synchronous wrapper
|
|
|
|
## Testing
|
|
|
|
Test your SDK against a local server:
|
|
|
|
```sh
|
|
# Start the server
|
|
cargo run -p quicprochat-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
|