# quinn — Repo Map (QUIC core)

## Brief refs: [^41][^42][^43]

quinn is the dominant pure-Rust QUIC stack, depended on by iroh and the libp2p QUIC transport. RFC 9000 / 9001 / 9002 compliant, TLS 1.3 via rustls, ECN + GSO/GRO via `quinn-udp`. Used by SPT as the QUIC layer behind Rank 2 (iroh) and Rank 3 (libp2p quic-v1) options in the Brief.

All paths below are relative to `docs/research/inspiration/repos/quinn-rs__quinn/`.

## Workspace layout

- `quinn/` — high-level async API (the `Endpoint` / `Connection` / `SendStream` / `RecvStream` types most apps use). Owns runtime trait + Tokio/smol integration. Spawns IO driver tasks. v0.12.
- `quinn-proto/` — sans-io state machine. Deterministic, no networking, no clock. Implements packet parsing, frame encoding, congestion control, MTU discovery, CID rotation, path validation, TLS-via-rustls glue. v0.12.
- `quinn-udp/` — cross-platform UDP socket wrapper exposing ECN, GSO/GRO segmentation, `IP_PKTINFO` source-IP control, `IP_MTU_DISCOVER`. Per-OS files: `unix.rs`, `windows.rs`, `fallback.rs`. v0.6.

## Integration points for SPT

### Endpoint server + client

- `quinn/src/endpoint.rs:85` — `Endpoint::client(addr: SocketAddr)`. Dual-stack v6 attempted; sets `IPV6_V6ONLY=0`. Uses `default_runtime()` (Tokio).
- `quinn/src/endpoint.rs:127` — `Endpoint::server(config: ServerConfig, addr)`. Same socket, just adds `ServerConfig`.
- `quinn/src/endpoint.rs:147` — `Endpoint::new(config, server_config, std::net::UdpSocket, runtime)`. Use this to bring your own bound socket (required for hole punching — bind once, share with signaling).
- `quinn/src/endpoint.rs:161` — `Endpoint::new_with_abstract_socket(...)`. Wrap an `AsyncUdpSocket` for sidechannel sharing (e.g. STUN piggyback). The integration seam for QUIC-on-arbitrary-transport.
- `quinn/src/endpoint.rs:193` — `Endpoint::accept() -> Accept` future yielding `Incoming`.
- `quinn/src/endpoint.rs:213` — `Endpoint::connect(addr, server_name) -> Result<Connecting>`. `server_name` must match cert SAN.
- `quinn/src/endpoint.rs:234` — `Endpoint::connect_with(client_config, addr, server_name)` — per-connection TLS config.
- `quinn-proto/src/endpoint.rs:318` — sans-io `proto::Endpoint::connect(now, config, remote, server_name)`. Use if integrating with a non-Tokio loop.
- `quinn-proto/src/endpoint.rs:514` — `proto::Endpoint::accept(...)`.

### Bidirectional streams

- `quinn/src/connection.rs:314` — `Connection::open_bi() -> OpenBi` (future → `(SendStream, RecvStream)`).
- `quinn/src/connection.rs:297` — `Connection::open_uni()`.
- `quinn/src/connection.rs:339` — `Connection::accept_bi()`. Peer must write before this resolves.
- `quinn/src/connection.rs:322` — `Connection::accept_uni()`.
- `quinn/src/connection.rs:347` — `Connection::read_datagram()` / `Connection::send_datagram(...)` for unreliable QUIC DATAGRAMs (RFC 9221) — useful for SPT pulse/heartbeat.
- `quinn/src/send_stream.rs:59,72,99,184,207,224` — `write`, `write_all`, `write_chunks`, `finish`, `reset`, `set_priority`.
- `quinn/src/recv_stream.rs:76,172,260,275,316` — `read`, `read_chunk`, `read_to_end`, `stop`, `received_reset`.

### Hole-punching / simultaneous open

QUIC does not have a TCP-style "simultaneous open" handshake; the standard pattern is asymmetric (one side `connect`, other `accept`) on a shared, pre-punched UDP socket. quinn supports this via:

