docs(listener): where the selfSigned QUIC certificate lives now, and that it needs no CLI

The README's self-signed section, its dependency list, the consumer
notes and the agent notes described the openssl CLI and the shared
/tmp/crafter-network-quic-cert/. They now give the per-user location
and its fallbacks, the day-before-expiry rollover (which is when a
pinned hash changes), what keeps processes that start together on one
pair, and that nothing has to be installed. The agent notes' dev
environment says where the Flatpak keeps the certificate between runs,
drops the two gotchas the move resolved (the CLI, and the comments in
-ListenerQUIC.cpp and ClientQUIC.cppm that disagreed with the code),
and notes the module-internal functions :TLS declares for the listener.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jorijn van der Graaf 2026-11-01 01:57:57 +01:00
commit 76e927595d
Signed by: jorijnvdgraaf
GPG key ID: 2937E59CDCC1BCFB
3 changed files with 11 additions and 11 deletions

View file

@ -299,7 +299,7 @@ Browser-initiated bidi/unidi WebTransport streams arrive via `session.OnStream(.
### Self-signed certs & browser peers
Passing `QUICServerCredentials{selfSigned = true}` makes the listener generate (and cache) an ephemeral cert at `/tmp/crafter-network-quic-cert/{cert,key}.pem` and reuse it across server restarts while it's still valid. The cert is shaped to satisfy Chromium's `WebTransport.serverCertificateHashes` rules: ECDSA P-256, signed with ECDSA-SHA256, validity ≤14 days (10 in practice), `BasicConstraints CA:FALSE`, `KeyUsage digitalSignature`, `EKU serverAuth`, `SAN=DNS:localhost,IP:127.0.0.1,IP:::1`. To let a browser peer pin this cert without trusting a CA:
Passing `QUICServerCredentials{selfSigned = true}` makes the listener serve a development certificate it makes in-process (no `openssl` CLI) and keeps per user at `$XDG_CACHE_HOME/crafter-network/quic-cert/{cert,key}.pem` (`~/.cache/...` without `XDG_CACHE_HOME`, `$XDG_RUNTIME_DIR/...` without a home), in a directory only that user can enter. It is reused across server restarts until it has less than a day left, then replaced by the next process that needs it. Processes starting at the same moment share one pair: a lock covers the check and the replacement, each file is renamed into place whole, and the listener loads the pair from memory, so none ends up with one run's key and another's certificate. The cert is shaped to satisfy Chromium's `WebTransport.serverCertificateHashes` rules: ECDSA P-256, signed with ECDSA-SHA256, validity ≤14 days (10 in practice), `BasicConstraints CA:FALSE`, `KeyUsage digitalSignature`, `EKU serverAuth`, `SAN=DNS:localhost,IP:127.0.0.1,IP:::1`. To let a browser peer pin this cert without trusting a CA:
```cpp
auto certPath = Crafter::GetSelfSignedCertificatePath();
@ -341,6 +341,7 @@ The library includes tests covering:
- QUIC unreliable datagrams (`ShouldSendRecieveQUICDatagram`)
- QUIC listener ports (`ShouldReportListenerPortQUIC`) — a listener on port 0 reports the port it got and is reachable there, a second one gets another, and a fixed port reads back as itself
- QUIC loopback listeners (`ShouldBindLoopbackQUIC`) — a listener on `127.0.0.1` serves loopback clients and refuses, at once, clients through the host's LAN address or `::1`; `::1` works too, and a name is refused
- The QUIC development certificate (`ShouldShareSelfSignedCertificateQUIC`) — six processes start `selfSigned` listeners together from an empty cache, with no `openssl` on `PATH`, and all serve one certificate; its directory is private, a later process reuses it, and a mismatched pair on disk is replaced
- QUIC connection teardown (`ShouldSurviveConnectionChurn`) — 25 build-up/tear-down cycles with reader threads parked inside `RecieveSync`, the shape a reconnect loop has; the rest of the QUIC tests end in `std::_Exit(0)` and so never run a `ClientQUIC` destructor
- WebTransport echo (`ShouldEchoWebTransport`) — extended-CONNECT acceptance, draft-02 SETTINGS, bidi data stream framing (`WT_STREAM 0x41` + session-id varint), and byte-for-byte echo
- HTTP/1.1 wire format (`ShouldParseHTTP1`) — serialisation, incremental parsing fed one byte at a time, chunked bodies with trailers, pipelining, interim 1xx, HTTP/1.0 and HEAD framing, and the malformed inputs the parser is required to reject
@ -367,8 +368,7 @@ The external-interop test requires outbound UDP/443; if your network blocks it t
- **Crafter.Thread**: Thread pool management for asynchronous operations.
- **msquic** (native target only) — fetched and built automatically as a Crafter `ExternalDependency` (no system install required). The build clones `microsoft/msquic` at a pinned release (v2.6.3), with the submodules that release records, into the per-project external cache, configures it via CMake (`QUIC_TLS_LIB=openssl` against the system OpenSSL, tests/tools/perf disabled), and links the produced `libmsquic` into the QUIC and HTTP/3 modules. Skipped entirely on browser builds.
- On Linux msquic links against `libnuma` (provided by the `numactl` package on most distros).
- The self-signed-cert path used by tests/dev shells out to the `openssl` CLI; install `openssl` if you intend to use `QUICServerCredentials{selfSigned = true}`. The same path also produces the cert hash that browser peers need for `serverCertificateHashes`.
- **libssl / libcrypto** (OpenSSL 3, native target only) — a *system* package, unlike msquic, linked via `-lssl -lcrypto`. Backs `Crafter.Network:TLS`, i.e. `https://` on the HTTP/1.1 client and listener. It is not built here on purpose, and msquic uses it too, so the process has one OpenSSL: msquic's QUIC support needs OpenSSL 3.5 or newer (Arch has it, as does the Flatpak runtime crafter-build builds in). msquic's own bundled fork (quictls) is not used: it is an older OpenSSL that still reads the system's `openssl.cnf`, and a config written for the system's newer OpenSSL (the Flatpak runtime's loads a PKCS#11 engine, with `config_diagnostics` on) made its initialisation fail. Install your distro's OpenSSL development package (`openssl` on Arch, `libssl-dev` on Debian/Ubuntu). Where that OpenSSL is older than 3.5 (Debian 12 and Ubuntu 24.04 ship 3.0), msquic doesn't build against it: build with `--no-quic`, which keeps TCP, TLS and HTTP/1.1. Certificate generation for `TLSServerCredentials{selfSigned = true}` happens in-process through this library — no `openssl` CLI needed.
- **libssl / libcrypto** (OpenSSL 3, native target only) — a *system* package, unlike msquic, linked via `-lssl -lcrypto`. Backs `Crafter.Network:TLS`, i.e. `https://` on the HTTP/1.1 client and listener. It is not built here on purpose, and msquic uses it too, so the process has one OpenSSL: msquic's QUIC support needs OpenSSL 3.5 or newer (Arch has it, as does the Flatpak runtime crafter-build builds in). msquic's own bundled fork (quictls) is not used: it is an older OpenSSL that still reads the system's `openssl.cnf`, and a config written for the system's newer OpenSSL (the Flatpak runtime's loads a PKCS#11 engine, with `config_diagnostics` on) made its initialisation fail. Install your distro's OpenSSL development package (`openssl` on Arch, `libssl-dev` on Debian/Ubuntu). Where that OpenSSL is older than 3.5 (Debian 12 and Ubuntu 24.04 ship 3.0), msquic doesn't build against it: build with `--no-quic`, which keeps TCP, TLS and HTTP/1.1. Certificate generation for `TLSServerCredentials{selfSigned = true}` and `QUICServerCredentials{selfSigned = true}`, and the hash `ComputeCertificateHashSHA256` gives browser peers, happen in-process through this library — no `openssl` CLI needed.
- Plaintext HTTP/1.1 needs nothing beyond POSIX sockets: `ClientHTTP1`/`ListenerHTTP1` built without credentials pull in no msquic and touch no TLS code path.
- **Browser build** has no extra dependencies beyond Crafter.Build's `wasi-browser` runtime: HTTP delegates to the browser's `fetch()`, QUIC to its `WebTransport`. The JS glue lives in `additional/network-env.js` and is shipped alongside the produced `.wasm`.