Commit Graph
24 Commits
Author SHA1 Message Date
glm-5.3-flash 37b5f025d0 publish prep: README + package hygiene (v0.1.0)
- README.md: honest, agnostic quick-start (server/client), identity
  model, feature table, invariants, scope boundary — written against
  the shipped API, sibling-crate style
- Cargo.toml: readme = README.md; exclude tests/ from the published
  package (matches the N-6 rationale — integration tests reference
  docs/reviews context and are not consumer docs)
- rust-version 1.88 / edition 2021 verified identical to the
  published siblings (alkcall 0.7.1, alktty 0.4.1, alktunnels 0.1.0)

Verification: cargo test (93 passed), cargo test --all-features
(123 passed), clippy -D warnings clean, fmt clean, doc warning-free,
cargo publish --dry-run --allow-dirty passes (16 files, 201.4KiB).
2026-09-12 06:26:47 +00:00
glm-5.3-flash f21b62c4b4 task: ACME event-loop coverage — fake-directory integration test (U-1)
- tests/acme_event_loop.rs: five integration tests drive the crate's
  real ACME path and assert the spawned loop's logged events through a
  hand-rolled tracing test subscriber: AccountCacheStore (eager, pre-
  network), the Order warn via a plain-HTTP fake directory stub on an
  ephemeral 127.0.0.1 port, DeployedCachedCert from a pre-seeded
  PKCS#8-key-first PEM chain, CertCacheLoad/AccountCacheLoad errors +
  AccountCacheStore warn from an ENOTDIR-poisoned cache path, and
  CachedCertParse from a corrupt cached PEM. Event collection is
  timeout-bounded, never JoinHandle-bounded (rustls-acme 0.12.1's
  stream never terminates — verified against the vendored sources).
- src/server.rs: delete the unreachable debug!("ACME: state machine
  ended") line (work item 2: it cannot execute under the
  never-terminating stream).
- DirCache's deterministic cache file names (SHA256 over element+NUL*
  + directory URL, base64url-nopad) are recomputed in-test via the
  sha2 dependency + a new base64 dev-dependency.
- Explicitly not-covered (work item 4): DeployedNewCert, CertCacheStore
  ok+warn, NewCertParse — reachable only through a successful ACME
  order (a full fake CA), out of scope at this effort level.

Verification: cargo llvm-cov --all-features line coverage 99.21%
(from 98.26%), server.rs 96.08% -> 98.85% lines / 100% functions;
cargo test (93 passed), --all-features (123 passed), --features acme;
clippy (default + all-features, -D warnings); fmt; doc. The event-loop
suite is stable across five consecutive runs.
2026-09-12 05:35:46 +00:00
glm-5.3-flash efb1f2dbf0 task: cheap coverage closes — U-2 groups closed, line coverage 95.90% → 98.26%
- client.rs: TLS 1.2 non-Ed25519 (X.509 ECDSA) routing pin on
  FingerprintPinVerifier::verify_tls12_signature's else-arm;
  deterministic webpki-roots fallback tests over a new private
  fill_root_store_from_native(&CertificateResult) seam (empty store →
  all webpki-roots anchors, errors + valid cert, no-fallback case)
- server.rs: VerifyPresentedCertVerifier::verify_tls12_signature unit
  tests (Ed25519 raw-key route, X.509 route, wrong-key and
  mismatched-message rejections), the escape hatch's TLS 1.2 no-pop
  pin, and the default verifier's nine-scheme list pin
- pem.rs: parse-error arm test (garbage-but-keyed file → CertLoad)
- fingerprint.rs: fixed vacuous bad_bit_string_lengths_extract_nothing
  (outer SEQUENCE lengths exceeded the actual bytes, so parsing never
  reached the line-67 bit-string checks); added the non-OID-tag /
  missing-BIT-STRING matrix
- no public-API growth; remaining uncovered lines are the ACME event
  loop (owned by acme-event-loop-test) and llvm-cov attribution
  artifacts documented in the task's Notes

Verification: cargo llvm-cov --all-features 98.26% lines; cargo test
(80 lib + 13 integration), cargo test --all-features (89 lib + 29
integration), clippy -D warnings (default + all-features), fmt --check
2026-09-12 04:57:13 +00:00
glm-5.3-flash 7713a6e210 docs-pin batch: self-signed validity, pin format-exactness, non-Clone decision, negotiation note (C-1, C-4, N-3, N-4)
- C-1: SelfSignedCert + generate_self_signed_cert document the
  rcgen-default validity (1975→4096, never expires in practice), no
  SANs (CA verification fails as designed — pairs with SHA256: pinning),
  and the additive not_before/not_after route if tighter validity is
  ever wanted
