What Are Connection Hooks in Iroh? Lifecycle Interception for Endpoints
Connection hooks in Iroh are asynchronous extension points defined by the EndpointHooks trait that let you intercept QUIC connections either before they are established or immediately after the TLS handshake completes, enabling programmatic acceptance or rejection based on custom logic.
Connection hooks provide a flexible mechanism for controlling the lifecycle of connections in the n0-computer/iroh repository. These hooks allow you to inspect remote addresses, verify identities, and enforce policies without modifying the core protocol implementation.
Understanding the EndpointHooks Trait
The connection hook system centers on the EndpointHooks trait defined in iroh/src/endpoint/hooks.rs. This trait exposes two lifecycle methods that correspond to distinct phases of connection establishment.
Before Connect Phase
The before_connect method runs whenever Endpoint::connect or connect_with_opts is invoked, but before any packets are sent to the remote peer. This method receives the remote address and ALPN protocol identifier, returning a BeforeConnectOutcome enum containing either Accept or Reject.
If any hook returns Reject during this phase, the connection attempt aborts immediately without generating network traffic. This early rejection is useful for firewall-style filtering or blocking specific protocols.
After Handshake Phase
Once the TLS handshake completes successfully, the after_handshake method executes. This hook receives a reference to the established Connection and returns an AfterHandshakeOutcome, which can be either Accept or Reject { error_code, reason }.
Rejecting at this stage closes the connection with the specified error code and reason string, allowing for identity verification after cryptographic authentication but before application data flows.
Hook Execution Order
Hooks are stored in an EndpointHooksList backed by Vec<Box<dyn DynEndpointHooks>> as implemented in iroh/src/endpoint/hooks.rs. They execute sequentially in the order they were added via Builder::hooks(). If any hook returns Reject, subsequent hooks are skipped for that phase, creating a short-circuit evaluation pattern.
Implementing Custom Connection Hooks
To create a connection hook, implement the EndpointHooks trait and attach it to your endpoint using the builder pattern. The following example demonstrates a hook that blocks connections to a specific ALPN protocol.
use iroh::endpoint::{Builder, EndpointHooks, BeforeConnectOutcome, AfterHandshakeOutcome};
use iroh_base::EndpointAddr;
use std::future::Future;
use std::pin::Pin;
/// A hook that forbids connections to a specific ALPN.
#[derive(Debug)]
struct BlockAlpnHook;
impl EndpointHooks for BlockAlpnHook {
fn before_connect<'a>(
&'a self,
_remote_addr: &'a EndpointAddr,
alpn: &'a [u8],
) -> Pin<Box<dyn Future<Output = BeforeConnectOutcome> + Send + 'a>> {
Box::pin(async move {
if alpn == b"blocked-protocol" {
BeforeConnectOutcome::Reject
} else {
BeforeConnectOutcome::Accept
}
})
}
fn after_handshake<'a>(
&'a self,
_conn: &'a iroh::endpoint::connection::Connection,
) -> Pin<Box<dyn Future<Output = AfterHandshakeOutcome> + Send + 'a>> {
// No extra checks after handshake; just accept.
Box::pin(async { AfterHandshakeOutcome::accept() })
}
}
Attach the hook to your endpoint before binding:
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Build an endpoint and install the hook.
let endpoint = Builder::default()
.hooks(BlockAlpnHook) // ← attach the hook
.bind(([0, 0, 0, 0], 0).into())?
.await?;
// Any attempt to connect using the blocked ALPN will be rejected
// before any packets are sent.
let _ = endpoint
.connect(("example.com", 443), b"blocked-protocol")
.await; // This will fail with BeforeConnectOutcome::Reject
Ok(())
}
Practical Use Cases for Connection Hooks
Connection hooks enable several powerful patterns in Iroh applications:
- Authentication: Verify tokens or certificates before allowing connections to proceed, as demonstrated in
iroh/examples/auth-hook.rs. - Access Control: Implement blacklists or whitelists based on remote endpoint addresses or ALPN protocols.
- Telemetry and Monitoring: Record connection metadata, remote IDs, and timestamps without interfering with the connection flow, shown in
iroh/examples/monitor-connections.rs. - Remote Information Mapping: Build dynamic maps of endpoint information as implemented in
iroh/examples/remote-info.rs.
Critical Implementation Details
When working with connection hooks, specific safety constraints documented in the source code must be observed.
Memory Safety Considerations
According to lines 62-64 of iroh/src/endpoint/hooks.rs, hook implementations must not maintain strong references to the Endpoint. Because hooks are stored on the endpoint itself, a circular reference would prevent both from being dropped, causing memory leaks. Use std::sync::Weak if you need to reference the endpoint from within a hook.
Source Code Integration
The hook mechanism is invoked in iroh/src/endpoint/connection.rs at line 353, where the code calls inner.hooks.before_connect() and inner.hooks.after_handshake(&conn). Hook registration occurs in iroh/src/endpoint/bind.rs through the Builder::hooks() method, which populates the EndpointHooksList used during connection establishment.
Summary
- Connection hooks in Iroh implement the
EndpointHookstrait to intercept connections at two distinct phases: before connect and after TLS handshake. - The
before_connecthook aborts connections before network packets are transmitted, whileafter_handshakecan close established connections with specific error codes. - Hooks execute sequentially in registration order and short-circuit on the first
Rejectoutcome, skipping subsequent hooks. - Always avoid strong references to the
Endpointwithin hook implementations to prevent reference-count cycles and memory leaks. - Hooks are attached via
Builder::hooks()and invoked iniroh/src/endpoint/connection.rsduring the connection lifecycle.
Frequently Asked Questions
What is the difference between before_connect and after_handshake hooks in Iroh?
The before_connect hook runs before any network packets are transmitted when Endpoint::connect is called, allowing you to reject connections based on the remote address and ALPN without network overhead. The after_handshake hook executes after the TLS handshake completes, giving you access to the authenticated remote endpoint identity but requiring the full cryptographic exchange to finish first.
Can I use multiple connection hooks in a single Iroh endpoint?
Yes. You can attach multiple hooks using successive calls to Builder::hooks(), and they will be stored in an internal EndpointHooksList. Hooks execute in the order they were added, and if any hook returns Reject during either phase, subsequent hooks are skipped for that specific connection attempt.
How do I prevent memory leaks when implementing connection hooks?
As documented in iroh/src/endpoint/hooks.rs lines 62-64, your hook must not store a strong reference to the Endpoint. Because the endpoint owns the hooks, a circular reference would prevent both from being dropped. Use weak references or avoid storing the endpoint entirely to ensure proper memory management.
Where are connection hooks invoked in the Iroh source code?
Connection hooks are invoked in iroh/src/endpoint/connection.rs at line 353, where the connection logic calls inner.hooks.before_connect() and inner.hooks.after_handshake(). The trait definition resides in iroh/src/endpoint/hooks.rs, and hook registration is handled in iroh/src/endpoint/bind.rs through the Builder::hooks() method.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →