Files
alknet/tasks/endpoint/accept-iroh.md
T
deepseek-v4-pro 5467c30892 feat(endpoint): add tests and mark tasks 1-7 as completed
- 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
2026-07-17 10:53:25 +00:00

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
endpoint/dispatch
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

  1. run_iroh_accept_loop → run_accept_loop: Rename since it's already in the iroh module. Takes iroh::Endpoint, Arc<HandlerRegistry>, Arc<dyn IdentityProvider>, &mut watch::Receiver<bool>.

  2. Connection conversion: After the handshake completes, convert the iroh::endpoint::Connection to alknet_core::Connection via Connection::from_iroh(connection) — same as the old code.

  3. Call dispatch instead of dispatch_iroh: The accept loop extracts ALPN and fingerprint, then calls crate::dispatch::dispatch_connection() (the free function). The old code called dispatch_iroh(connection, alpn, &handlers, &identity_provider).

  4. Imports: Update crate:: imports to alknet_core:: and crate::.

  5. 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.rs contains run_accept_loop, extract_client_fingerprint
  • run_accept_loop spawns a task per accepted connection, handles shutdown signal
  • run_accept_loop negotiates ALPN via connecting.alpn().await
  • extract_client_fingerprint extracts the iroh NodeId as ed25519:<node_id>
  • Accept loop calls crate::dispatch::dispatch_connection() with extracted values
  • remote_addr is None for iroh (no SocketAddr)
  • All code gated on #[cfg(feature = "iroh")]
  • All imports use alknet_core:: (not crate:: from core)
  • cargo check -p alknet-endpoint --features iroh succeeds
  • cargo clippy -p alknet-endpoint --features iroh succeeds with no warnings
  • cargo test -p alknet-core still 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 old dispatch_iroh(). Iroh connections don't have a SocketAddr, so remote_addr is always None. The old code in endpoint.rs is NOT deleted.

Summary

To be filled on completion