ADR-085 records the actual workspace scope: the mono-repo is the core networking toolkit (substrate: core, tls, call, channels; deployment shapes: hub, worker; foundational handlers: tty, http, ssh, tunnel, socks5, fs, sftp; vault). Consumer repos (docker, agent) are separate repos depending on the published core crates. The overview's crate graph had been describing the wrong scope since ADR-003 — a flat ~12-crate workspace including DNS/messaging/NAPI while omitting channels, hub, worker, and tls. This stale scope was a causal factor in the 'assembly layer' hedging pattern: when the overview implies everything lives in one repo but the architecture needs a hub/worker composition layer not in the graph, the gap gets filled with 'assembly layer' as an escape hatch. The overview is rewritten to match the real boundary. TLS spec review fixes (from architecture review): - C3: hub/worker/hub-worker terminology pointers (tls README + endpoint.md) - W1: server-only statement + OQ-64 (client-side TLS helper, deferred) - W2: ACME task lifecycle semantics (returns immediately, no first-cert await) - W3: remove stale EndpointError::TlsConfig variant - W4: update stale ALPN section for two-config hub - W5: add alknet-tls to hub dep graph (assembly-layer dep) - W6: trim inline rationale -> point to ADR-084 - W7: ADR-084 status dependency note New open questions: - OQ-62: ALPN list sharing for two-config hub (open, high) - OQ-63: TlsError shape (open, high) - OQ-64: client-side TLS helper (deferred, blocked on OQ-55)
6.6 KiB
ADR-084: aws-lc-rs as the TLS Crypto Provider
Status
Accepted (depends on ADR-082/083, which are still Proposed — this ADR records a decision those ADRs list as an invariant. If ADR-082/083 are revised in a way that changes the config-construction paths, the provider decision here still stands; only the "where it is applied" referent would update.)
Context
The TLS stack in alknet uses rustls, which requires a crypto provider.
Rustls 0.23 made the crypto provider explicit — it is no longer a
process-global default that gets set once and forgotten. Each
ServerConfig / ClientConfig is built with a specific provider via
builder_with_provider(Arc<dyn CryptoProvider>).
The current code (alknet-core/endpoint.rs, ADR-027, and the
alknet-tls extraction in ADR-082) uses
rustls::crypto::aws_lc_rs::default_provider() on all server config
paths (X509, RawKey, SelfSigned, ACME). This choice was made during
ADR-027's implementation to match iroh's tls-aws-lc-rs feature, but
was never recorded as a decision. ADR-082's behavior-preservation
invariants list says "do not switch to ring or the process-default
provider without an ADR" — this ADR is that record.
Why this is load-bearing
The crypto provider determines:
- FIPS status:
aws-lc-rshas a FIPS-certified build mode (via AWS-LC).ringdoes not. If alknet ever needs FIPS compliance (e.g., for regulated deployments),aws-lc-rsis the path;ringwould be a dead end. - Platform support:
aws-lc-rssupports a broad set of platforms via its C/C++ build.ringhas a different (historically narrower) platform matrix. Switching providers changes which platforms compile. - Cipher suite defaults:
default_provider()returns a provider with a specific set of cipher suites and signature algorithms.AcceptAnyCertVerifier'ssupported_verify_schemes()(ED25519 + ECDSA P-256/P-384 + RSA PSS/PKCS1) must be supported by the provider —aws_lc_rssupports all of these; a provider that dropped one would silently change which client cert signature algorithms the server accepts. - iroh compatibility: iroh uses
aws-lc-rsvia itstls-aws-lc-rsfeature. If alknet's quinn/TCP+TLS path used a different provider, the same Ed25519 key would produce TLS handshakes with different crypto internals on the quinn path vs the iroh path — a consistency risk for the fingerprint normalization (ADR-030 §6).
The options
aws_lc_rs::default_provider()(current): FIPS-capable, broad platform support, matches iroh. The choice already in the code.ring::default_provider(): simpler pure-Rust crate, no FIPS, different platform matrix. Would break iroh provider consistency.- Process-default provider (
rustls::crypto::CryptoProvider::get_default()): relies on whoever set the process global first. Non-deterministic in a library context — alknet crates shouldn't depend on the binary having set the right global. Explicit per-config is the library-correct pattern (and is what the current code does).
Decision
rustls::crypto::aws_lc_rs::default_provider() is alknet's TLS crypto
provider on all server and client config paths. This applies to
alknet-tls (the TlsServerConfig construction paths: X509, RawKey,
SelfSigned, ACME) and to alknet-call's client-side verifier
construction (which builds a rustls::ClientConfig with the same
provider for consistency).
The provider is constructed explicitly per config
(Arc::new(rustls::crypto::aws_lc_rs::default_provider()) passed to
builder_with_provider), not via the process-default global. This
keeps alknet crates correct as libraries — they don't depend on the
binary having set a global.
What this means for alknet-tls
The TlsServerConfig::new paths (X509, RawKey, SelfSigned, ACME) all
construct their rustls::ServerConfig with
builder_with_provider(Arc::new(aws_lc_rs::default_provider())). This
is already the case in the current code (ADR-082's
behavior-preservation invariants record this); this ADR makes the
decision explicit so that a future switch requires a new ADR.
What this means for alknet-call
The client-side rustls::ClientConfig (used by CallClient for
outgoing connections, with either FingerprintPinVerifier or
WebPkiServerVerifier) must use the same provider. This ensures the
client and server agree on cipher suites and signature algorithms — a
mismatch could cause handshake failures or silent signature-algorithm
changes.
Consequences
Positive:
- One crypto provider across all TLS paths (quinn, TCP+TLS, iroh's built-in TLS, client-side). Consistent cipher suites, signature algorithms, and FIPS status.
- FIPS-capable: if a regulated deployment needs FIPS,
aws-lc-rs's FIPS build mode is available without an ADR (it's a build-flag, not a provider change). - Matches iroh's
tls-aws-lc-rsfeature — no provider mismatch between alknet's quinn/TCP+TLS path and iroh's built-in TLS path.
Negative:
aws-lc-rsis a C/C++ crate (built viaaws-lc), not pure Rust. This adds a C build dependency.ringis also C-backed, so this is not a regression vs the alternative — but a pure-Rust provider (e.g., a futurerustlsprovider) would be lighter. Not switching now; the FIPS and iroh-consistency benefits outweigh the build complexity.- A future switch to a different provider requires a new ADR (this is already stated in ADR-082's invariants; this ADR makes the current choice a recorded decision, not just an invariant).
Door type
One-way. The provider choice is baked into every TLS config path.
Switching providers after consumers exist requires updating every config
construction site and verifying cipher-suite + signature-algorithm
compatibility. The FIPS and iroh-consistency constraints make ring
and the process-default provider non-viable — the decision is
aws_lc_rs unless a future ADR records a different choice with
rationale.
References
- ADR-027: TLS Identity Redesign (where
aws_lc_rswas first used, to match iroh'stls-aws-lc-rsfeature) - ADR-082: alknet-tls extraction (behavior-preservation invariants:
aws_lc_rs::default_provider()on all paths; "do not switch toringor the process-default provider without an ADR") - ADR-030 §6: fingerprint normalization (requires provider consistency across quinn/iroh/TCP+TLS — the same Ed25519 key must produce the same fingerprint regardless of transport)
crates/alknet-core/src/endpoint.rs— the current code usingaws_lc_rs::default_provider()on all server config pathscrates/alknet-call/src/client/call_client.rs— the client-siderustls::ClientConfigconstruction (must use the same provider)