phase 1: architecture spec — overview, server/client, ADR-001..006

- 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
This commit is contained in:
2026-09-10 05:37:55 +00:00
parent e93238cb4a
commit d74a27f764
15 changed files with 1570 additions and 246 deletions
@@ -0,0 +1,103 @@
---
status: accepted
last_updated: 2026-09-10
---
# ADR-006: Module layout and test surface
## Status
Accepted (2026-09-10)
## Context
The extracted crate is organized as four modules plus the error
(`lib.rs`, `server.rs`, `client.rs`, `pem.rs`, `signing.rs`) with all
tests as in-module `#[cfg(test)]` blocks. alktls adds three modules of
moved types (ADR-005) and, per the repo's conventions, needs a
decided module map and test layout before the port starts.
Two shape questions need answers: where the moved types live
relative to the config constructors, and how the seed tests port
(no external workspace exists here, so in-module tests are the only
cross-module surface a full-crate crate has).
## Decision
**Module layout** (one module per file, re-exported from `lib.rs`):
```
src/
├── lib.rs — crate docs, TlsError (ADR-002), re-exports
├── identity.rs — TlsIdentity, Ed25519SecretKey, AcmeDirectory (ADR-005)
├── credentials.rs — ConnectionCredentials, RemoteIdentity (ADR-005)
├── fingerprint.rs — fingerprint_from_cert_der, extract_ed25519_raw_key_from_spki,
│ DER parser (ADR-005)
├── server.rs — TlsServerConfig, build_rustls_server_config,
│ RawKeyCertResolver, AcceptAnyCertVerifier,
│ SelfSignedCert / generate_self_signed_cert
├── client.rs — TlsClientConfig, select_server_verifier,
│ build_client_auth, RawKeyClientCertResolver,
│ NoClientCertResolver, FingerprintPinVerifier,
│ load_platform_root_cert_store
├── pem.rs — load_cert_chain, load_private_key
└── signing.rs — Ed25519SigningKey (rustls SigningKey + Signer)
```
Public API surface is `lib.rs` re-exports only (AGENTS.md convention
12); modules are `pub` for discoverability but the re-export block is
the documented surface.
**Feature gates stay on types and accessors, not modules.** The
`noq` feature gates `for_noq()` + `NoqWrap`; the `tcp` feature gates
`for_tcp_tls()`; the `acme` feature gates the ACME branch of
`TlsServerConfig::new` + the spawned task + the `futures` dep. Unlike
the alktunnels/alktty backends, there are no substrate modules to
gate — the transport-specific code is accessor-shaped.
**Test surface:**
- Port the extracted crate's in-module tests as the seed (they assert
the behavior-preservation invariants: `max_early_data_size` on the
server paths, `enable_early_data` on the client config, the
nine-scheme list, resolver behavior, PEM error paths, signing).
- Add `tests/` integration tests for the cross-module surfaces a
single crate has: `TlsServerConfig::new``for_noq()` /
`for_tcp_tls()` round-trips per identity variant;
`TlsClientConfig` verifier selection matrix (`Some`/`None` ×
raw-key/X.509); the root-store fallback against an empty platform
store; `acme-tls/1` append on the ACME path only.
- Pin the exact nine-scheme list in an integration test
(Phase 0 OQ-TLS-05's regression-proof shape — the extracted
in-module test only checks membership of two schemes), and pin
`enable_early_data = true` on the client config (the client half
of the 0-RTT invariant).
- `TlsIdentity::Acme`'s `unreachable!` in
`build_rustls_server_config` becomes a config error (the extracted
code relies on an internal dispatch invariant; the port replaces
the `unreachable!` with the `AcmeConfig` variant, keeping
no-panics-in-library-code).
## Consequences
**Positive:**
- The port is mechanically guided: module map + seed tests make the
Phase 2 port a diff against the extracted source.
- The invariant assertions move from "documented" to "tested" at both
unit and integration level.
**Negative:**
- Integration tests that wrap real QUIC endpoints are out of scope
(they belong to transport crates); the seam tests assert config
construction, not handshakes — the scope boundary holds.
## References
- AGENTS.md conventions 1, 9, 12 — comments, feature gates, module
structure
- `docs/research/phase-0.md` §Gaps #5, OQ-TLS-05 — the test-surface
question
- ADR-002 — `TlsError` (no `Config(String)` catch-all)
- ADR-005 — the moved modules