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
311 lines
9.7 KiB
Markdown
311 lines
9.7 KiB
Markdown
# Running the Client
|
|
|
|
The quicprochat CLI client provides subcommands for connectivity testing, identity registration, KeyPackage exchange, and persistent group messaging. All commands connect to the server over QUIC + TLS 1.3 and issue Cap'n Proto RPC calls against the `NodeService` endpoint.
|
|
|
|
---
|
|
|
|
## Global flags
|
|
|
|
These flags apply to every subcommand:
|
|
|
|
| Flag | Env var | Default | Purpose |
|
|
|---|---|---|---|
|
|
| `--ca-cert` | `QPC_CA_CERT` | `data/server-cert.der` | Path to the server's TLS certificate (DER format). The client uses this to verify the server's identity during the TLS handshake. |
|
|
| `--server-name` | `QPC_SERVER_NAME` | `localhost` | Expected TLS server name. Must match a SAN in the server's certificate. |
|
|
|
|
Most subcommands also accept `--server` (default `127.0.0.1:7000`) to specify the server address.
|
|
|
|
---
|
|
|
|
## Connectivity
|
|
|
|
### `ping`
|
|
|
|
Send a health probe to the server and print the round-trip time.
|
|
|
|
```bash
|
|
cargo run -p quicprochat-client -- ping
|
|
```
|
|
|
|
```bash
|
|
cargo run -p quicprochat-client -- ping --server 192.168.1.10:7000
|
|
```
|
|
|
|
**Output:**
|
|
```
|
|
health=ok rtt=3ms
|
|
```
|
|
|
|
This exercises the full QUIC + TLS 1.3 connection setup plus a single Cap'n Proto `health()` RPC call. Useful for verifying that the server is reachable and TLS verification succeeds.
|
|
|
|
---
|
|
|
|
## Ephemeral identity commands
|
|
|
|
These commands generate a fresh identity keypair in memory each time they run. The identity is not persisted and is discarded when the process exits. They are useful for quick tests and for the automated `demo-group` scenario.
|
|
|
|
### `register`
|
|
|
|
Generate a fresh Ed25519 identity, create an MLS KeyPackage, and upload it to the Authentication Service.
|
|
|
|
```bash
|
|
cargo run -p quicprochat-client -- register
|
|
```
|
|
|
|
**Output:**
|
|
```
|
|
identity_key : a1b2c3d4e5f6... (64 hex chars = 32 bytes)
|
|
fingerprint : 9f8e7d6c5b4a... (SHA-256 of the KeyPackage)
|
|
KeyPackage uploaded successfully.
|
|
```
|
|
|
|
Share the `identity_key` value with peers who want to add you to a group. They will pass it to `fetch-key` or `invite --peer-key`.
|
|
|
|
### `fetch-key <identity_key>`
|
|
|
|
Fetch a peer's KeyPackage from the Authentication Service by their Ed25519 public key.
|
|
|
|
```bash
|
|
cargo run -p quicprochat-client -- fetch-key a1b2c3d4e5f6...
|
|
```
|
|
|
|
The `identity_key` argument must be exactly 64 lowercase hex characters (32 bytes).
|
|
|
|
**Output (success):**
|
|
```
|
|
fingerprint : 9f8e7d6c5b4a...
|
|
package_len : 742 bytes
|
|
KeyPackage fetched successfully.
|
|
```
|
|
|
|
**Output (no KeyPackage available):**
|
|
```
|
|
No KeyPackage available for this identity.
|
|
```
|
|
|
|
KeyPackages are single-use: fetching a KeyPackage atomically removes it from the server. The server may also enforce a TTL (e.g. 24 hours) on stored KeyPackages. If the peer needs to be added to another group, or their KeyPackage expired, they must upload a new one (see `refresh-keypackage` below).
|
|
|
|
### `demo-group`
|
|
|
|
Run a complete Alice-and-Bob MLS round-trip against a live server. Both identities are created in-process; both communicate through the server's AS and DS.
|
|
|
|
```bash
|
|
cargo run -p quicprochat-client -- demo-group --server 127.0.0.1:7000
|
|
```
|
|
|
|
**Output:**
|
|
```
|
|
Alice -> Bob plaintext: hello bob
|
|
Bob -> Alice plaintext: hello alice
|
|
demo-group complete
|
|
```
|
|
|
|
This is the fastest way to verify that the entire stack (QUIC + TLS + Cap'n Proto RPC + MLS group operations + DS relay) is working end to end. For a detailed breakdown of what happens during `demo-group`, see the [Demo Walkthrough](demo-walkthrough.md).
|
|
|
|
---
|
|
|
|
## Persistent group commands
|
|
|
|
These commands use a state file (`--state`, default `qpc-state.bin`) to persist the Ed25519 identity seed and MLS group state between invocations. A companion key store file (same path with `.ks` extension) holds HPKE init private keys.
|
|
|
|
All persistent commands share the `--state` flag:
|
|
|
|
| Flag | Env var | Default |
|
|
|---|---|---|
|
|
| `--state` | `QPC_STATE` | `qpc-state.bin` |
|
|
| `--server` | `QPC_SERVER` | `127.0.0.1:7000` |
|
|
|
|
### `register-state`
|
|
|
|
Create or load a persistent identity, generate a KeyPackage, and upload it to the AS.
|
|
|
|
```bash
|
|
cargo run -p quicprochat-client -- register-state \
|
|
--state alice.bin \
|
|
--server 127.0.0.1:7000
|
|
```
|
|
|
|
If `alice.bin` does not exist, a new identity is generated and saved. If it already exists, the existing identity is loaded and a new KeyPackage is generated from it. You can run `register-state` again at any time to upload a fresh KeyPackage (e.g. after the previous one was consumed or expired). For refresh-only (no new identity), use `refresh-keypackage` instead.
|
|
|
|
**Output:**
|
|
```
|
|
identity_key : a1b2c3d4e5f6...
|
|
fingerprint : 9f8e7d6c5b4a...
|
|
KeyPackage uploaded successfully.
|
|
```
|
|
|
|
### `refresh-keypackage`
|
|
|
|
Refresh the KeyPackage on the server using your **existing** state file. Does not create a new identity. Use this when:
|
|
|
|
- Your KeyPackage has expired (server TTL, e.g. 24h).
|
|
- Your KeyPackage was consumed (someone invited you) and you want to be invitable again.
|
|
|
|
Run with the same `--access-token` (or `QPC_ACCESS_TOKEN`) as for other commands.
|
|
|
|
```bash
|
|
cargo run -p quicprochat-client -- refresh-keypackage \
|
|
--state alice.bin \
|
|
--server 127.0.0.1:7000
|
|
```
|
|
|
|
**Output:**
|
|
```
|
|
identity_key : a1b2c3d4e5f6...
|
|
fingerprint : 9f8e7d6c5b4a...
|
|
KeyPackage uploaded successfully.
|
|
```
|
|
|
|
If you are told "no key" when someone tries to invite you, have them wait and run `refresh-keypackage`, then try the invite again.
|
|
|
|
### `create-group`
|
|
|
|
Create a new MLS group. The caller becomes the sole member at epoch 0.
|
|
|
|
```bash
|
|
cargo run -p quicprochat-client -- create-group \
|
|
--state alice.bin \
|
|
--group-id "project-chat"
|
|
```
|
|
|
|
**Output:**
|
|
```
|
|
group created: project-chat
|
|
```
|
|
|
|
The group state is saved to the state file. You can now invite peers with `invite`.
|
|
|
|
### `invite`
|
|
|
|
Fetch a peer's KeyPackage from the AS, add them to the group, and deliver the Welcome message via the DS.
|
|
|
|
```bash
|
|
cargo run -p quicprochat-client -- invite \
|
|
--state alice.bin \
|
|
--peer-key b9a8c7d6e5f4... \
|
|
--server 127.0.0.1:7000
|
|
```
|
|
|
|
This command performs three operations in sequence:
|
|
|
|
1. Fetches the peer's KeyPackage from the AS (`fetchKeyPackage` RPC).
|
|
2. Calls `add_member()` on the local MLS group, producing a Commit and a Welcome.
|
|
3. Enqueues the Welcome to the DS for the peer's identity key (`enqueue` RPC).
|
|
|
|
**Output:**
|
|
```
|
|
invited peer (welcome queued)
|
|
```
|
|
|
|
### `join`
|
|
|
|
Join a group by consuming a Welcome message from the DS.
|
|
|
|
```bash
|
|
cargo run -p quicprochat-client -- join \
|
|
--state bob.bin \
|
|
--server 127.0.0.1:7000
|
|
```
|
|
|
|
The command fetches all pending messages for the local identity from the DS and expects to find a Welcome. The Welcome is processed by `MlsGroup::new_from_welcome()`, which decrypts it using the HPKE init private key stored in the key store.
|
|
|
|
**Output:**
|
|
```
|
|
joined group successfully
|
|
```
|
|
|
|
### `send`
|
|
|
|
Encrypt and send an application message to a peer via the DS.
|
|
|
|
```bash
|
|
cargo run -p quicprochat-client -- send \
|
|
--state alice.bin \
|
|
--peer-key b9a8c7d6e5f4... \
|
|
--msg "hello from alice" \
|
|
--server 127.0.0.1:7000
|
|
```
|
|
|
|
The message is encrypted as an MLS `PrivateMessage` using the current epoch's key schedule, then enqueued to the DS for the specified recipient.
|
|
|
|
**Output:**
|
|
```
|
|
message sent
|
|
```
|
|
|
|
### `recv`
|
|
|
|
Receive and decrypt all pending messages from the DS.
|
|
|
|
```bash
|
|
cargo run -p quicprochat-client -- recv \
|
|
--state bob.bin \
|
|
--server 127.0.0.1:7000
|
|
```
|
|
|
|
**Output:**
|
|
```
|
|
[0] plaintext: hello from alice
|
|
```
|
|
|
|
Additional flags:
|
|
|
|
| Flag | Default | Purpose |
|
|
|---|---|---|
|
|
| `--wait-ms` | `0` | Long-poll timeout in milliseconds. If no messages are queued, wait up to this long before returning. Uses the `fetchWait` RPC. |
|
|
| `--stream` | `false` | Continuously long-poll for messages. The process will not exit until interrupted. |
|
|
|
|
```bash
|
|
# Wait up to 5 seconds for messages
|
|
cargo run -p quicprochat-client -- recv \
|
|
--state bob.bin \
|
|
--wait-ms 5000
|
|
|
|
# Stream messages continuously
|
|
cargo run -p quicprochat-client -- recv \
|
|
--state bob.bin \
|
|
--stream --wait-ms 10000
|
|
```
|
|
|
|
---
|
|
|
|
## HPKE init key lifecycle warning
|
|
|
|
The MLS protocol requires that the HPKE init private key generated during KeyPackage creation is available when processing the corresponding Welcome message. In quicprochat, this private key is stored in the key store file (`.ks` extension alongside the state file).
|
|
|
|
**The same state file and key store must be used for both `register-state` and `join`.** If you:
|
|
|
|
- Run `register-state` with `--state bob.bin` (which generates `bob.ks`)
|
|
- Delete or move `bob.ks` before running `join`
|
|
- Or use a different `--state` path for `join`
|
|
|
|
...then `join` will fail because the HPKE init private key cannot be found.
|
|
|
|
In ephemeral mode (`register` and `demo-group`), the key is held in process memory and is only valid for the lifetime of that process.
|
|
|
|
---
|
|
|
|
## Command reference summary
|
|
|
|
| Command | Persistent? | Description |
|
|
|---|---|---|
|
|
| `ping` | No | Health check, prints RTT |
|
|
| `register` | No | Generate ephemeral identity + KeyPackage, upload to AS |
|
|
| `fetch-key <hex>` | No | Fetch a peer's KeyPackage from AS |
|
|
| `demo-group` | No | Automated Alice-and-Bob round-trip |
|
|
| `register-state` | Yes | Upload KeyPackage for persistent identity (creates identity if needed) |
|
|
| `refresh-keypackage` | Yes | Upload a fresh KeyPackage from existing state (no new identity) |
|
|
| `create-group` | Yes | Create MLS group (sole member, epoch 0) |
|
|
| `invite` | Yes | Add peer to group, deliver Welcome via DS |
|
|
| `join` | Yes | Consume Welcome from DS, join group |
|
|
| `send` | Yes | Encrypt and enqueue application message via DS |
|
|
| `recv` | Yes | Fetch, decrypt, and display pending messages |
|
|
|
|
---
|
|
|
|
## Next steps
|
|
|
|
- [Demo Walkthrough](demo-walkthrough.md) -- step-by-step narrative with two terminals
|
|
- [Running the Server](running-the-server.md) -- server configuration and TLS setup
|
|
- [MLS (RFC 9420)](../protocol-layers/mls.md) -- how MLS group operations work under the hood
|