chore: rename project quicnprotochat -> quicproquo (binaries: qpq)

Rename the entire workspace:
- Crate packages: quicnprotochat-{core,proto,server,client,gui,p2p,mobile} -> quicproquo-*
- Binary names: quicnprotochat -> qpq, quicnprotochat-server -> qpq-server,
  quicnprotochat-gui -> qpq-gui
- Default files: *-state.bin -> qpq-state.bin, *-server.toml -> qpq-server.toml,
  *.db -> qpq.db
- Environment variable prefix: QUICNPROTOCHAT_* -> QPQ_*
- App identifier: chat.quicnproto.gui -> chat.quicproquo.gui
- Proto package: quicnprotochat.bench -> quicproquo.bench
- All documentation, Docker, CI, and script references updated

HKDF domain-separation strings and P2P ALPN remain unchanged for
backward compatibility with existing encrypted state and wire protocol.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-01 20:11:51 +01:00
parent 553de3a2b7
commit 853ca4fec0
152 changed files with 4070 additions and 788 deletions

View File

@@ -1,6 +1,6 @@
# Cap'n Proto Serialisation and RPC
quicnprotochat uses [Cap'n Proto](https://capnproto.org/) for both message serialisation and remote procedure calls. The serialisation layer encodes structured messages (Envelopes, Auth tokens, delivery payloads) into a compact binary format. The RPC layer provides the client-server interface for the Authentication Service, Delivery Service, and health checks -- all exposed through a single `NodeService` interface.
quicproquo uses [Cap'n Proto](https://capnproto.org/) for both message serialisation and remote procedure calls. The serialisation layer encodes structured messages (Envelopes, Auth tokens, delivery payloads) into a compact binary format. The RPC layer provides the client-server interface for the Authentication Service, Delivery Service, and health checks -- all exposed through a single `NodeService` interface.
This page covers why Cap'n Proto was chosen, how schemas are compiled, the owned `ParsedEnvelope` type, serialisation helpers, and ALPN integration with QUIC.
@@ -23,7 +23,7 @@ Cap'n Proto was selected for the following reasons:
3. **Canonical serialisation**: Cap'n Proto can produce deterministic byte representations of messages. This is critical for MLS, where Commits and KeyPackages must be signed -- the signature must cover exactly the same bytes that the verifier will see.
4. **Built-in async RPC**: The `capnp-rpc` crate provides a capability-based RPC system with promise pipelining. quicnprotochat uses it for the `NodeService` interface (KeyPackage upload/fetch, message enqueue/fetch, health checks, hybrid key operations). This avoids the need to hand-roll a request/response protocol.
4. **Built-in async RPC**: The `capnp-rpc` crate provides a capability-based RPC system with promise pipelining. quicproquo uses it for the `NodeService` interface (KeyPackage upload/fetch, message enqueue/fetch, health checks, hybrid key operations). This avoids the need to hand-roll a request/response protocol.
5. **Compact wire format**: Cap'n Proto's wire format is more compact than JSON or XML and comparable to Protocol Buffers, with the advantage of no decode step.
@@ -41,7 +41,7 @@ schemas/
### build.rs
The `quicnprotochat-proto` crate compiles these schemas at build time via `build.rs`:
The `quicproquo-proto` crate compiles these schemas at build time via `build.rs`:
```rust
capnpc::CompilerCommand::new()
@@ -63,7 +63,7 @@ Key details:
### Generated module inclusion
The generated code is spliced into the `quicnprotochat-proto` crate via `include!` macros:
The generated code is spliced into the `quicproquo-proto` crate via `include!` macros:
```rust
pub mod envelope_capnp {
@@ -84,7 +84,7 @@ Consumers import types from these modules. For example, `node_capnp::node_servic
## The Envelope schema
The `Envelope` is the top-level wire message for all quicnprotochat traffic. Every frame exchanged between peers is serialised as an Envelope:
The `Envelope` is the top-level wire message for all quicproquo traffic. Every frame exchanged between peers is serialised as an Envelope:
```capnp
struct Envelope {
@@ -114,7 +114,7 @@ The Delivery Service routes by `(groupId, msgType)` without inspecting `payload`
Cap'n Proto readers (`envelope_capnp::envelope::Reader`) borrow from the original byte buffer and cannot be sent across async task boundaries (`!Send`). This is a fundamental limitation of zero-copy reads.
To bridge this gap, `quicnprotochat-proto` defines `ParsedEnvelope`:
To bridge this gap, `quicproquo-proto` defines `ParsedEnvelope`:
```rust
pub struct ParsedEnvelope {
@@ -256,11 +256,11 @@ MessagePack is untyped -- there is no schema file, and type errors are caught at
FlatBuffers supports zero-copy reads (like Cap'n Proto) but lacks a built-in RPC framework. The ecosystem and tooling are also less mature for Rust.
## Design constraints of `quicnprotochat-proto`
## Design constraints of `quicproquo-proto`
The `quicnprotochat-proto` crate enforces three design constraints:
The `quicproquo-proto` crate enforces three design constraints:
1. **No crypto**: Key material never enters this crate. All encryption and signing happens in `quicnprotochat-core`.
1. **No crypto**: Key material never enters this crate. All encryption and signing happens in `quicproquo-core`.
2. **No I/O**: Callers own the transport. This crate only converts between bytes and types.
3. **No async**: Pure synchronous data-layer code. Async is the caller's responsibility.

View File

@@ -1,8 +1,8 @@
# Hybrid KEM: X25519 + ML-KEM-768
quicnprotochat implements a hybrid Key Encapsulation Mechanism that combines classical X25519 Diffie-Hellman with post-quantum ML-KEM-768 (FIPS 203). The hybrid construction ensures that the system remains secure even if one of the two components is broken: X25519 protects against failures in ML-KEM, and ML-KEM protects against quantum computers breaking X25519.
quicproquo implements a hybrid Key Encapsulation Mechanism that combines classical X25519 Diffie-Hellman with post-quantum ML-KEM-768 (FIPS 203). The hybrid construction ensures that the system remains secure even if one of the two components is broken: X25519 protects against failures in ML-KEM, and ML-KEM protects against quantum computers breaking X25519.
The implementation lives in `quicnprotochat-core/src/hybrid_kem.rs`. It is fully implemented and tested but **not yet integrated into the MLS ciphersuite** -- integration is planned for the M5 milestone. Currently, the module can be used as a standalone envelope encryption layer to wrap MLS payloads in an outer post-quantum-resistant encryption before they transit the network.
The implementation lives in `quicproquo-core/src/hybrid_kem.rs`. It is fully implemented and tested but **not yet integrated into the MLS ciphersuite** -- integration is planned for the M5 milestone. Currently, the module can be used as a standalone envelope encryption layer to wrap MLS payloads in an outer post-quantum-resistant encryption before they transit the network.
## Design approach
@@ -70,8 +70,8 @@ The two shared secrets are combined via HKDF-SHA256 with domain separation:
ikm = X25519_shared_secret(32 bytes) || ML-KEM_shared_secret(32 bytes)
salt = [] (empty)
key = HKDF-SHA256(salt, ikm, info="quicnprotochat-hybrid-v1", L=32)
nonce = HKDF-SHA256(salt, ikm, info="quicnprotochat-hybrid-nonce-v1", L=12)
key = HKDF-SHA256(salt, ikm, info="quicproquo-hybrid-v1", L=32)
nonce = HKDF-SHA256(salt, ikm, info="quicproquo-hybrid-nonce-v1", L=12)
```
The implementation in `derive_aead_material()`:
@@ -85,10 +85,10 @@ fn derive_aead_material(x25519_ss: &[u8], mlkem_ss: &[u8]) -> (Key, Nonce) {
let hk = Hkdf::<Sha256>::new(None, &ikm);
let mut key_bytes = Zeroizing::new([0u8; 32]);
hk.expand(b"quicnprotochat-hybrid-v1", &mut *key_bytes).unwrap();
hk.expand(b"quicproquo-hybrid-v1", &mut *key_bytes).unwrap();
let mut nonce_bytes = [0u8; 12];
hk.expand(b"quicnprotochat-hybrid-nonce-v1", &mut nonce_bytes).unwrap();
hk.expand(b"quicproquo-hybrid-nonce-v1", &mut nonce_bytes).unwrap();
(*Key::from_slice(&*key_bytes), *Nonce::from_slice(&nonce_bytes))
}
@@ -273,7 +273,7 @@ The AEAD nonce is derived deterministically from the shared secrets via HKDF. Si
## Further reading
- [Post-Quantum Readiness](../cryptography/post-quantum-readiness.md) -- Broader discussion of quicnprotochat's PQ strategy.
- [Post-Quantum Readiness](../cryptography/post-quantum-readiness.md) -- Broader discussion of quicproquo's PQ strategy.
- [MLS (RFC 9420)](mls.md) -- The MLS layer that the hybrid KEM will wrap.
- [Key Lifecycle and Zeroization](../cryptography/key-lifecycle.md) -- How hybrid key material is managed and cleared.
- [Threat Model](../cryptography/threat-model.md) -- Where hybrid KEM fits in the overall threat model.

View File

@@ -1,8 +1,8 @@
# MLS (RFC 9420)
The Messaging Layer Security protocol (RFC 9420) is the core cryptographic layer in quicnprotochat. It provides authenticated group key agreement with forward secrecy and post-compromise security -- properties that distinguish quicnprotochat from a simple transport-encrypted relay. This is the most detailed page in the Protocol Deep Dives section because MLS is the most complex layer in the stack.
The Messaging Layer Security protocol (RFC 9420) is the core cryptographic layer in quicproquo. It provides authenticated group key agreement with forward secrecy and post-compromise security -- properties that distinguish quicproquo from a simple transport-encrypted relay. This is the most detailed page in the Protocol Deep Dives section because MLS is the most complex layer in the stack.
The implementation lives in `quicnprotochat-core/src/group.rs` and `quicnprotochat-core/src/keystore.rs`, using the `openmls 0.5` crate.
The implementation lives in `quicproquo-core/src/group.rs` and `quicproquo-core/src/keystore.rs`, using the `openmls 0.5` crate.
## Background: what problem MLS solves
@@ -21,7 +21,7 @@ MLS takes a fundamentally different approach: it uses a **ratchet tree** (a bina
## Ciphersuite
quicnprotochat uses:
quicproquo uses:
```text
MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519
@@ -38,7 +38,7 @@ This ciphersuite provides 128-bit classical security. Post-quantum protection is
## The `GroupMember` state machine
The central type is `GroupMember`, defined in `quicnprotochat-core/src/group.rs`. It wraps an openmls `MlsGroup`, a persistent crypto backend (`StoreCrypto`), and the user's long-term Ed25519 identity keypair.
The central type is `GroupMember`, defined in `quicproquo-core/src/group.rs`. It wraps an openmls `MlsGroup`, a persistent crypto backend (`StoreCrypto`), and the user's long-term Ed25519 identity keypair.
### Lifecycle diagram
@@ -135,7 +135,7 @@ pub fn create_group(&mut self, group_id: &[u8]) -> Result<(), CoreError>
Creates a new MLS group at epoch 0 with the caller as the sole member.
**Parameters:**
- `group_id`: Any non-empty byte string. By convention, quicnprotochat uses the SHA-256 digest of a human-readable group name.
- `group_id`: Any non-empty byte string. By convention, quicproquo uses the SHA-256 digest of a human-readable group name.
**What happens internally:**
@@ -259,7 +259,7 @@ Processes an incoming TLS-encoded MLS message.
## The `StoreCrypto` backend
The `StoreCrypto` struct (in `quicnprotochat-core/src/keystore.rs`) implements `OpenMlsCryptoProvider`, which openmls requires for all cryptographic operations:
The `StoreCrypto` struct (in `quicproquo-core/src/keystore.rs`) implements `OpenMlsCryptoProvider`, which openmls requires for all cryptographic operations:
```rust
pub struct StoreCrypto {
@@ -318,11 +318,11 @@ KeyPackageIn::tls_deserialize(&mut bytes.as_ref())?
### Feature-gated methods
Several convenient methods (`into_welcome()`, `into_protocol_message()`) are feature-gated behind openmls feature flags that quicnprotochat does not enable. The workaround is to use `msg_in.extract()` and pattern-match on the `MlsMessageInBody` enum variants.
Several convenient methods (`into_welcome()`, `into_protocol_message()`) are feature-gated behind openmls feature flags that quicproquo does not enable. The workaround is to use `msg_in.extract()` and pattern-match on the `MlsMessageInBody` enum variants.
### MlsGroup is not Send
`MlsGroup` holds internal state that may not be `Send` depending on the crypto backend. In quicnprotochat, `StoreCrypto` uses `RwLock` (which is `Send + Sync`), so `GroupMember` is `Send`. However, all MLS operations must use the same backend instance, so `GroupMember` should not be cloned across tasks.
`MlsGroup` holds internal state that may not be `Send` depending on the crypto backend. In quicproquo, `StoreCrypto` uses `RwLock` (which is `Send + Sync`), so `GroupMember` is `Send`. However, all MLS operations must use the same backend instance, so `GroupMember` should not be cloned across tasks.
## Ratchet tree embedding
@@ -335,7 +335,7 @@ The trade-off:
- **Pro**: No need for a separate tree distribution service or additional round-trips.
- **Con**: Welcome messages grow with the group size (O(n log n) for a balanced tree of n members).
For quicnprotochat's target group sizes (2-100 members), this trade-off is acceptable.
For quicproquo's target group sizes (2-100 members), this trade-off is acceptable.
## Wire format
@@ -386,7 +386,7 @@ The following sequence shows a complete Alice-and-Bob scenario, matching the `tw
## Credential model
quicnprotochat uses MLS `Basic` credentials. The credential body is the raw Ed25519 public key bytes (32 bytes), and the `signature_key` is the same public key:
quicproquo uses MLS `Basic` credentials. The credential body is the raw Ed25519 public key bytes (32 bytes), and the `signature_key` is the same public key:
```rust
let credential = Credential::new(

View File

@@ -1,6 +1,6 @@
# Protocol Layers Overview
quicnprotochat composes four distinct protocol layers into a single security stack. Each layer addresses a specific class of threat and delegates everything else to the layers above or below it. No single layer is sufficient on its own; the composition is what delivers end-to-end confidentiality, server authentication, forward secrecy, post-compromise security, and post-quantum resistance.
quicproquo composes four distinct protocol layers into a single security stack. Each layer addresses a specific class of threat and delegates everything else to the layers above or below it. No single layer is sufficient on its own; the composition is what delivers end-to-end confidentiality, server authentication, forward secrecy, post-compromise security, and post-quantum resistance.
This page provides a high-level comparison and a suggested reading order. The deep-dive pages that follow contain implementation details drawn directly from the source code.
@@ -47,7 +47,7 @@ The pages in this section are ordered to build understanding incrementally:
1. **[QUIC + TLS 1.3](quic-tls.md)** -- Start here. This is the transport layer that every client-server connection uses. Understanding QUIC stream multiplexing and the TLS 1.3 handshake is prerequisite to understanding how Cap'n Proto RPC rides on top.
2. **[MLS (RFC 9420)](mls.md)** -- The core cryptographic innovation. MLS provides the group key agreement that makes quicnprotochat an E2E encrypted group messenger rather than just a transport-encrypted relay. This is the longest and most detailed page.
2. **[MLS (RFC 9420)](mls.md)** -- The core cryptographic innovation. MLS provides the group key agreement that makes quicproquo an E2E encrypted group messenger rather than just a transport-encrypted relay. This is the longest and most detailed page.
3. **[Cap'n Proto Serialisation and RPC](capn-proto.md)** -- The serialisation and RPC layer that bridges MLS application data with the transport. Understanding the Envelope schema, the ParsedEnvelope owned type, and the NodeService RPC interface is essential for reading the server and client source code.
@@ -70,9 +70,9 @@ Each protocol layer maps to one or more workspace crates:
| Layer | Primary Crate | Source File(s) |
|---|---|---|
| QUIC + TLS 1.3 | `quicnprotochat-server`, `quicnprotochat-client` | `main.rs` (server and client entry points) |
| Cap'n Proto | `quicnprotochat-proto` | `src/lib.rs`, `build.rs`, `schemas/*.capnp` |
| MLS | `quicnprotochat-core` | `src/group.rs`, `src/keystore.rs` |
| Hybrid KEM | `quicnprotochat-core` | `src/hybrid_kem.rs` |
| QUIC + TLS 1.3 | `quicproquo-server`, `quicproquo-client` | `main.rs` (server and client entry points) |
| Cap'n Proto | `quicproquo-proto` | `src/lib.rs`, `build.rs`, `schemas/*.capnp` |
| MLS | `quicproquo-core` | `src/group.rs`, `src/keystore.rs` |
| Hybrid KEM | `quicproquo-core` | `src/hybrid_kem.rs` |
For a full crate responsibility breakdown, see [Crate Responsibilities](../architecture/crate-responsibilities.md).

View File

@@ -1,6 +1,6 @@
# QUIC + TLS 1.3
quicnprotochat uses QUIC (RFC 9000) with mandatory TLS 1.3 (RFC 9001) as its transport layer. This page explains how the `quinn` and `rustls` crates are integrated and what security properties the transport provides.
quicproquo uses QUIC (RFC 9000) with mandatory TLS 1.3 (RFC 9001) as its transport layer. This page explains how the `quinn` and `rustls` crates are integrated and what security properties the transport provides.
## Why QUIC
@@ -14,16 +14,16 @@ QUIC provides several advantages over traditional TCP-based transports:
## Crate integration
quicnprotochat uses the following crates for QUIC and TLS:
quicproquo uses the following crates for QUIC and TLS:
- **`quinn 0.11`** -- The async QUIC implementation for Tokio. Provides `Endpoint`, `Connection`, and bidirectional stream types.
- **`quinn-proto 0.11`** -- The protocol-level types, including `QuicServerConfig` and `QuicClientConfig` wrappers that bridge `rustls` into `quinn`.
- **`rustls 0.23`** -- The TLS implementation. quicnprotochat uses it in strict TLS 1.3 mode with no fallback to TLS 1.2.
- **`rustls 0.23`** -- The TLS implementation. quicproquo uses it in strict TLS 1.3 mode with no fallback to TLS 1.2.
- **`rcgen 0.13`** -- Self-signed certificate generation for development and testing.
### Server configuration
The server builds its QUIC endpoint configuration in `build_server_config()` (in `quicnprotochat-server/src/main.rs`):
The server builds its QUIC endpoint configuration in `build_server_config()` (in `quicproquo-server/src/main.rs`):
```rust
let mut tls = rustls::ServerConfig::builder_with_protocol_versions(&[&TLS13])
@@ -37,7 +37,7 @@ Ok(ServerConfig::with_crypto(Arc::new(crypto)))
Key points:
1. **TLS 1.3 strict mode**: `builder_with_protocol_versions(&[&TLS13])` ensures no TLS 1.2 fallback. This is a hard requirement: TLS 1.2 lacks the 0-RTT and full forward secrecy guarantees that quicnprotochat relies on.
1. **TLS 1.3 strict mode**: `builder_with_protocol_versions(&[&TLS13])` ensures no TLS 1.2 fallback. This is a hard requirement: TLS 1.2 lacks the 0-RTT and full forward secrecy guarantees that quicproquo relies on.
2. **No client certificate authentication**: `with_no_client_auth()` means the server does not verify client certificates at the TLS layer. Client authentication is handled at the application layer via Ed25519 identity keys and MLS credentials. This is a deliberate design choice -- MLS provides stronger authentication properties than TLS client certificates.
@@ -82,11 +82,11 @@ Because `capnp-rpc` uses `Rc<RefCell<>>` internally (making it `!Send`), all RPC
## Certificate trust model
quicnprotochat currently uses a **trust-on-first-use (TOFU)** model with self-signed certificates:
quicproquo currently uses a **trust-on-first-use (TOFU)** model with self-signed certificates:
1. On first start, the server generates a self-signed certificate using `rcgen::generate_simple_self_signed` with SANs for `localhost`, `127.0.0.1`, and `::1`.
2. The certificate and private key are persisted to disk as DER files (default: `data/server-cert.der` and `data/server-key.der`).
3. Clients must obtain the server's certificate file out-of-band and reference it via the `--ca-cert` flag or `QUICNPROTOCHAT_CA_CERT` environment variable.
3. Clients must obtain the server's certificate file out-of-band and reference it via the `--ca-cert` flag or `QPQ_CA_CERT` environment variable.
This model is adequate for development and single-server deployments. The roadmap includes:
@@ -136,18 +136,18 @@ The QUIC + TLS 1.3 layer provides:
| Environment Variable | CLI Flag | Default | Description |
|---|---|---|---|
| `QUICNPROTOCHAT_LISTEN` | `--listen` | `0.0.0.0:7000` | QUIC listen address |
| `QUICNPROTOCHAT_TLS_CERT` | `--tls-cert` | `data/server-cert.der` | TLS certificate path |
| `QUICNPROTOCHAT_TLS_KEY` | `--tls-key` | `data/server-key.der` | TLS private key path |
| `QUICNPROTOCHAT_DATA_DIR` | `--data-dir` | `data` | Persistent storage directory |
| `QPQ_LISTEN` | `--listen` | `0.0.0.0:7000` | QUIC listen address |
| `QPQ_TLS_CERT` | `--tls-cert` | `data/server-cert.der` | TLS certificate path |
| `QPQ_TLS_KEY` | `--tls-key` | `data/server-key.der` | TLS private key path |
| `QPQ_DATA_DIR` | `--data-dir` | `data` | Persistent storage directory |
### Client
| Environment Variable | CLI Flag | Default | Description |
|---|---|---|---|
| `QUICNPROTOCHAT_CA_CERT` | `--ca-cert` | `data/server-cert.der` | Server certificate to trust |
| `QUICNPROTOCHAT_SERVER_NAME` | `--server-name` | `localhost` | Expected TLS server name (must match certificate SAN) |
| `QUICNPROTOCHAT_SERVER` | `--server` | `127.0.0.1:7000` | Server address (per-subcommand) |
| `QPQ_CA_CERT` | `--ca-cert` | `data/server-cert.der` | Server certificate to trust |
| `QPQ_SERVER_NAME` | `--server-name` | `localhost` | Expected TLS server name (must match certificate SAN) |
| `QPQ_SERVER` | `--server` | `127.0.0.1:7000` | Server address (per-subcommand) |
## Further reading