- 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
103 lines
4.2 KiB
Markdown
103 lines
4.2 KiB
Markdown
---
|
||
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 |