- Add 7 registry tests (handler_registry_*) to registry.rs - Add 4 dispatch tests (build_auth_context_*, dispatch_decision_logic_*) to dispatch.rs - Add 6 endpoint tests to endpoint.rs: - debug_for_alknet_endpoint_is_implemented_without_panicking - endpoint_constructs_with_iroh_raw_key_identity (adapted for new API) - iroh_endpoint_runs_accept_loop_and_shutdown (adapted for new API) - with_iroh_sets_field (replaces has_iroh_identity_true_for_raw_key) - without_iroh_field_is_none (replaces has_iroh_identity_false_for_x509) - endpoint_works_without_iroh (replaces has_iroh_identity_false_when_no_identity) - Add async-trait as dev-dependency - All 17 tests pass with iroh feature - All 8 tests pass without features - Workspace tests all pass (no breakage) - Mark tasks 1-7 as completed
5.9 KiB
id, name, status, depends_on, scope, risk, impact, level
| id | name | status | depends_on | scope | risk | impact | level | |
|---|---|---|---|---|---|---|---|---|
| endpoint/accept-iroh | Implement iroh accept loop and extractors in alknet-endpoint | completed |
|
narrow | low | component | implementation |
Description
Phase 2, Task 6 of the crate extraction. Implement the iroh accept loop and
transport-specific extractors in crates/alknet-endpoint/src/accept/iroh.rs.
Extract from crates/alknet-core/src/endpoint.rs lines 390-472:
run_iroh_accept_loop, extract_iroh_client_fingerprint.
The old code stays in endpoint.rs (duplicated) — no breakage.
Types to extract
From endpoint.rs lines 390-472:
| Type/Function | Lines | Destination |
|---|---|---|
run_iroh_accept_loop() |
391-437 | accept/iroh.rs |
extract_iroh_client_fingerprint() |
469-472 | accept/iroh.rs |
Adaptations
-
run_iroh_accept_loop→run_accept_loop: Rename since it's already in theirohmodule. Takesiroh::Endpoint,Arc<HandlerRegistry>,Arc<dyn IdentityProvider>,&mut watch::Receiver<bool>. -
Connection conversion: After the handshake completes, convert the
iroh::endpoint::Connectiontoalknet_core::ConnectionviaConnection::from_iroh(connection)— same as the old code. -
Call
dispatchinstead ofdispatch_iroh: The accept loop extracts ALPN and fingerprint, then callscrate::dispatch::dispatch_connection()(the free function). The old code calleddispatch_iroh(connection, alpn, &handlers, &identity_provider). -
Imports: Update
crate::imports toalknet_core::andcrate::. -
Feature gates: All code in this module is gated on
#[cfg(feature = "iroh")].
Implementation sketch
// accept/iroh.rs
#[cfg(feature = "iroh")]
use std::sync::Arc;
#[cfg(feature = "iroh")]
use tokio::sync::watch;
#[cfg(feature = "iroh")]
use tracing::{debug, warn};
#[cfg(feature = "iroh")]
use alknet_core::auth::IdentityProvider;
#[cfg(feature = "iroh")]
use alknet_core::types::Connection;
#[cfg(feature = "iroh")]
use crate::registry::HandlerRegistry;
#[cfg(feature = "iroh")]
pub(crate) async fn run_accept_loop(
iroh: iroh::Endpoint,
handlers: Arc<HandlerRegistry>,
identity_provider: Arc<dyn IdentityProvider>,
shutdown_rx: &mut watch::Receiver<bool>,
) {
loop {
tokio::select! {
_ = shutdown_rx.changed() => {
debug!("iroh accept loop: shutdown signaled");
break;
}
incoming = iroh.accept() => {
let Some(incoming) = incoming else {
debug!("iroh accept loop: endpoint closed");
break;
};
let handlers = handlers.clone();
let identity_provider = identity_provider.clone();
tokio::spawn(async move {
let mut connecting = match incoming.accept() {
Ok(c) => c,
Err(e) => {
warn!("iroh accept failed: {e}");
return;
}
};
let alpn = match connecting.alpn().await {
Ok(alpn) => alpn,
Err(e) => {
warn!("iroh ALPN negotiation failed: {e}");
return;
}
};
let connection = match connecting.await {
Ok(conn) => conn,
Err(e) => {
warn!("iroh handshake completion failed: {e}");
return;
}
};
let fingerprint = extract_client_fingerprint(&connection);
let conn = Connection::from_iroh(connection);
crate::dispatch::dispatch_connection(
conn, alpn, fingerprint, None, // iroh has no SocketAddr
&handlers, &identity_provider,
);
});
}
}
}
}
#[cfg(feature = "iroh")]
pub(crate) fn extract_client_fingerprint(connection: &iroh::endpoint::Connection) -> Option<String> {
let node_id = connection.remote_id();
Some(format!("ed25519:{}", node_id))
}
What stays in core
The old run_iroh_accept_loop and extract_iroh_client_fingerprint in endpoint.rs are
not deleted — they stay as duplicates. The prune happens in Phase 4.
Acceptance Criteria
accept/iroh.rscontainsrun_accept_loop,extract_client_fingerprintrun_accept_loopspawns a task per accepted connection, handles shutdown signalrun_accept_loopnegotiates ALPN viaconnecting.alpn().awaitextract_client_fingerprintextracts the irohNodeIdased25519:<node_id>- Accept loop calls
crate::dispatch::dispatch_connection()with extracted values remote_addrisNonefor iroh (noSocketAddr)- All code gated on
#[cfg(feature = "iroh")] - All imports use
alknet_core::(notcrate::from core) cargo check -p alknet-endpoint --features irohsucceedscargo clippy -p alknet-endpoint --features irohsucceeds with no warningscargo test -p alknet-corestill passes (old code untouched)
References
- docs/research/alknet-crate-extraction/findings.md — Phase 2, accept/iroh module
- docs/architecture/crates/endpoint/README.md — Accept loops (lines 177-193)
- crates/alknet-core/src/endpoint.rs — lines 390-472 (source code to extract)
Notes
This is a straightforward extraction — the iroh accept loop logic doesn't change. The main adaptation is calling
crate::dispatch::dispatch_connection()instead of the olddispatch_iroh(). Iroh connections don't have aSocketAddr, soremote_addris alwaysNone. The old code inendpoint.rsis NOT deleted.
Summary
To be filled on completion