- ADR-001: inherit the alknet TLS design as the baseline; deviations recorded as alktls ADRs - ADR-002: TlsError ships the ADR-088 six-variant shape from day one (typed #[from] sources; NoqWrap; no string catch-all) - ADR-003: the QUIC feature is noq (iroh's extracted fork), pre- consumer rename; default = [] per the lean-crate convention (corrects the extracted code's default = ["quinn"]) - ADR-004: complete accessors — for_tcp_tls() adopted, rustls_config() adopted; server accessors borrow (&self), client accessors consume - ADR-005: identity + credentials + fingerprint types move into alktls; auth layer stays out - ADR-006: eight-module layout; seed tests + integration invariant pins (exact nine-scheme list, client enable_early_data) - specs: overview (transport picture, terminology), server.md (ACME lifecycle, invariants), client.md (verifier selection matrix, root- store fallback); open-questions.md promotes OQ-TLS-01..08 (all resolved at entry) - Cargo.toml: quinn feature -> noq (per ADR-003); AGENTS.md aligned Architecture review pass done: 0 critical, 2 major (ADR-002 AcmeConfig doc comment contradiction; ADR-003 unrecorded default deviation) and 8 minors all addressed; cross-references verified against alknet ADRs, rustls/noq/iroh sources. Verified: cargo test, test --all-features, clippy -D warnings, fmt --check, doc --no-deps
5.5 KiB
status, last_updated
| status | last_updated |
|---|---|
| draft | 2026-09-10 |
alktls — Client side
TlsClientConfig and its verifiers: the client-side TLS setup,
extracted from alknet (crates/alknet-tls/src/client.rs) and ported
per the ADRs. Decisions referenced here: see
open-questions.md and the index in
overview.md.
TlsClientConfig
Built per dial from a ConnectionCredentials + ALPN (the
dial-seam pattern, alknet ADR-089). Consumed by its accessors — a
TlsClientConfig is not reused across dials (ADR-004).
pub struct TlsClientConfig {
rustls_config: rustls::ClientConfig,
}
impl TlsClientConfig {
pub fn new(credentials: &ConnectionCredentials, alpn: &[u8])
-> Result<Self, TlsError>;
#[cfg(feature = "noq")]
pub fn for_noq(self) -> Result<noq::ClientConfig, TlsError>;
pub fn into_rustls_config(self) -> rustls::ClientConfig;
}
new is sync and infallible-free aside from config construction
(cert file loading for an X.509 local identity is the only I/O).
Every config sets enable_early_data = true (the client half of the
0-RTT invariant) and carries the aws-lc-rs provider (alknet ADR-084).
The two credential dimensions
ConnectionCredentials (ADR-005, moved from alknet ADR-091) carries
exactly the two inputs the client config consumes:
-
local_identity: Option<TlsIdentity>— the client-auth cert presentation:Local identity Presented RawKeyRFC 7250 raw public key (SPKI DER; only_raw_public_keys()auto-detected from the DER)X509the cert chain + key, loaded from disk SelfSigned/Nonenothing ( NoClientCertResolver) — documented, resolved in OQ-TLS-02Acmeconfig error ( TlsError::AcmeConfig) — server-only identity -
remote_identity: Option<RemoteIdentity>— the verifier selection matrix (below). BothOptions are load-bearing, not cosmetic:Somemeans "pin this",Nonemeans "trust the CA or fail" — never a placeholder default.
Verifier selection (alknet ADR-034 §3)
Exactly three outcomes, driven by remote_identity:
remote_identity |
Remote cert | Verifier | Outcome |
|---|---|---|---|
Some(fingerprint) |
any | FingerprintPinVerifier |
pin match required |
None |
X.509 | WebPkiServerVerifier (CA) |
CA verification against the platform root store |
None |
Ed25519 raw key | (the CA verifier fails) | fail closed at handshake |
None is the public-X.509-endpoint state, not "skip
verification". An unknown raw-key remote fails at handshake — it must
never silently downgrade to CA verification. Verifier selection
happens at config construction; the fail-closed manifests at
handshake time (the config carries the CA verifier; the raw-key
remote simply cannot satisfy it) — this is the ADR-002 scope
boundary in action.
Fail-closed is structural: known peer + fingerprint → pin; unknown + X.509 → CA; unknown + raw key → fail. No fourth path.
FingerprintPinVerifier
The known-peer path. The fingerprint IS the trust anchor: for
ed25519:<hex> remotes the raw Ed25519 key is extracted from the
presented cert (SPKI) and matched; for SHA256:<hex> remotes the
full cert DER is hashed and matched. No CA verification, no name
verification — only the pin.
Handshake signatures are still verified (TLS 1.2/1.3, aws-lc-rs
algorithms; Ed25519 SPKI certs route through
verify_tls13_signature_with_raw_key): a stolen fingerprint cannot
be replayed with a forged signature — the presenter must prove
possession of the corresponding private key.
The root-store fallback (alknet ADR-088 §5)
The None + X.509 CA path loads the platform's native root certs
(rustls-native-certs). If the platform store is empty — a
containerized deployment with no system CA bundle — the built-in
webpki-roots are merged in so the store is never empty.
Native-certs load errors are logged, not returned; the fallback
guarantees non-emptiness regardless. This makes NoRootAnchors
unreachable in practice (the VerifierBuild variant exists for the
builder API's completeness, not a reachable path — ADR-002).
The root store is built unconditionally for the CA path — not
feature-gated on tcp or noq — because any client dialing a public
X.509 endpoint needs it regardless of transport.
What the client side does NOT do
- No dial: the dial seam (
AlknetClientin alknet; the rewrite's dial crate) consumes the config viafor_noq()/into_rustls_config()and ownsTcpStream::connect,Endpoint::connect, SNI, and SOCKS5 proxying (alknet ADR-089; SOCKS5 proxy seam per alknet ADR-090). Handshake failures are the dial's errors (ClientDialError), notTlsError. - No iroh: the iroh dial takes the 32-byte Ed25519 key directly (key-not-config; ADR-003). iroh's built-in verifier (NodeId check) is iroh's own application of the same fail-closed rule.
- No per-request auth: a call-protocol
auth_tokenis a call-layer concept; it never reachesTlsClientConfig.
References
- overview.md — the index; server.md — the server side
- alknet
crates/tls/README.md§Client side — the full client-side spec this doc mirrors - alknet ADR-034 (verifier selection), ADR-088 §5 (root-store
fallback), ADR-091 (
ConnectionCredentials) - ADR-002 (
TlsError), ADR-004 (accessors), ADR-005 (credential types), ADR-006 (tests — the verifier-selection matrix)