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
166 lines
4.4 KiB
Markdown
166 lines
4.4 KiB
Markdown
# quicprochat Python SDK
|
|
|
|
Python client library for the [quicprochat](https://github.com/nicholasgasior/quicprochat) E2E encrypted messenger.
|
|
|
|
## Prerequisites
|
|
|
|
- Python 3.10+
|
|
- A running quicprochat server
|
|
|
|
## Installation
|
|
|
|
```sh
|
|
pip install quicprochat
|
|
```
|
|
|
|
For development:
|
|
|
|
```sh
|
|
pip install -e ".[dev]"
|
|
```
|
|
|
|
## Transport Backends
|
|
|
|
### 1. Async QUIC (pure Python)
|
|
|
|
Uses [aioquic](https://github.com/aiortc/aioquic) for native QUIC transport with the v2 protobuf wire format. No Rust dependency required.
|
|
|
|
```python
|
|
import asyncio
|
|
from quicprochat import QpqClient, ConnectOptions
|
|
|
|
async def main():
|
|
client = await QpqClient.connect(ConnectOptions(
|
|
addr="127.0.0.1:5001",
|
|
ca_cert_path="ca.pem",
|
|
))
|
|
|
|
health = await client.health()
|
|
print(f"Server: {health.status}")
|
|
|
|
# OPAQUE auth (requires external OPAQUE library for crypto)
|
|
server_resp = await client.login_start("alice", opaque_request_bytes)
|
|
# ... process server_resp with OPAQUE library ...
|
|
token = await client.login_finish("alice", finalization, identity_key)
|
|
|
|
# Resolve a user
|
|
key, proof = await client.resolve_user("bob")
|
|
|
|
# Send a message
|
|
seq, proof = await client.send(recipient_key, b"hello")
|
|
|
|
# Receive messages (long-poll)
|
|
messages = await client.receive_wait(my_key, timeout_ms=5000)
|
|
for msg in messages:
|
|
print(f"[{msg.seq}] {msg.data}")
|
|
|
|
await client.close()
|
|
|
|
asyncio.run(main())
|
|
```
|
|
|
|
### 2. Rust FFI (synchronous)
|
|
|
|
Wraps `libquicprochat_ffi` via CFFI for full Rust crypto stack (MLS, hybrid KEM, OPAQUE) at native speed.
|
|
|
|
```sh
|
|
# Build the FFI library first
|
|
cargo build --release -p quicprochat-ffi
|
|
```
|
|
|
|
```python
|
|
from quicprochat import QpqClient, ConnectOptions
|
|
|
|
client = QpqClient.connect_ffi(ConnectOptions(
|
|
addr="127.0.0.1:5001",
|
|
ca_cert_path="ca.pem",
|
|
))
|
|
|
|
client.ffi_login("alice", "password123")
|
|
client.ffi_send("bob", b"hello from Python!")
|
|
|
|
messages = client.ffi_receive(timeout_ms=5000)
|
|
for msg in messages:
|
|
print(msg)
|
|
|
|
client.close_sync()
|
|
```
|
|
|
|
## API Reference
|
|
|
|
### Connection
|
|
|
|
| Method | Transport | Description |
|
|
|---|---|---|
|
|
| `QpqClient.connect(opts)` | QUIC | Async connect to server |
|
|
| `QpqClient.connect_ffi(opts)` | FFI | Sync connect via Rust FFI |
|
|
| `client.close()` | QUIC | Async disconnect |
|
|
| `client.close_sync()` | Both | Sync disconnect |
|
|
|
|
### Authentication (QUIC)
|
|
|
|
| Method | Description |
|
|
|---|---|
|
|
| `client.register_start(username, request)` | Start OPAQUE registration |
|
|
| `client.register_finish(username, upload, identity_key)` | Complete registration |
|
|
| `client.login_start(username, request)` | Start OPAQUE login |
|
|
| `client.login_finish(username, finalization, identity_key)` | Complete login |
|
|
| `client.set_session_token(token)` | Set pre-existing session token |
|
|
|
|
### Authentication (FFI)
|
|
|
|
| Method | Description |
|
|
|---|---|
|
|
| `client.ffi_login(username, password)` | Full OPAQUE login (Rust handles crypto) |
|
|
|
|
### Messaging (QUIC)
|
|
|
|
| Method | Description |
|
|
|---|---|
|
|
| `client.health()` | Server health check |
|
|
| `client.resolve_user(username)` | Look up identity key |
|
|
| `client.resolve_identity(key)` | Reverse look up username |
|
|
| `client.create_channel(peer_key)` | Create 1:1 DM channel |
|
|
| `client.send(recipient_key, payload)` | Send a message |
|
|
| `client.receive(recipient_key)` | Fetch queued messages |
|
|
| `client.receive_wait(recipient_key, timeout_ms=5000)` | Long-poll for messages |
|
|
| `client.ack(recipient_key, seq_up_to)` | Acknowledge messages |
|
|
| `client.upload_key_package(key, package)` | Upload MLS key package |
|
|
| `client.fetch_key_package(key)` | Fetch MLS key package |
|
|
| `client.delete_account()` | Permanently delete account |
|
|
|
|
### Messaging (FFI)
|
|
|
|
| Method | Description |
|
|
|---|---|
|
|
| `client.ffi_send(recipient, message)` | Send message by username |
|
|
| `client.ffi_receive(timeout_ms=5000)` | Receive pending messages |
|
|
|
|
## Wire Format
|
|
|
|
The SDK implements the qpc v2 wire format:
|
|
|
|
```
|
|
[method_id:u16][req_id:u32][len:u32][protobuf payload]
|
|
```
|
|
|
|
Each RPC is sent over its own QUIC bidirectional stream.
|
|
|
|
## Structure
|
|
|
|
- `quicprochat/client.py` -- High-level client API
|
|
- `quicprochat/transport.py` -- QUIC transport (aioquic)
|
|
- `quicprochat/ffi.py` -- Rust FFI transport (CFFI)
|
|
- `quicprochat/proto.py` -- Protobuf encode/decode (no codegen)
|
|
- `quicprochat/wire.py` -- v2 wire format framing
|
|
- `quicprochat/types.py` -- Data types and exceptions
|
|
- `examples/bot.py` -- Async echo bot example
|
|
- `examples/ffi_demo.py` -- Synchronous FFI example
|
|
|
|
## Running Tests
|
|
|
|
```sh
|
|
pip install -e ".[dev]"
|
|
pytest
|
|
```
|