- `quinn/examples/single_socket.rs:25` — canonical "one UDP socket, many connections, server + client roles share it" example. Bind one `Endpoint`, run `accept()` and `connect()` from the same instance.
- `quinn/src/endpoint.rs:147` — `Endpoint::new(EndpointConfig, Some(server_config), std::net::UdpSocket, runtime)`. Pass an *already-bound, already-punched* `UdpSocket` (from STUN/coordinator). This is the hook.
- `quinn/examples/README.md:67-78` — Explicitly documents the hole-punching use case: "if you have a hole punched UDP socket… QUIC servers and clients can both operate on the same UDP socket."
- `quinn-proto/src/endpoint.rs:729` — comment: simultaneous-initial CID collision "will fail fast and may be retried by the application layer" — quinn does not auto-retry; SPT must orchestrate which side calls `connect` vs `accept` (e.g. lexicographic peer-id tiebreak).
- No native ICE / DCUTR. For libp2p-style DCUTR coordination, wrap quinn yourself or use the libp2p quic-v1 transport (which already does this).

### 0-RTT

- `quinn/src/connection.rs:134` — `Connecting::into_0rtt() -> Result<Connection, Self>`. Client side: returns Ok only if the client TLS config recognizes a cached server session. Server side: always Ok (becomes 0.5-RTT, sendable before client auth).
- `quinn/src/connection.rs:594` — `Connection::authenticated()` — await this before treating 0-RTT data as non-replay-safe.
- `quinn-proto/src/crypto/rustls.rs:306,351` — Quinn forces `enable_early_data = true` on its default `QuicClientConfig`. On a custom rustls config, you must set this explicitly.
- `quinn-proto/src/crypto/rustls.rs:487` — server side forces `max_early_data_size = u32::MAX` (QUIC mandates 0 or `u32::MAX`).
- `quinn/src/incoming.rs:27` — `Incoming::accept() -> Connecting`; chain `.into_0rtt()` for 0.5-RTT server data.
- Replay-safety: docs at `connection.rs:99-133`. Never use 0-RTT for non-idempotent ops.

### Connection migration (NAT rebind survival)

- `quinn-proto/src/config/mod.rs:219` — `ServerConfig.migration: bool` (default `true`). Allows clients to change source address mid-connection. Disable only for hardened deployments.
- `quinn-proto/src/config/mod.rs:292` — `ServerConfig::migration(value)` setter.
- `quinn-proto/src/connection/mod.rs:3097` — `Connection::migrate(now, remote)` — internal path-validation kicked off on new remote.
- `quinn-proto/src/connection/mod.rs:3100-3117` — NAT-rebinding heuristic: if only port changes (same IPv4 IP), preserve RTT/CCC state; else reset.
- `quinn-proto/src/connection/mod.rs:3138` — `Connection::local_address_changed()` — public API for *active* migration; call after local rebind.
- `quinn/src/endpoint.rs:269` — `Endpoint::rebind(std::net::UdpSocket)` — swap underlying socket on existing endpoint; existing connections receive `ConnectionEvent::Rebind`, incoming on old socket are lost.
- `quinn/src/endpoint.rs:279` — `Endpoint::rebind_abstract(Box<dyn AsyncUdpSocket>)`.
- `quinn/examples/client.rs:124-129` — demo of `endpoint.rebind(...)` mid-connection.
- `quinn-proto/src/connection/paths.rs` — `PathData`, path validation (PATH_CHALLENGE / PATH_RESPONSE), CID rotation on path change.
- `quinn-proto/src/config/transport.rs:268` — `TransportConfig::keep_alive_interval(...)` — set this to a value less than NAT timeout (e.g. 15s) to prevent silent connection death rather than relying on migration.
- `quinn-proto/src/config/transport.rs:102` — `max_idle_timeout(...)`.

### TLS config (rustls)

