# What Are Connection Hooks in Iroh? Lifecycle Interception for Endpoints

> Integrate custom logic into your Iroh application with connection hooks. Intercept QUIC connections before establishment or after TLS handshake for programmatic acceptance or rejection.

- Repository: [number zero/iroh](https://github.com/n0-computer/iroh)
- Tags: deep-dive
- Published: 2026-07-15

---

**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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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.

```rust
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:

```rust
#[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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/monitor-connections.rs).
- **Remote Information Mapping**: Build dynamic maps of endpoint information as implemented in [`iroh/examples/remote-info.rs`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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 `EndpointHooks` trait to intercept connections at two distinct phases: before connect and after TLS handshake.
- The `before_connect` hook aborts connections before network packets are transmitted, while `after_handshake` can close established connections with specific error codes.
- Hooks execute sequentially in registration order and short-circuit on the first `Reject` outcome, skipping subsequent hooks.
- Always avoid strong references to the `Endpoint` within hook implementations to prevent reference-count cycles and memory leaks.
- Hooks are attached via `Builder::hooks()` and invoked in [`iroh/src/endpoint/connection.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/connection.rs) during 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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/hooks.rs), and hook registration is handled in [`iroh/src/endpoint/bind.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/bind.rs) through the `Builder::hooks()` method.