Three tasks implementing ADR-027: 1. core/rawkey-decouple-from-iroh: TlsIdentity::RawKey now uses Ed25519SecretKey (alknet-core-owned wrapper over ed25519_dalek) instead of iroh::SecretKey. RawKeyCertResolver and Ed25519SigningKey un-gated from #[cfg(all(quinn, iroh))] to #[cfg(quinn)] only. Quinn-only builds (default) now support RFC 7250 raw-key identity. iroh transport converts via iroh::SecretKey::from_bytes. 2. core/endpoint-request-client-cert: replaced with_no_client_auth() with AcceptAnyCertVerifier — a custom ClientCertVerifier that requests client certs but doesn't require them or verify against a CA. alknet's identity model is fingerprint-based (the authorized_fingerprints set is the trust anchor), not PKI-based. Peer certs are extracted at the TLS layer for fingerprinting; peers without certs connect normally. 3. core/acme-integration: TlsIdentity::Acme variant (domains, cache_dir, directory, contact) + AcmeDirectory enum. TlsSetup two-phase construction: synchronous for X509/RawKey/SelfSigned, async for Acme (spawns AcmeState event loop, builds ServerConfig with ResolvesServerCertAcme). acme-tls/1 ALPN added when ACME is active; dispatch_quinn guard closes challenge connections gracefully (challenge is TLS-layer-handled). acme feature gate keeps rustls-acme out of non-ACME builds. Workspace: build/test/clippy green across all 3 feature configs (quinn-only, quinn+iroh, quinn+acme, all-features). 331 tests, 0 failures, 0 warnings.
5.4 KiB
id, name, status, depends_on, scope, risk, impact, level
| id | name | status | depends_on | scope | risk | impact | level |
|---|---|---|---|---|---|---|---|
| core/rawkey-decouple-from-iroh | Decouple TlsIdentity::RawKey from the iroh feature (ADR-027) | completed | narrow | medium | component | implementation |
Description
TlsIdentity::RawKey(iroh::SecretKey) is gated #[cfg(feature = "iroh")]
and the RawKeyCertResolver / Ed25519SigningKey rustls impls are gated
#[cfg(all(feature = "quinn", feature = "iroh"))]. This means quinn-only
builds (the default feature set) cannot use RFC 7250 raw-key identity —
the mode described as "default for most alknet nodes" (OQ-12, ADR-027).
The coupling is artificial: iroh::SecretKey is a thin newtype over
ed25519_dalek::SigningKey. The alknet code uses only .public().as_bytes(),
.sign(msg), and .clone(). This task replaces iroh::SecretKey with an
alknet-core-owned Ed25519SecretKey wrapper, un-gates the raw-key TLS
path from the iroh feature, and updates the iroh transport to convert.
See ADR-027 for the full design rationale.
Implementation steps
-
Add
ed25519-dalekas a direct dependency of alknet-core inCargo.toml. It's already in the lockfile (transitive via iroh). Version:2.2(match what's inCargo.lock). -
Introduce
Ed25519SecretKeyinconfig.rs(or a newtls.rsmodule if config.rs is getting large):#[derive(Clone)] pub struct Ed25519SecretKey(ed25519_dalek::SigningKey); impl Ed25519SecretKey { pub fn generate() -> Self { ... } pub fn from_bytes(bytes: &[u8; 32]) -> Self { ... } pub fn as_bytes(&self) -> &[u8; 32] { ... } pub fn public(&self) -> ed25519_dalek::VerifyingKey { ... } }Add
ZeroizeOnDrop(the key is secret material). Add a redactingDebugimpl (likeSecret<T>in types.rs). Do NOT deriveDebug— the raw key bytes must not be printed. -
Change
TlsIdentity::RawKeyfromRawKey(iroh::SecretKey)toRawKey(Ed25519SecretKey). Remove the#[cfg(feature = "iroh")]gate —RawKeyis available in all builds. -
Rewire
Ed25519SigningKeyinendpoint.rs:- Change the inner field from
iroh::SecretKeytoEd25519SecretKey(ored25519_dalek::SigningKey). spki_public_key(): useself.key.public().as_bytes()(same logic, different key type —ed25519_dalek::VerifyingKeyhasas_bytes()).sign(): useself.key.sign(message)→ ed25519-dalek'sSigningKey::signreturnsSignaturewhich hasto_bytes().- Change the cfg gate from
#[cfg(all(feature = "quinn", feature = "iroh"))]to#[cfg(feature = "quinn")]onRawKeyCertResolver,Ed25519SigningKey, and all related impls.
- Change the inner field from
-
Update
build_iroh_endpoint: whenTlsIdentity::RawKey(key)is present, convert toiroh::SecretKey::from_bytes(key.as_bytes())before passing toiroh::Endpoint::builder().secret_key(...). This conversion is#[cfg(feature = "iroh")]only. -
Update
build_rustls_server_config: theRawKeyarm changes from#[cfg(feature = "iroh")]to always-available (within the#[cfg(feature = "quinn")]function). TheRawKeyCertResolver::newtakes&Ed25519SecretKeyinstead of&iroh::SecretKey. -
Update all tests that construct
TlsIdentity::RawKey:endpoint.rstests:iroh::SecretKey::generate(&mut csprng)→Ed25519SecretKey::generate().- Any test in
config.rsthat constructsRawKey.
What NOT to change
TlsIdentity::X509andSelfSigned— untouched by this task.- The
endpoint-request-client-certtask (server config client auth) — independent, can proceed in parallel or before/after this task. - ACME — separate follow-up task (
core/acme-integration).
Acceptance Criteria
ed25519-dalekis a direct dependency of alknet-coreEd25519SecretKeytype exists withgenerate,from_bytes,as_bytes,public; redactingDebug;ZeroizeOnDropTlsIdentity::RawKeyusesEd25519SecretKey, notiroh::SecretKeyTlsIdentity::RawKeyis not gated behind#[cfg(feature = "iroh")]RawKeyCertResolverandEd25519SigningKeyare gated#[cfg(feature = "quinn")]only (notall(feature = "quinn", feature = "iroh"))build_iroh_endpointconvertsEd25519SecretKey→iroh::SecretKey::from_bytescargo build -p alknet-core --features quinn(no iroh) succeeds withTlsIdentity::RawKeyusablecargo build -p alknet-core --all-featuressucceedscargo test -p alknet-core --all-featuressucceedscargo test -p alknet-core --features quinnsucceeds (quinn-only, no iroh)cargo clippy -p alknet-core --all-features --all-targetscleancargo clippy -p alknet-core --features quinn --all-targetsclean
References
- ADR-027 — full design rationale
- crates/alknet-core/src/config.rs:33-41 —
TlsIdentityenum - crates/alknet-core/src/endpoint.rs:593-689 —
RawKeyCertResolver,Ed25519SigningKey - crates/alknet-core/src/endpoint.rs:511-538 —
build_iroh_endpoint(conversion site) - crates/alknet-core/src/endpoint.rs:484-495 —
build_rustls_server_configRawKey arm - /workspace/iroh/iroh-base/src/key.rs:261 —
iroh::SecretKey(SigningKey)newtype
Notes
This is the foundation task for ADR-027. The ACME task (
core/acme-integration) depends on this one because both modifyTlsIdentityandbuild_rustls_server_config. Doing decoupling first means the ACME task builds on the cleaned-up enum without iroh coupling.