port-server (src/server.rs, 614 lines): - TlsServerConfig with ADR-004 accessors: for_noq(&self) (borrow + inner-clone + NoqWrap via #[from]), for_tcp_tls(&self) adopted (infallible TlsAcceptor), rustls_config(&self) adopted - acme_handle renamed per ADR-006; the defensive Acme arm returns TlsError::AcmeConfig (unreachable! eliminated) - error sites remapped to typed variants (Rustls/SelfSigned/AcmeConfig) - ACME path verbatim: DirCache + directory + contacts + resolver + acme-tls/1 + spawned event loop (tracing lines ported), returns immediately, detached handle - 14 tests incl. the nine-scheme exact-list pin, ACME spawn/return/ALPN assertion (blackhole URL — no network I/O), no-feature AcmeConfig, for_tcp_tls/rustls_config round-trips port-client (src/credentials.rs + src/client.rs): - ConnectionCredentials/RemoteIdentity wholesale with load-bearing Option-semantics docs (None = public-X.509 state, Some = pin) - verifier selection matrix + client-auth presentation matrix test-pinned at unit level; enable_early_data=true pinned; root-store fallback asserted non-empty - FingerprintPinVerifier: pin match/mismatch + raw-key signature routing (verify_tls13_signature_with_raw_key) asserted - resolvers/verifier made pub for the re-export block Cargo.toml (two required deltas): - noq feature gains aws-lc-rs: lockfile resolves noq-proto 1.3.0 where ServerConfig::with_crypto is #[cfg(any(aws-lc-rs, ring))]; ADR-003's TOML block was written against the 1.2 API. Amendment note recorded in tasks/port-server.md for the review-impl sync - acme = [dep:rustls-acme, dep:futures] — ADR-006 already gates the futures dep on acme; the scaffold omitted it lib.rs: re-export block complete (all eight modules) Verification: cargo test 68, --all-features 75, --features noq 72, clippy -D warnings, fmt --check, doc --no-deps (0 warnings) — green
7.8 KiB
id, name, status, depends_on, scope, risk, impact, level, tags
| id | name | status | depends_on | scope | risk | impact | level | tags | ||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| port-server | Port server side — TlsServerConfig, resolvers, ACME path (src/server.rs) | completed |
|
broad | medium | phase | implementation |
|
Description
Port src/server.rs from alknet-tls per the ADRs: TlsServerConfig,
build_rustls_server_config, RawKeyCertResolver,
AcceptAnyCertVerifier, SelfSignedCert / generate_self_signed_cert,
and the ACME branch. This is the biggest port task; the deltas against
the extraction source are all ADR-pinned, everything else is a port.
The API deltas (from the extracted code)
- Accessors borrow:
for_noq(&self)(ADR-004),for_tcp_tls(&self)(adopted — did not exist in the extraction),rustls_config(&self)(adopted — the field waspub(crate); now the re-exported accessor). for_noqreplacesfor_quinn:noq::ServerConfig::with_crypto(Arc::new( noq::crypto::rustls::QuicServerConfig::try_from(inner)?)); the failure maps toTlsError::NoqWrap(ADR-002/003).build_rustls_server_config'sTlsIdentity::Acmearm: the extractedunreachable!becomes aTlsError::AcmeConfigreturn (ADR-006; no panics in library code). The internal dispatch invariant (Acme is handled bynew_acme) is preserved — the arm is defensive.- Field name:
acme_handle(ADR-006 unified on the alknet-spec name).
The behavior-preservation invariants (all must be test-pinned here)
max_early_data_size = u32::MAXon every path (X509, RawKey, SelfSigned, ACME branch).aws_lc_rs::default_provider()on every path (constructed viabuilder_with_provider; no process-default fallback).AcceptAnyCertVerifier::supported_verify_schemes()returns the nine schemes verbatim (ED25519; ECDSA P-256/P-384; RSA PSS 256/384/512; RSA PKCS1 256/384/512).acme-tls/1appended by the crate on the ACME path only.- Verifier behavior:
offer_client_auth() == true,client_auth_mandatory() == false, emptyroot_hint_subjects, accepts any client cert. RawKeyCertResolver:only_raw_public_keys() == true, resolves the SPKI-backedCertifiedKey.
ACME path (feature acme)
Port new_acme per server.md: rustls_acme::AcmeConfig + DirCache
- directory URL + contacts;
state.resolver()wired in;acme-tls/1appended; the event-loop task spawned (EventOk/EventErrormatched totracinglogs — port the extracted log lines); returns immediately. Handle stored inacme_handle, never aborted (detached; OQ-TLS-06). Without the feature,Acmeidentities returnTlsError::AcmeConfig.
Work
- Port the module; apply the deltas above; rewire imports to this crate's types (identity, signing, pem, TlsError).
- Port the extracted in-module tests (they assert most invariants).
- Add the exact nine-scheme list pin (the extracted test only checks two schemes' membership).
- Feature-combo verification (below).
Verification
- Invariant pins green:
max_early_data_sizeper path, nine-scheme exact list, resolver behavior, verifier behavior - ACME branch (with
--features acme): spawns, returns immediately, appendsacme-tls/1(test with a staging URL + tempdir cache; do NOT hit Let's Encrypt — construct and assert config state, assert the ALPN list) Acmeidentity without theacmefeature →TlsError::AcmeConfigfor_noq/for_tcp_tlsround-trip per identity variant (construction-level; handshakes are out of scope)cargo test(default),cargo test --all-features,cargo clippy --all-targets -- -D warnings,cargo fmt --check
Acceptance Criteria
- All five server invariants are test-asserted (not just compiled)
for_noq,for_tcp_tls,rustls_configexist with ADR-004 signatures (&self;for_tcp_tlsinfallible)- No
unreachable!/panics in library code lib.rsre-exports the server surface
References
- docs/architecture/server.md (the normative doc)
- docs/architecture/decisions/002-tlserror-shape.md, 003, 004
- Prior art:
/workspace/@alkdev/alknet/crates/alknet-tls/src/server.rs(port source; note every delta above)
Notes
- The extracted
unreachable!is now the defensive arm: the Acme arm inbuild_rustls_server_configreturnsTlsError::AcmeConfig("TlsIdentity::Acme is handled by TlsServerConfig::new_acme, not build_rustls_server_config")— test-asserted (build_rustls_server_config_acme_returns_config_error). - Error mapping: every extraction
TlsError::Config(e.to_string())site at arustls::Errorproducer (with_safe_default_protocol_versions,with_single_cert) became?intoTlsError::Rustls(#[from]); rcgen sites became?intoTlsError::SelfSigned(#[from] rcgen::Error). The non-acmeTlsServerConfig::newAcme arm keeps its message verbatim but underAcmeConfig. - The ACME test uses a
Custom("http://127.0.0.1:9/directory")directory rather than the staging URL — never contacted either way (port 9 discard; no network I/O beyond rustls-acme's async task start); the task's "staging URL" intent (do-not-hit-Let's-Encrypt) is satisfied with a stronger guarantee: a blackhole address. Staging would risk a real contact if a test misfired;127.0.0.1:9cannot succeed. for_noq(&self)clones the inner config (try_fromconsumes) — the ADR-004 borrow shape requires it; the clone is cheap (Arc-shared resolvers).- Cargo.toml deltas (two, both required):
noqgains"aws-lc-rs"in its feature list. ADR-003's TOML block (features = ["rustls"]) was written against noq 1.2's API, but the lockfile resolvesnoq-proto 1.3.0, whereServerConfig::with_crypto(crypto)(the single-arg constructorfor_noqcalls) is#[cfg(any(feature = "aws-lc-rs", feature = "ring"))]— the retry-token key comes from noq'sring_likemodule, which needs one of the two provider features."aws-lc-rs"(not"ring") matches the crate's provider posture; it does not fight ADR-084: the config's internal provider is still the crate's explicitaws_lc_rs::default_provider()(noq consumes it from the config — ADR-003's provider paragraph). One new lockfile line (aws-lc-rsunder noq-proto). ADR-003 needs a one-line amendment (recorded for review-impl docs sync).acme = ["dep:rustls-acme", "dep:futures"]— ADR-006 §Feature gates already records this ("theacmefeature gates ... the spawned task + thefuturesdep"); the scaffold's TOML omitted thefutureshalf. Without it,--features acmealone fails to compile (the spawned task usesfutures::StreamExt).
Summary
Ported src/server.rs (614 lines) from alknet-tls server.rs with all
five ADR-pinned deltas applied: for_noq(&self) (borrow + clone +
NoqWrap via #[from]), for_tcp_tls(&self) adopted (infallible
TlsAcceptor), rustls_config(&self) adopted, acme_handle renamed,
Acme arm → TlsError::AcmeConfig (no unreachable!). All error sites
remapped to the typed variants (Rustls/SelfSigned/AcmeConfig).
The ACME path is verbatim (DirCache + directory URL + contacts +
resolver + acme-tls/1 + spawned event loop with the extracted
tracing lines, returns immediately, handle never aborted).
14 tests: 12 ported (with the Acme-unreachable → AcmeConfig and
for_quinn → for_noq rewires; the acme_directory URL tests live in
identity.rs now) + the nine-scheme exact-list pin + ACME
spawn/return/ALPN assertion + no-feature AcmeConfig test +
for_tcp_tls/rustls_config round-trips for X509 (rcgen PEM pair in
tempdir)/RawKey/SelfSigned. Verification: cargo test 68 pass,
--all-features 75 pass, --features noq 72 pass, clippy -D
warnings (default + all-features), fmt --check, feature checks
(tcp/acme) — all green.