- `quinn-proto/src/crypto/rustls.rs:291` — `QuicClientConfig` (wraps `Arc<rustls::ClientConfig>`).
- `quinn-proto/src/crypto/rustls.rs:432` — `QuicServerConfig` (analog).
- `quinn-proto/src/crypto/rustls.rs:384` — `TryFrom<rustls::ClientConfig> for QuicClientConfig` — entry point if you build your own rustls config.
- `quinn/examples/client.rs:95-105` — full client TLS build: `rustls::ClientConfig::builder().with_root_certificates(roots).with_no_client_auth()`, then `QuicClientConfig::try_from`, then `quinn::ClientConfig::new(Arc::new(...))`.
- `quinn/examples/server.rs:122-131` — server TLS build: `rustls::ServerConfig::builder().with_no_client_auth().with_single_cert(certs, key)`, then `QuicServerConfig::try_from`, then `quinn::ServerConfig::with_crypto`.
- `quinn-proto/src/crypto.rs` — `crypto::ClientConfig` / `crypto::ServerConfig` traits if you want a non-rustls TLS (rare; for FFI / hardware HSM).
- Self-signed for dev: `rcgen::generate_simple_self_signed(...)` — `examples/server.rs:105`.

### tokio runtime integration

- `quinn/src/runtime/mod.rs:17` — `Runtime` trait (`new_timer`, `spawn`, `wrap_udp_socket`, `now`). Implement for any executor.
- `quinn/src/runtime/mod.rs:43` — `AsyncUdpSocket` trait (`create_sender`, `poll_recv`, `local_addr`, `max_receive_segments`, `may_fragment`). Implement to wrap a custom socket (STUN-multiplexed, in-memory test, etc.).
- `quinn/src/runtime/mod.rs:85` — `UdpSender` trait. One sender per task — multi-task write readiness.
- `quinn/src/runtime/mod.rs:225` — `default_runtime()` — auto-picks Tokio if in a Tokio context, else smol.
- `quinn/src/runtime/tokio.rs:19` — `TokioRuntime` impl. Wraps `tokio::net::UdpSocket` + `quinn_udp::UdpSocketState`.
- `quinn/src/runtime/smol.rs` — `SmolRuntime`.

### Windows specifics

- `quinn-udp/src/windows.rs:28` — `UdpSocketState` struct: tracks Winsock ECN support per-family (`ecn_v4_supported` / `ecn_v6_supported`) because Wine/Proton lacks `IP_RECVECN`/`IP_ECN`.
- `quinn-udp/src/windows.rs:76` — hard error if `WSARecvMsg` pointer unavailable (very old Windows).
- `quinn-udp/src/windows.rs:199` — `UdpSocketState::send(socket, transmit)` — uses `WSASendMsg` with cmsg for PKTINFO + ECN + UDP_SEND_MSG_SIZE (GSO).
- `quinn-udp/src/windows.rs:226` — `UdpSocketState::recv(...)` — uses runtime-loaded `WSARecvMsg`.
- `quinn-udp/src/windows.rs:560` — `max_gso_segments()`: probes via `UDP_SEND_MSG_SIZE`. Returns 512 on Win11 x64, 1 if unsupported.
- ECN is best-effort: if `setsockopt` fails with `WSAENOPROTOOPT`/`WSAEOPNOTSUPP`, ECN is disabled silently (lines 85-92).
- IPv6 dual-stack requires explicit `set_only_v6(false)` (`quinn/src/endpoint.rs:88,130`); some Windows configs disallow this.
- Depends on `windows-sys` for `WinSock::*` symbols (declared in `quinn-udp/Cargo.toml:31-32`).

## Examples worth copying

- `quinn/examples/connection.rs` — minimal viable: 45 lines, self-signed cert, one connection, one stream. **Start here.**
- `quinn/examples/single_socket.rs` — same UDP socket serving multiple outgoing connections + accept. **Hole-punching template.**
- `quinn/examples/client.rs` — full TLS, rustls roots, rebind demo (`--rebind` flag triggers `endpoint.rebind(...)`).
- `quinn/examples/server.rs` — full server: cert loading, PEM/DER, ALPN, stateless retry, connection limit, per-stream `accept_bi`.
- `quinn/examples/insecure_connection.rs` — `SkipServerVerification` certifier; useful for SPT-style mutual-known-key setups.
- `quinn/examples/common/` — `make_server_endpoint` / `make_client_endpoint` helpers; copy these wholesale.

