docs: rewrite mdBook documentation for v2 architecture
Update 25+ files and add 6 new pages to reflect the v2 migration from Cap'n Proto to Protobuf framing over QUIC. Integrates SDK and Operations docs into the mdBook, restructures SUMMARY.md, and rewrites the wire format, architecture, and protocol sections with accurate v2 content.
This commit is contained in:
@@ -10,26 +10,27 @@ QUIC provides several advantages over traditional TCP-based transports:
|
||||
- **0-RTT resumption**: Returning clients can send data in the first flight, reducing connection setup latency.
|
||||
- **Integrated encryption**: TLS 1.3 is integral to the QUIC handshake; no extra round-trips for transport security.
|
||||
- **NAT traversal**: UDP-based; connection migration survives NAT rebinding.
|
||||
- **Ecosystem support**: `capnp-rpc` can use QUIC bidirectional streams directly via the `tokio-util` compat layer.
|
||||
- **Per-call concurrency**: The v2 RPC framework opens one bidirectional stream per RPC call. Multiple calls run concurrently without blocking each other.
|
||||
- **Push streams**: Server-to-client push events use QUIC uni-directional streams, avoiding any request-response overhead.
|
||||
|
||||
## Crate integration
|
||||
|
||||
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 0.11`** -- The async QUIC implementation for Tokio. Provides `Endpoint`, `Connection`, and bidirectional/uni-directional 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. 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 `quicproquo-server/src/main.rs`):
|
||||
The server builds its QUIC endpoint configuration with:
|
||||
|
||||
```rust
|
||||
let mut tls = rustls::ServerConfig::builder_with_protocol_versions(&[&TLS13])
|
||||
.with_no_client_auth()
|
||||
.with_single_cert(cert_chain, key)?;
|
||||
tls.alpn_protocols = vec![b"capnp".to_vec()];
|
||||
tls.alpn_protocols = vec![b"qpq".to_vec()];
|
||||
|
||||
let crypto = QuicServerConfig::try_from(tls)?;
|
||||
Ok(ServerConfig::with_crypto(Arc::new(crypto)))
|
||||
@@ -39,9 +40,9 @@ 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 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.
|
||||
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 OPAQUE password authentication and Ed25519 identity keys. This is a deliberate design choice -- OPAQUE provides stronger authentication properties than TLS client certificates without requiring PKI infrastructure.
|
||||
|
||||
3. **ALPN negotiation**: The Application-Layer Protocol Negotiation extension is set to `b"capnp"`, advertising that this endpoint speaks Cap'n Proto RPC. Both client and server must agree on this protocol identifier or the TLS handshake fails.
|
||||
3. **ALPN negotiation**: The Application-Layer Protocol Negotiation extension is set to `b"qpq"`, advertising that this endpoint speaks the quicproquo v2 Protobuf framing protocol. Both client and server must agree on this protocol identifier or the TLS handshake fails.
|
||||
|
||||
4. **`QuicServerConfig` bridge**: The `quinn-proto` crate provides `QuicServerConfig::try_from(tls)` to adapt the `rustls::ServerConfig` for use with QUIC. This handles the QUIC-specific TLS parameters (transport parameters, QUIC header protection keys) automatically.
|
||||
|
||||
@@ -53,10 +54,10 @@ The client performs the mirror operation. It loads the server's DER-encoded cert
|
||||
let mut roots = rustls::RootCertStore::empty();
|
||||
roots.add(CertificateDer::from(cert_bytes))?;
|
||||
|
||||
let tls = rustls::ClientConfig::builder_with_protocol_versions(&[&TLS13])
|
||||
let mut tls = rustls::ClientConfig::builder_with_protocol_versions(&[&TLS13])
|
||||
.with_root_certificates(roots)
|
||||
.with_no_client_auth();
|
||||
tls.alpn_protocols = vec![b"capnp".to_vec()];
|
||||
tls.alpn_protocols = vec![b"qpq".to_vec()];
|
||||
|
||||
let crypto = QuicClientConfig::try_from(tls)?;
|
||||
```
|
||||
@@ -65,20 +66,26 @@ The client trusts exactly one certificate: the server's self-signed cert loaded
|
||||
|
||||
### Per-connection handling
|
||||
|
||||
Each accepted QUIC connection spawns a handler task:
|
||||
The v2 server accepts connections and handles streams concurrently:
|
||||
|
||||
```rust
|
||||
let (send, recv) = connection.accept_bi().await?;
|
||||
let (reader, writer) = (recv.compat(), send.compat_write());
|
||||
// Accept a QUIC connection
|
||||
let connection = endpoint.accept().await?;
|
||||
|
||||
let network = twoparty::VatNetwork::new(reader, writer, Side::Server, Default::default());
|
||||
let service: node_service::Client = capnp_rpc::new_client(NodeServiceImpl { store, waiters });
|
||||
RpcSystem::new(Box::new(network), Some(service.client)).await?;
|
||||
// For each incoming bidirectional stream (one per RPC call):
|
||||
let (send, recv) = connection.accept_bi().await?;
|
||||
// Read RequestFrame, dispatch, write ResponseFrame
|
||||
tokio::spawn(handle_rpc(send, recv, server_state));
|
||||
|
||||
// For server-initiated push events:
|
||||
let send = connection.open_uni().await?;
|
||||
// Write PushFrame
|
||||
tokio::spawn(send_push(send, event));
|
||||
```
|
||||
|
||||
The `tokio-util` compat layer (`compat()` and `compat_write()`) converts Quinn's `RecvStream` and `SendStream` into types that implement `futures::AsyncRead` and `futures::AsyncWrite`, which `capnp-rpc`'s `VatNetwork` requires. The entire Cap'n Proto RPC system then runs over this single QUIC bidirectional stream.
|
||||
|
||||
Because `capnp-rpc` uses `Rc<RefCell<>>` internally (making it `!Send`), all RPC tasks run on a `tokio::task::LocalSet`. The server spawns each connection handler via `tokio::task::spawn_local`.
|
||||
Unlike the v1 Cap'n Proto RPC (which required `tokio::task::LocalSet` due to
|
||||
`!Send` internals), the v2 framework uses `Arc`-based shared state and
|
||||
`tokio::spawn` for full multi-threaded concurrency.
|
||||
|
||||
## Certificate trust model
|
||||
|
||||
@@ -126,9 +133,9 @@ The QUIC + TLS 1.3 layer provides:
|
||||
|
||||
### What TLS does *not* provide
|
||||
|
||||
- **Client authentication**: Handled by MLS identity credentials at the application layer. See [MLS (RFC 9420)](mls.md).
|
||||
- **End-to-end encryption**: TLS terminates at the server. The server can read the Cap'n Proto RPC framing and message routing metadata. Payload confidentiality is provided by MLS. See [MLS (RFC 9420)](mls.md).
|
||||
- **Post-quantum resistance**: TLS 1.3 key exchange uses classical ECDHE. Post-quantum protection of application data is provided by the [Hybrid KEM](hybrid-kem.md) layer (M5 milestone).
|
||||
- **Client authentication**: Handled by OPAQUE password authentication (methods 100-103) and Ed25519 identity keys at the application layer. See [Service Architecture](../architecture/service-architecture.md).
|
||||
- **End-to-end encryption**: TLS terminates at the server. The server can read the Protobuf framing and message routing metadata. Payload confidentiality is provided by MLS. See [MLS (RFC 9420)](mls.md).
|
||||
- **Post-quantum resistance**: TLS 1.3 key exchange uses classical ECDHE. Post-quantum protection of application data is provided by the [Hybrid KEM](hybrid-kem.md) layer.
|
||||
|
||||
## Configuration reference
|
||||
|
||||
@@ -136,7 +143,7 @@ The QUIC + TLS 1.3 layer provides:
|
||||
|
||||
| Environment Variable | CLI Flag | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `QPQ_LISTEN` | `--listen` | `0.0.0.0:7000` | QUIC listen address |
|
||||
| `QPQ_LISTEN` | `--listen` | `0.0.0.0:5001` | 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 |
|
||||
@@ -147,9 +154,9 @@ The QUIC + TLS 1.3 layer provides:
|
||||
|---|---|---|---|
|
||||
| `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) |
|
||||
| `QPQ_SERVER` | `--server` | `127.0.0.1:5001` | Server address (per-subcommand) |
|
||||
|
||||
## Further reading
|
||||
|
||||
- [Cap'n Proto Serialisation and RPC](capn-proto.md) -- The RPC layer that runs on top of QUIC streams.
|
||||
- [Service Architecture](../architecture/service-architecture.md) -- How the server's `NodeServiceImpl` binds to the QUIC endpoint.
|
||||
- [Protobuf Framing](capn-proto.md) -- The RPC framing layer that runs on top of QUIC streams.
|
||||
- [Service Architecture](../architecture/service-architecture.md) -- How the server binds to the QUIC endpoint and dispatches 44 RPC methods.
|
||||
|
||||
Reference in New Issue
Block a user