phase 2: task decomposition — 8-task port graph

- crate-init: skeleton, feature gates (ADR-003), TlsError (ADR-002)
- three parallel foundation ports: identity types, fingerprint, pem+signing
- port-server / port-client in parallel (each consumes the foundation;
  client also carries credentials.rs per ADR-005 co-location)
- integration-suite: invariant pins + feature-matrix + seam round-trips
- review-impl: spec-conformance gate — the API-freeze point before the
  alknet rewrite consumes the crate
- validated: taskgraph topo (5 generations), no cycles, critical path
  len 5, risk-path 1.45; medium risk concentrated on the two broad
  ports + the suite (each a diff against a fixed extraction source,
  not open-ended work)
This commit is contained in:
2026-09-10 06:32:44 +00:00
parent d74a27f764
commit 05b3832c88
8 changed files with 706 additions and 0 deletions
+106
View File
@@ -0,0 +1,106 @@
---
id: port-server
name: Port server side — TlsServerConfig, resolvers, ACME path (src/server.rs)
status: pending
depends_on: [port-identity-types, port-pem-signing]
scope: broad
risk: medium
impact: phase
level: implementation
tags: [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
> Agent fills this during implementation.
## Summary
> Agent fills this on completion.