## Binary size knobs

Default feature set (from `quinn/Cargo.toml:18`): `["tracing-log", "platform-verifier", "runtime-tokio", "rustls-ring", "bloom"]`.

For minimum binary:

```toml
quinn = { version = "0.12", default-features = false, features = ["runtime-tokio", "rustls-ring"] }
```

Feature analysis (`quinn/Cargo.toml:15-46`):

- `runtime-tokio` (required for `Endpoint::client`/`server` convenience) — pulls `tokio/time`, `tokio/rt`, `tokio/net`.
- `runtime-smol` — alternative; smaller than Tokio if you don't already use Tokio elsewhere.
- `rustls-ring` vs `rustls-aws-lc-rs` — `ring` is smaller and pure Rust (no C compiler). `aws-lc-rs` is FIPS-certifiable but pulls aws-lc-sys (large C blob). **Use `rustls-ring` for SPT.**
- `platform-verifier` — pulls `rustls-platform-verifier` (~heavyweight; uses OS cert store). Drop if you ship your own roots or use self-signed/mutual-auth.
- `bloom` — pulls `fastbloom`. Drop if you don't need address-validation token replay protection (server-side only).
- `tracing-log` — drops tracing→log bridge.
- `qlog` — debug-only; keep off.

Bare-metal protocol only (no IO, no async, no TLS): use `quinn-proto` direct with `default-features = false, features = ["rustls-ring"]`. Useful for embedded/FFI/C bindings.

`quinn-udp/Cargo.toml:16` default features: `["tracing", "tracing-log"]` — both can go.

`fast-apple-datapath` (`quinn-udp/Cargo.toml:21`) — only relevant on macOS/iOS (private API for sendmsg_x).

## Gotchas

- `EndpointDriver` must be `tokio::spawn`'d; it's done automatically by `Endpoint::new_with_abstract_socket` (`quinn/src/endpoint.rs:176`) — but if you `mem::forget` the spawn or drop the runtime, no IO happens.
- Calling `accept_bi()` on a stream the peer opened via `open_bi()` but hasn't written to yet will hang forever (`quinn/src/connection.rs:330-334`). Always write something on the open side before awaiting the accept side.
- `Endpoint::client(addr)` chooses `default_runtime()` based on **current Tokio context**. Outside a Tokio runtime, must call `Endpoint::new(...)` with explicit `Arc::new(TokioRuntime)`.
- `EndpointConfig` and `ServerConfig` defaults require either `ring` or `aws-lc-rs` enabled (`quinn/Cargo.toml:83`). Without a crypto provider, `EndpointConfig::default()` doesn't exist; use `EndpointConfig::new(custom_hmac_key)`.
- For NAT survival, set `TransportConfig::keep_alive_interval(Some(Duration::from_secs(15)))` *and* `max_idle_timeout(Some(IdleTimeout::try_from(Duration::from_secs(30))?))` — defaults let connections die silently.
- For hole punching, **both peers must agree which side is server** before either binds — there's no symmetric handshake. SPT can use lexicographic perch-id ordering.
- Migration only triggers if `ServerConfig.migration = true` on the server. Disabled means client NAT rebind kills the connection regardless of keep-alive.
- 0-RTT data on the client requires the server's TLS session to be cached in rustls' resumption store (default in-memory, 256 entries). Server restarts lose this.
- `Endpoint::rebind(...)` discards in-flight incoming on old socket; existing connections do *active* migration.
- Windows: `quinn-udp` returns `io::ErrorKind::Unsupported` if `WSARecvMsg` is missing — only matters on Windows older than what SPT targets.

## Tests to study

- `quinn-proto/src/tests/mod.rs` — full deterministic protocol tests; search for `migrate`/`rebind` for migration coverage.
- `quinn/src/tests.rs:325-380` — full 0-RTT roundtrip (`zero_rtt`/`accept_connection` closure pattern). Shows `into_0rtt()` on both sides.
- `quinn-udp/tests/` — UDP socket smoke tests; useful for verifying GSO/GRO availability on a target platform.
- `quinn/src/tests.rs` — search for `connect` + `rebind` to find migration integration tests.
