Delete 8 Noise-specific documentation pages (noise-xx.md,
transport-keys.md, adr-001/003/006, framing-codec.md) and update
~30 remaining wiki pages to reflect QUIC+TLS as the sole transport.
Remove obsolete Noise-based integration tests (auth_service.rs,
mls_group.rs). Code-side Noise removal was done in f334ed3.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
227 lines
12 KiB
Markdown
227 lines
12 KiB
Markdown
# Crate Responsibilities
|
|
|
|
The quicnprotochat workspace is split into four crates with strict layering
|
|
rules. Each crate owns one concern and depends only on the crates below it.
|
|
This page documents what each crate provides, what it explicitly avoids, and
|
|
how the crates relate to one another.
|
|
|
|
---
|
|
|
|
## Dependency Flow Diagram
|
|
|
|
```text
|
|
┌──────────────────────────┐
|
|
│ quicnprotochat-client │
|
|
│ (CLI, QUIC client, │
|
|
│ GroupMember orchestr.) │
|
|
└─────────┬───────┬────────┘
|
|
│ │
|
|
┌───────┘ └────────┐
|
|
▼ ▼
|
|
┌────────────────────────┐ ┌────────────────────────┐
|
|
│ quicnprotochat-core │ │ quicnprotochat-server │
|
|
│ (crypto, MLS, │ │ (QUIC listener, │
|
|
│ hybrid KEM) │ │ NodeService RPC, │
|
|
│ │ │ storage) │
|
|
└──────────┬─────────────┘ └─────────┬──────────────┘
|
|
│ │
|
|
│ ┌───────────────────┘
|
|
▼ ▼
|
|
┌────────────────────────┐
|
|
│ quicnprotochat-proto │
|
|
│ (Cap'n Proto schemas, │
|
|
│ codegen, helpers) │
|
|
└────────────────────────┘
|
|
```
|
|
|
|
**Arrows point from dependant to dependency.** The proto crate sits at the base
|
|
of the dependency graph. The core crate depends on proto for envelope
|
|
serialisation. The server and client crates both depend on core and proto.
|
|
|
|
---
|
|
|
|
## quicnprotochat-core
|
|
|
|
**Role:** Pure cryptographic logic. No network I/O. No async runtime
|
|
dependency.
|
|
|
|
### Modules
|
|
|
|
| Module | Public API | Description |
|
|
|---------------|-----------------------------------------------------------------------------|-------------|
|
|
| `identity` | `IdentityKeypair` | Ed25519 signing keypair for MLS credentials. Seed stored as `Zeroizing<[u8; 32]>`. Implements `openmls_traits::Signer`. |
|
|
| `group` | `GroupMember` | MLS group state machine wrapping `openmls::MlsGroup`. Lifecycle: `new` -> `generate_key_package` -> `create_group` / `join_group` -> `send_message` / `receive_message`. |
|
|
| `keypackage` | `generate_key_package` | Standalone KeyPackage generation (returns TLS-encoded bytes + SHA-256 fingerprint). |
|
|
| `keystore` | `DiskKeyStore`, `StoreCrypto` | `OpenMlsKeyStore` implementation backed by an in-memory `HashMap` with optional bincode flush to disk. `StoreCrypto` couples `RustCrypto` + `DiskKeyStore` into an `OpenMlsCryptoProvider`. |
|
|
| `hybrid_kem` | `HybridKeypair`, `HybridPublicKey`, `hybrid_encrypt`, `hybrid_decrypt` | X25519 + ML-KEM-768 hybrid KEM. HKDF-SHA256 key derivation, ChaCha20-Poly1305 AEAD. Versioned envelope wire format. |
|
|
| `error` | `CoreError`, `MAX_PLAINTEXT_LEN` | Unified error types. `CoreError` covers Cap'n Proto, MLS, and hybrid KEM failures. |
|
|
|
|
### What this crate does NOT do
|
|
|
|
- No network I/O.
|
|
- No QUIC or TLS -- that is the server and client crates' concern.
|
|
- No async runtime setup (it uses Tokio types internally but does not spawn or
|
|
manage a runtime).
|
|
- No CLI parsing.
|
|
|
|
### Key dependencies
|
|
|
|
`ed25519-dalek`, `openmls`, `openmls_rust_crypto`,
|
|
`openmls_traits`, `tls_codec`, `ml-kem`, `x25519-dalek`, `chacha20poly1305`,
|
|
`hkdf`, `sha2`, `zeroize`, `capnp`, `quicnprotochat-proto`, `tokio`,
|
|
`serde`, `bincode`, `serde_json`, `thiserror`.
|
|
|
|
---
|
|
|
|
## quicnprotochat-proto
|
|
|
|
**Role:** Cap'n Proto schema definitions, compile-time code generation, and
|
|
pure-synchronous serialisation helpers. This crate is the single source of truth
|
|
for the wire format.
|
|
|
|
### Contents
|
|
|
|
| Item | Description |
|
|
|---------------------------|-------------|
|
|
| `schemas/envelope.capnp` | `Envelope` struct and `MsgType` enum -- top-level wire message. |
|
|
| `schemas/auth.capnp` | `AuthenticationService` interface -- `uploadKeyPackage`, `fetchKeyPackage`. |
|
|
| `schemas/delivery.capnp` | `DeliveryService` interface -- `enqueue`, `fetch`. |
|
|
| `schemas/node.capnp` | `NodeService` interface (unified AS+DS) -- all RPC methods plus `Auth` struct. |
|
|
| `build.rs` | Invokes `capnpc` to generate Rust types from the four `.capnp` files. |
|
|
| `lib.rs` | `pub mod envelope_capnp`, `auth_capnp`, `delivery_capnp`, `node_capnp` -- re-exports generated modules. |
|
|
| `MsgType` | Re-exported enum from `envelope_capnp::envelope::MsgType`. |
|
|
| `ParsedEnvelope` | Owned, `Send + 'static` representation of a decoded `Envelope`. All byte fields are eagerly copied out of the Cap'n Proto reader. |
|
|
| `build_envelope` | Serialise a `ParsedEnvelope` to unpacked Cap'n Proto wire bytes. |
|
|
| `parse_envelope` | Deserialise wire bytes into a `ParsedEnvelope`. |
|
|
| `to_bytes` / `from_bytes` | Low-level Cap'n Proto message <-> byte conversions. |
|
|
|
|
### What this crate does NOT do
|
|
|
|
- **No crypto** -- key material never enters this crate.
|
|
- **No I/O** -- callers own the transport; this crate only converts bytes to
|
|
types and back.
|
|
- **No async** -- pure synchronous data-layer code.
|
|
|
|
### Key dependencies
|
|
|
|
`capnp` (runtime), `capnpc` (build-time only).
|
|
|
|
---
|
|
|
|
## quicnprotochat-server
|
|
|
|
**Role:** Network-facing server binary. Accepts QUIC + TLS 1.3 connections,
|
|
dispatches Cap'n Proto RPC calls to `NodeServiceImpl`, and persists state to
|
|
disk via `FileBackedStore`.
|
|
|
|
### Components
|
|
|
|
| Component | Description |
|
|
|----------------------|-------------|
|
|
| `NodeServiceImpl` | Implements `node_service::Server` (Cap'n Proto generated trait). Handles all eight RPC methods: `uploadKeyPackage`, `fetchKeyPackage`, `enqueue`, `fetch`, `fetchWait`, `health`, `uploadHybridKey`, `fetchHybridKey`. |
|
|
| `FileBackedStore` | Mutex-guarded `HashMap`s for KeyPackages (keyed by Ed25519 public key), delivery queues (keyed by `ChannelKey = (channelId, recipientKey)`), and hybrid public keys. Each mutation flushes the full map to a bincode file on disk. |
|
|
| `DashMap` waiters | `DashMap<Vec<u8>, Arc<Notify>>` -- per-recipient `tokio::sync::Notify` instances for `fetchWait` long-polling. `enqueue` calls `notify_waiters()` after appending. |
|
|
| TLS config | Self-signed certificate auto-generated on first run (`rcgen`). TLS 1.3 only, ALPN `capnp`. |
|
|
| CLI (`clap`) | `--listen` (default `0.0.0.0:7000`), `--data-dir`, `--tls-cert`, `--tls-key`. |
|
|
|
|
### Connection lifecycle
|
|
|
|
```text
|
|
QUIC accept
|
|
└─ TLS 1.3 handshake (self-signed cert, ALPN "capnp")
|
|
└─ accept_bi() -> bidirectional QUIC stream
|
|
└─ tokio_util::compat adapters (AsyncRead/AsyncWrite)
|
|
└─ capnp-rpc twoparty::VatNetwork (Side::Server)
|
|
└─ RpcSystem drives NodeServiceImpl
|
|
```
|
|
|
|
Because `capnp-rpc` uses `Rc<RefCell<>>` internally and is therefore `!Send`,
|
|
the entire RPC stack runs on a `tokio::task::LocalSet`. Each incoming connection
|
|
is handled by `spawn_local`.
|
|
|
|
### What this crate does NOT do
|
|
|
|
- No direct crypto operations (it delegates to `quicnprotochat-core` types
|
|
for fingerprinting and storage only).
|
|
- No MLS processing -- all payloads are opaque byte strings.
|
|
|
|
### Key dependencies
|
|
|
|
`quicnprotochat-core`, `quicnprotochat-proto`, `quinn`, `quinn-proto`,
|
|
`rustls`, `rcgen`, `capnp`, `capnp-rpc`, `tokio`, `tokio-util`, `dashmap`,
|
|
`sha2`, `clap`, `tracing`, `anyhow`, `thiserror`, `bincode`, `serde`.
|
|
|
|
---
|
|
|
|
## quicnprotochat-client
|
|
|
|
**Role:** CLI client binary. Connects to the server over QUIC + TLS 1.3,
|
|
orchestrates MLS group operations via `GroupMember`, and persists identity and
|
|
group state to disk.
|
|
|
|
### Components
|
|
|
|
| Component | Description |
|
|
|-------------------------|-------------|
|
|
| `connect_node` | Establishes a QUIC/TLS connection, opens a bidirectional stream, and bootstraps a `capnp-rpc` `RpcSystem` to obtain a `node_service::Client`. |
|
|
| CLI subcommands (`clap`)| `ping`, `register`, `fetch-key`, `demo-group`, `register-state`, `create-group`, `invite`, `join`, `send`, `recv`. |
|
|
| `GroupMember` usage | The client creates a `GroupMember` (from `quicnprotochat-core`), calls `generate_key_package` / `create_group` / `add_member` / `join_group` / `send_message` / `receive_message`. |
|
|
| State persistence | `StoredState` holds `identity_seed` (32 bytes) and optional serialised `MlsGroup`. A companion `.ks` file stores the `DiskKeyStore` with HPKE init private keys. |
|
|
| Auth context | `ClientAuth` bundles an optional bearer token and device ID. Passed to every RPC via the `Auth` struct in `node.capnp`. |
|
|
|
|
### CLI subcommand summary
|
|
|
|
| Subcommand | What it does |
|
|
|-------------------|--------------|
|
|
| `ping` | Call `health()` and print RTT. |
|
|
| `register` | Generate a fresh identity + KeyPackage, upload to AS, print identity key. |
|
|
| `register-state` | Same as `register` but uses/creates persistent state file. |
|
|
| `fetch-key` | Fetch a peer's KeyPackage by hex identity key. |
|
|
| `create-group` | Create a new MLS group and save state. |
|
|
| `invite` | Fetch peer's KeyPackage, add to group, enqueue Welcome via DS. |
|
|
| `join` | Fetch Welcome from DS, join the MLS group. |
|
|
| `send` | Encrypt a message with MLS, enqueue via DS. |
|
|
| `recv` | Fetch pending payloads from DS, decrypt with MLS. Supports `--stream` for continuous long-polling. |
|
|
| `demo-group` | End-to-end Alice+Bob round-trip (ephemeral identities). |
|
|
|
|
### What this crate does NOT do
|
|
|
|
- No server-side logic.
|
|
- No direct crypto beyond calling `GroupMember` and verifying SHA-256
|
|
fingerprints.
|
|
|
|
### Key dependencies
|
|
|
|
`quicnprotochat-core`, `quicnprotochat-proto`, `quinn`, `quinn-proto`,
|
|
`rustls`, `capnp`, `capnp-rpc`, `tokio`, `tokio-util`, `clap`, `sha2`,
|
|
`serde`, `bincode`, `anyhow`, `thiserror`, `tracing`.
|
|
|
|
---
|
|
|
|
## Layering Rules
|
|
|
|
1. **proto** depends on nothing in-workspace. It is pure data definition.
|
|
2. **core** depends on **proto** (for `ParsedEnvelope` and envelope helpers).
|
|
It does not depend on server or client.
|
|
3. **server** depends on **core** and **proto**. It does not depend on client.
|
|
4. **client** depends on **core** and **proto**. It does not depend on server.
|
|
5. **server** and **client** never depend on each other. They communicate
|
|
exclusively via the Cap'n Proto RPC wire protocol.
|
|
|
|
This layering ensures that:
|
|
|
|
- Crypto code can be tested in isolation (`cargo test -p quicnprotochat-core`).
|
|
- Schema changes propagate automatically through codegen.
|
|
- The server binary contains no client-side MLS orchestration logic.
|
|
- The client binary contains no server-side storage or listener logic.
|
|
|
|
---
|
|
|
|
## Further Reading
|
|
|
|
- [Architecture Overview](overview.md) -- high-level system diagram
|
|
- [Service Architecture](service-architecture.md) -- NodeService RPC details
|
|
- [Wire Format Overview](../wire-format/overview.md) -- Cap'n Proto schema reference
|
|
- [GroupMember Lifecycle](../internals/group-member-lifecycle.md) -- MLS state machine details
|
|
- [Storage Backend](../internals/storage-backend.md) -- FileBackedStore internals
|