docs(arch): spec alknet-tls crate — shared TLS config across quinn + TCP+TLS + iroh (ADR-082, OQ-59)

The TLS setup in alknet-core is welded to quinn: build_rustls_server_config
produces a rustls::ServerConfig, then build_quinn_server_config_from_rustls
consumes it into quinn::ServerConfig — the rustls config is moved, not
shareable. ACME is worse: the AcmeState task is spawned inside the quinn
endpoint, so a TCP+TLS listener would need a second ACME state machine
(two orders for the same domain, two cert caches, Let's Encrypt
rate-limit risk).

alknet-tls extracts the TLS setup into a shareable TlsServerConfig:
- TlsServerConfig::new(identity, alpns) builds the rustls::ServerConfig
  once (including the ACME state machine if ACME)
- for_quinn() clones it for quinn (QuicServerConfig::try_from)
- for_tcp_tls() clones it for tokio-rustls (TlsAcceptor::from)
- rustls_config() borrows it for any other consumer
- One cert, one ACME state machine, N transports

TlsIdentity and Ed25519SecretKey stay in core (config types).
fingerprint.rs stays in core (shared by server endpoint + client
FingerprintPinVerifier in alknet-call — OQ-59 tracks whether it should
move; likely stays because moving would force alknet-call to depend on
alknet-tls, pulling TLS infra into client-only deployments).

iroh shares the key, not the rustls config — iroh has its own TLS built
into the Endpoint (takes iroh::SecretKey, not rustls::ServerConfig).
alknet-tls has no for_iroh() method; the assembly layer passes
Ed25519SecretKey to iroh directly.

Feature gates: quinn / tcp / acme as independent opt-ins. rustls always
present. tokio-rustls only when tcp. quinn only when quinn. rustls-acme
only when acme.

Files:
- crates/tls/README.md: crate spec (TlsServerConfig API, what moves/
  stays, feature gates, deps, behavior-preservation invariants)
- decisions/082-alknet-tls-extraction.md: the ADR (Proposed)
- questions/059-fingerprint-module-location.md: OQ-59 (should
  fingerprint.rs stay in core or move to alknet-tls? open, two-way,
  likely stays)
- open-questions.md: OQ-59 added to alknet-core table
- README.md: tls doc table row + ADR-082 row added

Review (architecture-reviewer): 3 critical (missing
build_quinn_server_config_from_rustls in README table, missing futures
dep, two TlsServerConfig code blocks disagreed on feature gates), 7
warnings (behavior-preservation invariants: max_early_data_size,
aws_lc_rs provider, verifier scheme list, acme-tls/1 ALPN;
rustls-pki-types dep; fingerprint.rs rustls-usage imprecision; ADR
missing acme-tls/1), 4 suggestions. All criticals and warnings addressed.
This commit is contained in:
glm-5.2 committed 2026-07-13 10:58:45 +00:00
1 parent b0cc0a01b7
commit 192bd0a5a2
5 files changed
+722

No files matched your search

@@ -0,0 +1,56 @@
# OQ-59: Should `fingerprint.rs` Stay in `alknet-core` or Move to `alknet-tls`?
- **Origin**: `docs/architecture/crates/tls/README.md` (the crate spec),
`docs/architecture/decisions/082-alknet-tls-extraction.md` (the ADR).
- **Status**: open
- **Door type**: two-way (moving a module between crates is a refactor,
not a wire-format change; the function signatures and types are
unchanged)
- **Priority**: medium
- **Blocked on**: nothing structural — the question is a dependency-edge
trade-off, not a missing capability. The existing code works in either
location. The decision is about which dep edge is cleaner.
- **Resolution**: Not yet decided. The trade-off:
**Option A: `fingerprint.rs` stays in `alknet-core`.**
- `alknet-core` keeps a narrow `rustls` dep (uses
`rustls::pki_types::CertificateDer` and `rustls::sign::public_key_to_spki`
in tests; the production code uses only `sha2` and manual DER parsing,
but the test helper `build_ed25519_spki_der` uses `rustls::sign`).
- `alknet-call`'s client-side `FingerprintPinVerifier` depends on
`alknet-core` (existing edge, no new dep).
- `alknet-tls` depends on `alknet-core` and re-exports the fingerprint
functions for convenience.
- Core is not `rustls`-free, but the dep is narrow (pki-types + sign
types, not the full TLS stack).
- This is the lower-friction option — no dep edges change.
**Option B: `fingerprint.rs` moves to `alknet-tls`.**
- `alknet-core` becomes `rustls`-free (loses the `rustls`,
`rustls-pki-types` deps for fingerprint).
- `alknet-call`'s `FingerprintPinVerifier` gains a dep on `alknet-tls`
— a new dep edge from a handler crate to a transport-infra crate.
This may or may not violate ADR-003's "no handler-depends-on-handler"
rule (alknet-tls is not a handler, but it is transport infra that
handler crates didn't previously depend on).
- The dep edge is the main concern: `alknet-call` depending on
`alknet-tls` means every call-protocol consumer transitively pulls
`rustls` + `tokio` + (optionally) `quinn`/`tokio-rustls`/`rustls-acme`.
That's heavy for a client-only deployment that just needs
fingerprint matching, not TLS setup.
**Likely resolution**: Option A (stay in core). The `rustls` dep in
core is narrow (type usage, not the TLS stack), and moving
`fingerprint.rs` to `alknet-tls` creates a heavy dep edge from
`alknet-call` to `alknet-tls` that doesn't serve the client-only case.
The fingerprint functions are used by both the server path (endpoint,
which does depend on `alknet-tls`) and the client path (call-client,
which does not want to depend on `alknet-tls`). Keeping them in core
serves both paths without forcing the client path to pull TLS infra.
- **What does NOT block on this**: the `alknet-tls` extraction itself
(ADR-082). The crate can be created with `fingerprint.rs` staying in
core, and the question can be resolved later without blocking the
extraction.
- **Cross-references**: ADR-082 (alknet-tls extraction), ADR-003 (crate
decomposition — handler dep rules), ADR-030 §6 (fingerprint
normalization), `crates/alknet-core/src/fingerprint.rs` (the code).