docs(arch): TlsError shape — single enum, owned by alknet-tls (ADR-088, resolves OQ-63)
Grounded in the actual error-producing call sites (endpoint.rs server side, call_client.rs client side) and the dependency-crate sources read from the cargo cache (rustls 0.23.41, rustls-pemfile 2.2.0, rcgen 0.13.2, quinn-proto 0.11.15, rustls-acme 0.12.1). Decision: single #[non_exhaustive] enum, one variant per failure category, owned by alknet-tls (not re-exported from core). Six variants: CertLoad(io::Error), SelfSigned(rcgen::Error), Rustls(rustls::Error), VerifierBuild(VerifierBuilderError), QuinnWrap(NoInitialCipherSuite) [quinn-gated], AcmeConfig(String). Three findings drove single-enum over thin wrapper: (1) for_quinn() fails with NoInitialCipherSuite, not rustls::Error — a rustls::Error wrapper cannot represent the for_quinn() failure; (2) rustls_pemfile::Error is not a std::error::Error (no Display, no Error impl) so #[from] would not compile — pemfile BufRead APIs return io::Error; (3) WebPkiServerVerifier::build() returns VerifierBuilderError, not rustls::Error — a thin wrapper cannot represent empty-CA-root-store as a first-class failure. Deliberately NOT variants: ACME EventError/OrderError (stream events, logged not returned from new); unknown-raw-key fail-closed (handshake- time rejection at dial time, not a config-construction error — corrects OQ-63's original framing); provider init (infallible); resolver construction (infallible).
This commit is contained in:
1 parent
5941280bca
commit
43b8179304
5 files changed
+573
-23
No files matched your search
@@ -6,7 +6,7 @@
|
||||
crate's public signatures, but is never sketched or defined); ADR-082
|
||||
(same — `TlsError` in signatures, no shape); ADR-087 (extends the
|
||||
surface to `TlsClientConfig::new` — the client-side error variants).
|
||||
- **Status**: open
|
||||
- **Status**: resolved (ADR-088)
|
||||
- **Door type**: one-way (the error type is the public API surface of
|
||||
`alknet-tls`; changing it after consumers exist is a breaking change
|
||||
to every assembly-layer call site)
|
||||
@@ -18,9 +18,12 @@
|
||||
in their signatures. Blocks the hub's assembly-layer wiring (which
|
||||
calls both). This is the next decision needed before the TLS crate
|
||||
can be implemented.
|
||||
- **Resolution**: Not yet decided. The shape needs to cover the failure
|
||||
modes across all server identity paths **and** the client verifier
|
||||
paths (ADR-087):
|
||||
- **Resolution**: **Decided (ADR-088).** `TlsError` is a single
|
||||
`#[non_exhaustive]` enum with one variant per failure category, owned
|
||||
by `alknet-tls` (not re-exported from core). The variants, grounded in
|
||||
the actual error-producing call sites and the dependency-crate sources
|
||||
(rustls 0.23.41, rustls-pemfile 2.2.0, rcgen 0.13.2, quinn-proto
|
||||
0.11.15, rustls-acme 0.12.1):
|
||||
|
||||
- **Cert/key loading** (`X509`: file read + PEM parse;
|
||||
`SelfSigned`: rcgen generation) — currently `io::Error`-wrapped in
|
||||
@@ -46,22 +49,43 @@
|
||||
overlap with the server-side rustls-build errors but have
|
||||
client-specific context (verifier selection inputs).
|
||||
|
||||
The open question is the granularity: a single `TlsError` enum with
|
||||
The open question was the granularity: a single `TlsError` enum with
|
||||
variants per failure category (cert-load, rustls-build, quinn-wrap,
|
||||
acme-disabled) vs a thin wrapper around `rustls::Error` /
|
||||
`io::Error`. The single-enum shape gives callers matchable context
|
||||
(the assembly layer can distinguish "cert file missing" from "quinn
|
||||
rejected the rustls config"); the thin wrapper is less code but
|
||||
loses the distinction. This is an API-surface decision — it needs an
|
||||
ADR or at minimum a sketch in the TLS README before implementation.
|
||||
`io::Error`. **Resolved: the single-enum shape.** The thin wrapper is
|
||||
actively wrong here — three findings from the dependency-crate source
|
||||
drive the decision: (1) `for_quinn()` fails with
|
||||
`NoInitialCipherSuite`, a distinct type from `rustls::Error` — a thin
|
||||
`rustls::Error` wrapper cannot represent the `for_quinn()` failure;
|
||||
(2) `rustls_pemfile::Error` is not a `std::error::Error` (no
|
||||
`Display`, no `Error` impl), so pemfile's BufRead APIs return
|
||||
`io::Error` — a `#[from] rustls_pemfile::Error` would not compile;
|
||||
(3) `WebPkiServerVerifier::build()` returns `VerifierBuilderError`,
|
||||
not `rustls::Error` — a thin wrapper cannot represent "empty CA root
|
||||
store" as a first-class failure. The six variants: `CertLoad(io::Error)`,
|
||||
`SelfSigned(rcgen::Error)`, `Rustls(rustls::Error)`,
|
||||
`VerifierBuild(VerifierBuilderError)`, `QuinnWrap(NoInitialCipherSuite)`
|
||||
(quinn-gated), `AcmeConfig(String)`. ACME `EventError`/`OrderError`
|
||||
are deliberately absent — they are stream events, logged not returned
|
||||
from `new`. The unknown-raw-key fail-closed is deliberately absent —
|
||||
it is a handshake-time rejection (dial time), not a
|
||||
config-construction error (`new` time). See ADR-088 for the full
|
||||
rationale, the variant definitions, and the "what is NOT a variant"
|
||||
list.
|
||||
|
||||
Subsidiary question: does `TlsError` live in `alknet-tls` (owned by
|
||||
the crate that produces it) or is it re-exported from `alknet-core`?
|
||||
Likely `alknet-tls` (it's the crate's own error), but worth
|
||||
confirming so `alknet-core`'s `EndpointError` (which no longer has a
|
||||
`TlsConfig` variant after ADR-083) doesn't need to know about it.
|
||||
- **Cross-references**: ADR-082 (the extraction that introduces
|
||||
**Resolved: `alknet-tls`, owned by the crate that produces it.**
|
||||
`alknet-core`'s `EndpointError` no longer has a `TlsConfig` variant
|
||||
after ADR-083 (the endpoint takes no TLS config); core does not need to
|
||||
know about `TlsError`. Re-exporting from core would invert the
|
||||
ownership (core re-exporting a type from a crate that depends on it).
|
||||
- **Cross-references**: ADR-088 (the resolution — single enum, owned by
|
||||
`alknet-tls`, six variants), ADR-082 (the extraction that introduces
|
||||
`TlsError`), ADR-083 (the endpoint refactor that removes
|
||||
`EndpointError::TlsConfig`, making `TlsError` the sole TLS error
|
||||
surface), `crates/alknet-core/src/endpoint.rs` (the current
|
||||
`io::Error`-wrapping pattern the new type replaces)
|
||||
surface), ADR-087 (extends the surface to client-side variants),
|
||||
`crates/alknet-core/src/endpoint.rs` (the current
|
||||
`io::Error`-wrapping pattern the new type replaces),
|
||||
`crates/alknet-call/src/client/call_client.rs` (the current
|
||||
`String`-wrapping pattern on the client side)
|
||||
Reference in new issue
Block a user