- C-4: RemoteIdentity::fingerprint + FingerprintPinVerifier document
  case-/format-exactness (pins produced by fingerprint_from_cert_der,
  lowercase hex, exact ed25519:/SHA256: prefixes); written from the
  ADR-007-corrected chain — same-format-but-wrong pins fail closed at
  the pin compare, cross-format mismatches fail earlier at cert-type
  negotiation (the review's original pin-compare mechanism superseded)
- N-3: TlsServerConfig doc records the keep-non-Clone-for-v1 decision
  (API identical across feature configurations; conditional Clone is
  its own trap); N-3 closed in review 001
- N-4: "Client-cert-type negotiation" section on
  VerifyPresentedCertVerifier (cross-referenced from
  AcceptAnyCertVerifier): requires_raw_public_keys() stays trait-default
  false on both server verifiers — do not fix to true (rejects X.509
  clients); the raw-only-offer rejection can only arise from a foreign
  resolver with only_raw_public_keys() == true — interop boundary of
  the request-not-require shape, fail-closed, not a downgrade; pinned
  by server_verifiers_keep_requires_raw_public_keys_default_false
- Work item 5: FingerprintPinVerifier's pop cross-ref now names both
  server verifiers (default VerifyPresentedCertVerifier verifies per
  ADR-008; AcceptAnyCertVerifier escape hatch does not)

Verification: cargo test (default, 69 lib tests) and --all-features
(78 lib tests, 1 new), clippy --all-targets --all-features -D warnings,
fmt --check, doc --no-deps warning-free — all green
2026-09-12 04:28:58 +00:00
glm-5.3-flash 9bdc32d848 config robustness + trivia batch: ALPN dedup, empty-domains validation, packaging excludes, https doc line (C-2, C-3, N-6, N-7)
- C-2: new_acme dedups the acme-tls/1 ALPN append (idempotent
  construction); ACME/non-ACME ALPN asymmetry documented on
  TlsServerConfig::new; pinned by
  new_acme_caller_supplied_acme_tls_alpn_is_not_duplicated
- C-3: new_acme rejects an empty domains list with
  TlsError::AcmeConfig before spawning the order loop; contact stays
  unvalidated (RFC 8555 7.3 zero-contact accounts are legal); pinned
  by new_acme_empty_domains_returns_config_error (acme-gated)
- N-6: Cargo.toml exclude gains tasks/ and docs/architecture/
- N-7: AcmeDirectory::Custom documents the https-only caller contract
  (no runtime validation, per finding)

Verification: cargo test (default) and --all-features (77 lib tests,
2 new), clippy -D warnings, fmt --check, doc, package --list (no
tasks/ or docs/architecture/), publish --dry-run — all green
2026-09-12 04:25:55 +00:00
glm-5.3-flash 23893d6236 re-baseline the four pending remediation tasks against the ADR-007/008 tree
The tasks were decomposed (d7db6b1) before 49d4432/ac440f3 landed;
both commits touched exactly the areas the tasks reference. Fixes
grounded in verified sources (vendored rustls-acme 0.12.1, rustls-pemfile
2.2.0, rustls-native-certs 0.8.4) and a fresh cargo llvm-cov run:

- acme-event-loop-test: the termination assertion was impossible —
  rustls-acme's Stream for AcmeState never yields None
  (state.rs:407-412, poll_next_infinite + 2^16s backoff); the review's
  U-1 exit-condition premise is withdrawn and corrected in place.
  Replaced with timeout-bounded event collection, a reachable-arm
  inventory (Order warn, AccountCacheStore, Load/Parse error arms,
  DeployedCachedCert/CertCacheStore via deterministic DirCache file
  pre-seeding), and an explicit mark for the full-fake-CA arms.
  server.rs:135 flagged as unreachable dead code (delete or accept).
- coverage-cheap-closes: re-baselined per-line ground truth — original
  groups 1 and 5 are already closed by the handshake suites; group 2's
  TLS 1.3 half is covered, leaving the TLS 1.2 else-arm (client.rs:316);
  new group added for VerifyPresentedCertVerifier::verify_tls12_signature
  (server.rs:349-366, opened by ADR-008; required for the >=98% bar);
  AcceptAnyCertVerifier refs moved to server.rs:468-475 with the stale
  OQ-TLS-09 coordination caveat retired.
- docs-pin-c1-c4-n3-n4: N-4 re-scoped (the mechanism analysis already
  lives in ADR-007 + the resolver doc block; what remains is a short
  server-verifier note covering both verifiers); C-4 updated for
  ADR-007's negotiation-earlier failure point; added the
  FingerprintPinVerifier pop cross-ref update (post-ADR-008 the default
  verifier does verify possession).
- config-validation-and-trivia: added the feature-gate mechanics note
  for the C-3 test (a non-gated test passes vacuously under default
  features); refreshed drifted line refs with a re-grep advisory.
- review 001: Status block records OQ-TLS-09/-10 resolutions; U-1
  carries the termination correction; U-2 carries the supersession
  note. ADR-008 gains the suite-number-to-test-name mapping.

Verification: taskgraph validate 14 tasks; cargo doc --no-deps
warning-free; all edits docs-only (no code paths touched).
2026-09-12 02:42:20 +00:00
glm-5.3-flash ac440f3a9a ADR-008: server-path possession verification — the verifying verifier is the default (OQ-TLS-09 resolved)
Close review 001 §S-1: the default client-cert verifier never checked
the client's CertificateVerify, so anyone holding a peer's *public*
cert/SPKI bytes (public by design — peers publish them to be dialable)
could complete a handshake as that peer, and the auth layer could not
detect it. The consumer designs are known (X.509 and raw-key TCP/QUIC
endpoints with identity-bearing clients), so implementing now — the
zero-consumer moment — avoids the guaranteed breaking republish of
flipping the default later.

- VerifyPresentedCertVerifier (new): request, don't require, verify
  possession — permissive verify_client_cert (self-signed chains and
  bare SPKIs stay valid presentation) + CertificateVerify routing by
  presented cert kind (Ed25519 SPKI -> verify_tls13_signature_with_
  raw_key both TLS versions; X.509 -> standard route), the same
  routing FingerprintPinVerifier implements. Nine-scheme list
  verbatim (shared fn, exact-list pin covers both).
- Default on every TlsServerConfig path — X509 / RawKey / SelfSigned
  / ACME (the verifier install is crate-side rustls in new_acme, not
  rustls-acme's).
- AcceptAnyCertVerifier stays public as the explicit no-pop escape
  hatch, no longer installed by any crate path.
- tests/impersonation_posture.rs: four pins — default rejects the
  attacker (X.509: UnsupportedSignatureAlgorithmForPublicKeyContext;
  raw-key: BadSignature), escape hatch still accepts + extracts the
  victim's fingerprint (both cert types).
- tests/handshake_behavior.rs: suites 4/4b — possession-checked legit
  clients (raw-key pin vs raw-key server; X.509 client vs X.509
  server) complete and the server extracts the fingerprint; suite 3b
  doc updated.
- Docs: ADR-008 written; OQ-TLS-09 -> resolved (option (b));
  ADR-007 §Limits deferral retired to not-planned; server.md/client.md
  invariants/README/overview synced.

Verification: cargo test 81 / --features tcp 95 / --all-features 104
green; clippy -D warnings clean (default + all-features); fmt clean;
cargo doc warning-free.
2026-09-11 11:12:48 +00:00
glm-5.3-flash 49d4432247 ADR-007: RFC 7250 cert-type negotiation — the offer follows the identity (OQ-TLS-10 resolved)
Resolve the cert-type negotiation gap (review 001 §U-3, OQ-TLS-10) by
deviation from alknet: the gap was a defect in the prior art (alknet's
code never delivered its spec's raw-key-over-TCP promise — ADR-082
"works for both QUIC and TCP+TLS"), not behavior to preserve.

- FingerprintPinVerifier::requires_raw_public_keys() derives from the
  pin format: ed25519: -> true (offer [RawPublicKey]), SHA256: ->
  false (X.509 offer). Crate pin client now completes against the
  crate raw-key server; SHA256: pins negotiate unchanged.
- RawKeyClientCertResolver presents the SPKI under the X.509 offer
  (only_raw_public_keys() == false): a raw-only client offer can only
  negotiate against a requires_raw server verifier, and
  AcceptAnyCertVerifier correctly stays on the default (accepts both
  cert types). The server extracts the ed25519: fingerprint from the
  SPKI bytes either way.
- Fail-closed preserved and strengthened: an ed25519: pin against an
  X.509 server now aborts at negotiation (suite 2b), never a
  downgrade; no API change (no public signature affected; the fix is
  invisible to consumers apart from working handshakes).
- tests/handshake_behavior.rs: suite 3 now runs crate-native (no
  custom iroh-shaped verifier), new negotiation fail-closed suite,
  suite 3b inverted to end-to-end success; invariant_pins.rs
  resolver-offer assertions flipped; unused imports dropped.
- Docs: ADR-007 written; OQ-TLS-10 -> resolved-by-deviation;
  client.md/server.md/overview/README/task postscript synced
  (incl. the strict-foreign-server limit in ADR-007 §Limits).

Verification: cargo test 81 / --features tcp 94 / --all-features
105 green; clippy -D warnings clean (default + all-features); fmt
clean; cargo doc warning-free.
2026-09-11 10:26:28 +00:00
glm-5.3-flash 4a4fae64af task 2: handshake-level suites — pin, fail-closed, RFC 7250 paths executed (U-3)
tests/handshake_behavior.rs (tcp-gated, tokio duplex + tokio-rustls,
no new deps) turns the fail-closed / pin / raw-key language into
executed behavior:

- pin match: X.509 server + SHA256 pin -> handshake completes, app
  data round-trips, server extracts the client cert fingerprint
- pin mismatch: wrong pin -> handshake error (the pin IS the anchor)
- fail closed: remote_identity None + raw-key server -> HandshakeFailure
- raw-key server path end-to-end: completes with the iroh-shaped
  client verifier (requires_raw_public_keys == true); presented cert
  asserted to be the SPKI carrying the raw Ed25519 key
- N-4's interop trap executed: raw-key client resolver vs
  AcceptAnyCertVerifier -> IncorrectCertificateTypeExtension alert

Major finding, recorded as OQ-TLS-10 (open): a crate-built pin
client cannot reach a crate-built raw-key server over rustls TCP+TLS
— the raw-key resolver requires the client to offer [RawPublicKey]
server cert types, sent only when the client verifier overrides
requires_raw_public_keys() == true. FingerprintPinVerifier keeps the
trait default false (AcceptAnyCertVerifier too); iroh's verifier
overrides true on both sides. Gap inherited from alknet
(behavior-preserving); pinned both ways by the suite.

client.md / server.md carry the interop notes; task file updated
(premise adjustments documented in Notes, summary filled).

Verification: 81 default / 91 tcp / 99 all-features tests green
(+5 new), clippy -D warnings clean both configs, fmt clean,
cargo doc warning-free, taskgraph validate 14 tasks.
2026-09-11 08:17:28 +00:00
glm-5.3-flash d82956385b record S-1/N-1 remediation status + the N-4 parenthetical correction
- review 001: §Status carries the remediation status (S-1 + N-1 landed
  in e86b8ba, OQ-TLS-09 open, probe permanent) and the N-4 correction
  summary; §S-1 remediation-task line updated; §N-1 marked remediated;
  §N-4 gains the corrected mechanism chain (rustls 0.23.41/0.23.44 send
  [RawPublicKey] iff the resolver's only_raw_public_keys() is true —
  no "offers both types" behavior exists; a raw-key client fails
  IncorrectCertificateTypeExtension against this verifier; the S-1
  probe presents the SPKI under the default X.509 offer instead)
- task fix-accept-any-cert-verifier-posture: drop the placeholder
  fill-in lines (Notes/Summary carry the actual record)
- task docs-pin-c1-c4-n3-n4: N-4 work item rewritten from the
  corrected chain so the implementing agent does not re-derive it;
  references point at the rustls source lines and the probe's
  resolver choice
2026-09-11 07:38:21 +00:00
glm-5.3-flash e86b8ba1b5 task 1: S-1 remediation — no-pop posture doc + OQ-TLS-09 + permanent impersonation pin
- AcceptAnyCertVerifier doc: the presented CertificateVerify signature
  is not verified (no proof-of-possession) — the server-extracted
  fingerprint is attacker-suppliable from observed public cert/SPKI
  bytes; states the two safe patterns (auth-layer challenge-response /
  a verifying verifier) and points at OQ-TLS-09
- FingerprintPinVerifier doc (N-1): fixed the "stolen-but-stale
  fingerprint" phrasing (the cert is presented fresh each handshake;
  the signature check defeats a stolen/observed cert used by a party
  without the private key) and added the server-verifier cross-reference
- OQ-TLS-09 recorded (open, high): which layer owns server-path
  proof-of-possession — three options; deferral noted (needs the
  auth-layer design or an API call before the first consumer)
- tests/impersonation_posture.rs (tcp-gated): the S-1 probe made
  permanent, both variants — X.509 victim cert + attacker key and RFC
  7250 victim SPKI + attacker key complete the handshake, application
  data flows, and the server extracts the victim's fingerprint; any
  future pop change must fail/update this test with the doc + OQ
- server.md / client.md synced with the same posture
- task note: the review's N-4 parenthetical ("alknet's client resolver
  offers both types") is inaccurate — rustls 0.23.41/0.23.44 offer
  [RawPublicKey] iff the resolver's only_raw_public_keys() is true;
  task 6 should write N-4 from the rustls sources

Verified: cargo test 68 default / 77 all-features (+2) green; clippy
-D warnings clean (default + all-features); fmt clean; cargo doc
--no-deps warning-free
2026-09-10 22:00:57 +00:00
glm-5.3-flash d7db6b17a6 review-001 decomposition: 6 remediation tasks from verified findings
Verify every review-001 finding before decomposition:
- S-1 re-confirmed by fresh executable probe (X.509 + raw-key
  impersonation both complete the handshake with the victim's
  fingerprint extracted server-side)
- U-2/U-3 uncovered-line inventory re-derived from cargo llvm-cov
  (--all-features); matches Part C exactly
- rcgen 1975/4096 defaults, rustls cert-type negotiation arm,
  packaging list, doc texts: all verified against sources

Tasks (all verified, none speculative):
- fix-accept-any-cert-verifier-posture (S-1 + N-1 + OQ-TLS-09 + probe)
- handshake-tests (U-3 suites 1-3; suite 4 lives in the S-1 task)
- coverage-cheap-closes (U-2's seven groups)
- acme-event-loop-test (U-1; depends on coverage-cheap-closes for the
  tracing-capture pattern)
- config-validation-and-trivia (C-2 ALPN dedup, C-3 empty-domains,
  N-6 excludes, N-7 https doc line)
- docs-pin-c1-c4-n3-n4 (C-1, C-4, N-3 decision note, N-4 negotiation
  note)

Graph: acme-event-loop-test is generation 2; the rest run in
generation 1. taskgraph validate: 14 tasks, no cycles.

Verification: cargo test (default + --all-features) green before and
after; probe file deleted after its run.
2026-09-10 21:16:13 +00:00
glm-5.3-flash bb0d060135 review 001: alktls v1 implementation — correctness, security, coverage
Independent post-port review (behavior-preservation + security +
coverage focus; the alknet sources consulted only as prior art).

Findings:
- S-1 [major, security posture]: AcceptAnyCertVerifier does not verify
  the client CertificateVerify (no proof-of-possession) — executable
  probe confirms a victim-cert + attacker-key handshake completes and
  the server extracts the victim's fingerprint (both X.509 and RFC
  7250 SPKI variants). Doc/ADR remediation, not a behavior change
  (the shape is the alknet-inherited design).
- C-1..C-4 [minor]: self-signed cert validity 1975-4096 undocumented;
  acme-tls/1 duplicated if caller supplies it; empty ACME domains
  construct without validation; malformed/case-shifted fingerprint
  pins reject at handshake with no config-layer doc.
- U-1..U-3 [minor, coverage]: ACME event-loop body uncovered (23
  lines); resolvers' resolve() methods uncovered; zero
  handshake-level tests (fail-closed/pin/raw-key paths asserted only
  structurally); webpki-roots fallback loop nondeterministically
  covered.
- N-1..N-7: doc nits, two non-findings (dangerous()-downgrade suspect
  retracted; zeroize marker verified inert via ed25519-dalek default
  features), packaging noise (tasks/ + docs/architecture/ ship),
  requires_raw_public_keys interaction note (raw-only clients fail
  against request-but-don't-require), http:// custom ACME directory
  warning.
- Behavior preservation: full src/ diff vs alknet-tls + alknet-core —
  zero unrecorded behavioral divergences (Part B).

Verification: cargo test 68 default / 75 --all-features; clippy -D
warnings (both configs); fmt --check; cargo doc --no-deps;
cargo publish --dry-run --allow-dirty; cargo llvm-cov --all-features
95.32% line / 94.48% functions, uncovered-line inventory in the doc;
temporary review probes (fingerprint pins, DER adversarial battery,
empty-PEM, ACME construction edges, impersonation handshake) all run
and deleted after the run.
2026-09-10 16:14:05 +00:00
glm-5.3-flash 8a15978c41 generation 5: review-impl — spec-conformance gate passed, API frozen
Checklist (all six PASS):
1. API surface == ADR-004 — every accessor signature verified verbatim
2. TlsError == ADR-002 — six variants, #[non_exhaustive], typed sources,
   AcmeConfig holds exactly the two config-mismatch cases
3. Invariants: all five server invariants + client 0-RTT half +
   fail-closed structure, each with a passing behavioral test at unit
   and integration level
4. Deltas vs extraction: all ADR-pinned; two surfaced divergences
   recorded as ADR amendments — zero un-pinned divergences remain
5. Feature hygiene: default = [] lean, tokio subset (no full),
   doc comments on public API, no inline // comments, no panics
6. Docs sync: ADR-002 + ADR-003 amendment notes; overview/server/client
   Draft → Reviewed; README carries the API-freeze lifecycle note

5 findings, all low severity, all resolved forward (table in task Notes)

Verification: cargo test (81), cargo test --all-features (92),
clippy -D warnings, fmt --check, doc --no-deps,
publish --dry-run — all green. API FROZEN for the alknet rewrite.
2026-09-10 15:05:01 +00:00
glm-5.3-flash 87b69e19e6 generation 4: integration suite — seam round-trips + invariant pins
tests/server_seams.rs:
- TlsServerConfig::new per identity variant → rustls_config() +
  for_tcp_tls (tcp) + for_noq (noq); ALPN + max_early_data_size=u32::MAX
  asserted per path; Acme-no-feature → AcmeConfig cell

tests/client_seams.rs:
- TlsClientConfig::new per credentials cell → into_rustls_config +
  for_noq; enable_early_data=true + single-value ALPN pinned

tests/invariant_pins.rs:
- nine-scheme exact-list pin (vec equality, order included)
- verifier-selection + client-auth presentation matrices via public
  API (Debug probe for the pub(super) verifier, direct resolver
  introspection); root-store fallback non-empty

tests/acme_lifecycle.rs (#![cfg(feature = acme)]):
- spawn-and-return, acme-tls/1 in ALPN, resolver wiring, 0-RTT on the
  ACME branch; blackhole + staging URLs — zero network I/O

Verification: all five feature combos green (default 68+13, noq 72+16,
tcp 71+13, acme 68+11, all-features 75+17), clippy -D warnings,
fmt --check, doc --no-deps
2026-09-10 15:00:06 +00:00
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
glm-5.3-flash 4bdc12e84f generation 2: port identity types, fingerprint, pem + signing
port-identity-types (src/identity.rs):
- TlsIdentity (four variants), Ed25519SecretKey, AcmeDirectory ported
  verbatim from alknet-core config.rs; OQ-TLS-02 + server-only docs
  on the variants; 9 in-module tests incl. Debug-no-leak
- dep decision: rand_core 0.6 (+getrandom) with ed25519-dalek
  rand_core feature, NOT rand (lockfile rand 0.10/rand_core 0.10
  traits are incompatible with ed25519-dalek 2.2's CryptoRngCore);
  rand stays out of the tree entirely

port-fingerprint (src/fingerprint.rs):
- fingerprint_from_cert_der, extract_ed25519_raw_key_from_spki,
  DerParser ported verbatim; production code sha2 + manual DER
  (+hex for the normalized formats)
- 16 tests: 7 ported + 9 new malformed-DER edges (the extraction
  had none despite the invariant naming them)
- empty-input behavior: matches extraction's actual code (always
  Some via the SHA-256 fallback); doc records the deviation from
  the stale None claim

port-pem-signing (src/pem.rs, src/signing.rs):
- load_cert_chain/load_private_key remapped to TlsError::CertLoad
  per ADR-002; InvalidData no-key path kept
- Ed25519SigningKey rewired to crate::identity::Ed25519SecretKey
  (the one intentional change); rcgen PEM round-trip test added

lib.rs re-export block: fingerprint + pem + signing + identity lines
landed; server/client/credentials pending their port tasks

Verification: cargo test (36), cargo test --all-features (37),
clippy -D warnings (default+all-features), fmt --check, feature
checks (noq/tcp/acme) — all green
2026-09-10 13:48:32 +00:00
glm-5.3-flash f68234133f crate-init: crate skeleton — module map, feature gates, TlsError, re-export block deferred per port
- src/lib.rs: crate docs, ADR-002 TlsError verbatim (six variants,
  #[non_exhaustive], typed #[source] chains), module declarations,
  compile-assertion tests for the enum + noq-gated NoqWrap
- seven module files with doc headers only (ports land per task)
- Cargo.toml verified against ADR-003 verbatim (no edits needed)
- deviation: VerifierBuild source is rustls::client::VerifierBuilderError
  (rustls::webpki is private at pinned 0.23.44; same type, public path)

Verification: cargo build/test (default, noq, tcp, acme, all-features),
clippy -D warnings, fmt --check, doc --no-deps — all green
2026-09-10 13:27:46 +00:00
glm-5.3-flash 05b3832c88 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)
2026-09-10 06:32:44 +00:00
glm-5.3-flash d74a27f764 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
2026-09-10 05:37:55 +00:00
glm-5.3-flash e93238cb4a adopt ecosystem MSRV floor 1.88; drop the time pin
- rust-version 1.85 -> 1.88 (ecosystem-wide resolution from the audit
  sessions; matches noq 1.2's floor, sits below iroh 1.91)
- Cargo.lock: time 0.3.36 pin removed (existed only to keep the 1.85
  claim satisfiable); tree re-floated to current
- phase-0: MSRV thread marked RESOLVED; OQ-TLS-08 narrowed to the
  quinn->noq feature-rename half; lesson recorded (rust-version is
  passive metadata — claims must be compile-checked, not assumed)

Verified: cargo test, test --all-features, clippy -D warnings,
fmt --check, rustup run 1.88 cargo check
2026-09-10 03:51:31 +00:00
glm-5.3-flash 6bd19994f8 phase-0: noq/iroh investigation — quinn→noq shift recorded
- noq 1.2.0 published (iroh's extracted quinn fork); iroh 1.1.0 is
  built on noq 1.2.0 and re-exports noq as its public API
- TLS seam verified API-compatible with quinn 0.11: QuicServerConfig/
  QuicClientConfig try_from + NoInitialCipherSuite — port is mechanical
- one rustls 0.23 tree across all QUIC paths (both pin 0.23.33);
  ADR-084 posture strengthens (no ring/aws-lc provider split possible)
- iroh key surface unchanged (32-byte Ed25519 SecretKey) —
  key-not-config exception intact
- OQ-TLS-08 opened: quinn feature → noq feature rename + honest MSRV
  floor (noq 1.88, iroh 1.91); OQ-TLS-04/07 updated; survey + checklist
  extended

Verified: cargo fmt --check, cargo test, cargo doc --no-deps
2026-09-09 16:45:41 +00:00
glm-5.3-flash a570bee0fe repo scaffold + AGENTS.md + Phase 0 research
- Cargo scaffold: feature gates (quinn/tcp/acme), lean tokio subset,
  placeholder lib; Cargo.lock committed with time pinned to 0.3.36 so
  rust-version = 1.85 is actually satisfiable (rcgen's default time
  resolution requires 1.88 — alknet-tls fails the same check)
- AGENTS.md adapted from alktunnels: TLS-crate conventions (behavior-
  preservation invariants, fail-closed verifier selection, one ACME
  state machine, config-construction scope boundary, no wasm target)
- .opencode/agents: implementation-specialist conventions + coordinator
  prompt template + architect deferral examples updated for alktls
- docs/research/phase-0.md: extraction inventory with verified
  invariants (line-referenced), spec-vs-code gaps (TlsError shape,
  for_tcp_tls, config-type ownership), rewrite requirements,
  OQ-TLS-01..07, MSRV verification record

Verified: cargo test, clippy -D warnings, fmt --check, doc --no-deps,
test --all-features, rustup run 1.85 cargo check
2026-09-09 16:25:55 +00:00
glm-5.3-flash dbc77af3d3 init 2026-09-09 14:47:09 +00:00