Files
alktls/tasks/port-server.md
T
glm-5.3-flash 0cd565fc28 generation 3: port server + client — the two big ports
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
2026-09-10 14:51:44 +00:00

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
port-identity-types
port-pem-signing
broad medium phase implementation
server
acme
port
invariants

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 was pub(crate); now the re-exported accessor).
  • for_noq replaces for_quinn: noq::ServerConfig::with_crypto(Arc::new( noq::crypto::rustls::QuicServerConfig::try_from(inner)?)); the failure maps to TlsError::NoqWrap (ADR-002/003).
  • build_rustls_server_config's TlsIdentity::Acme arm: the extracted unreachable! becomes a TlsError::AcmeConfig return (ADR-006; no panics in library code). The internal dispatch invariant (Acme is handled by new_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::MAX on every path (X509, RawKey, SelfSigned, ACME branch).
  • aws_lc_rs::default_provider() on every path (constructed via builder_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/1 appended by the crate on the ACME path only.
  • Verifier behavior: offer_client_auth() == true, client_auth_mandatory() == false, empty root_hint_subjects, accepts any client cert.
  • RawKeyCertResolver: only_raw_public_keys() == true, resolves the SPKI-backed CertifiedKey.

ACME path (feature acme)

Port new_acme per server.md: rustls_acme::AcmeConfig + DirCache

  • directory URL + contacts; state.resolver() wired in; acme-tls/1 appended; the event-loop task spawned (EventOk/EventError matched to tracing logs — port the extracted log lines); returns immediately. Handle stored in acme_handle, never aborted (detached; OQ-TLS-06). Without the feature, Acme identities return TlsError::AcmeConfig.

Work

  1. Port the module; apply the deltas above; rewire imports to this crate's types (identity, signing, pem, TlsError).
  2. Port the extracted in-module tests (they assert most invariants).
  3. Add the exact nine-scheme list pin (the extracted test only checks two schemes' membership).
  4. Feature-combo verification (below).

Verification

  • Invariant pins green: max_early_data_size per path, nine-scheme exact list, resolver behavior, verifier behavior
  • ACME branch (with --features acme): spawns, returns immediately, appends acme-tls/1 (test with a staging URL + tempdir cache; do NOT hit Let's Encrypt — construct and assert config state, assert the ALPN list)
  • Acme identity without the acme feature → TlsError::AcmeConfig
  • for_noq / for_tcp_tls round-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_config exist with ADR-004 signatures (&self; for_tcp_tls infallible)
  • No unreachable!/panics in library code
  • lib.rs re-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 in build_rustls_server_config returns TlsError::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 a rustls::Error producer (with_safe_default_protocol_versions, with_single_cert) became ? into TlsError::Rustls (#[from]); rcgen sites became ? into TlsError::SelfSigned (#[from] rcgen::Error). The non-acme TlsServerConfig::new Acme arm keeps its message verbatim but under AcmeConfig.
  • 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:9 cannot succeed.
  • for_noq(&self) clones the inner config (try_from consumes) — the ADR-004 borrow shape requires it; the clone is cheap (Arc-shared resolvers).
  • Cargo.toml deltas (two, both required):
    1. noq gains "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 resolves noq-proto 1.3.0, where ServerConfig::with_crypto(crypto) (the single-arg constructor for_noq calls) is #[cfg(any(feature = "aws-lc-rs", feature = "ring"))] — the retry-token key comes from noq's ring_like module, 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 explicit aws_lc_rs::default_provider() (noq consumes it from the config — ADR-003's provider paragraph). One new lockfile line (aws-lc-rs under noq-proto). ADR-003 needs a one-line amendment (recorded for review-impl docs sync).
    2. acme = ["dep:rustls-acme", "dep:futures"] — ADR-006 §Feature gates already records this ("the acme feature gates ... the spawned task + the futures dep"); the scaffold's TOML omitted the futures half. Without it, --features acme alone fails to compile (the spawned task uses futures::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.