How Iroh Handles Authentication: Cryptographic Peer Identity and Layered Security

Iroh authenticates every peer-to-peer connection using cryptographic key pairs where the public key serves as the endpoint identity, layered with optional relay tokens and application-level hooks for custom authorization.

Iroh, the open-source peer-to-peer networking library from n0-computer/iroh, implements a multi-layered authentication model that secures connections from the transport level up to the application layer. Understanding how iroh handles authentication is essential for building secure distributed systems that verify peer identity without centralized certificate authorities.

Transport-Level Authentication with Cryptographic Keys

Endpoint Identity and Public Key Verification

In iroh-base/src/key.rs, iroh defines EndpointId as a type alias for PublicKey, making the public key the canonical identifier for every node. When you generate a new endpoint, SecretKey::generate() creates a curve25519/ed25519 key pair on the elliptic curve.

The secret key remains private to the endpoint, while the public key becomes its addressable identity on the network. This design ensures that owning the private key cryptographically proves ownership of the endpoint identity. According to the source code in iroh/src/lib.rs, each endpoint uses its "unique SecretKey used to authenticate and encrypt the connection."

use iroh::Endpoint;
use iroh_base::SecretKey;

// Generate a fresh secret key (the private part)
let secret = SecretKey::generate();

// The corresponding endpoint identifier is the public key
let endpoint_id = secret.public(); // implements EndpointId

// Build an endpoint that will authenticate using this key pair
let ep = Endpoint::builder(presets::N0)
    .secret_key(secret)            // (pseudo‑API – the builder picks up the secret internally)
    .bind()
    .await?;

TLS-Wrapped QUIC Handshake

Iroh wraps QUIC connections in TLS to authenticate peers during the initial handshake. When an Endpoint connects to a remote peer, the QUIC layer creates a TLS session that uses the local endpoint's SecretKey for signing and the remote peer's PublicKey for verification.

This process happens automatically before any application data flows, ensuring that the peer presenting a specific EndpointId actually possesses the corresponding private key. The handshake verifies the public key against the curve25519/ed25519 curve, guaranteeing cryptographic proof of identity.

Relay Authentication with Bearer Tokens

While transport-level encryption secures the connection, relay servers add an additional access control layer. According to iroh-relay/README.md, relays can require a shared secret token via either an Authorization: Bearer <token> HTTP header or a ?token= query parameter.

Configure relay authentication using RelayConfig::with_auth_token(). The relay transport actor in iroh/src/socket/transports/relay/actor.rs processes this token, attaching it to outgoing connections and validating it on incoming relay requests.

use iroh::{Endpoint, RelayConfig};

// Configure a relay with a token "my-secret-token"
let relay_cfg = RelayConfig::default()
    .with_auth_token("my-secret-token".to_string());

// Build the endpoint and tell it to use that relay
let ep = Endpoint::builder(presets::N0)
    .relay_config(relay_cfg)
    .bind()
    .await?;

Application-Level Authentication Hooks

For scenarios requiring custom authorization logic, iroh provides the EndpointHooks trait. This system allows applications to implement before_connect for pre-connection checks and after_handshake for post-handshake verification.

The example in iroh/examples/auth-hook.rs demonstrates a complete token-exchange protocol. On the outgoing side, OutgoingAuthHook::before_connect triggers a token check that can abort connections before they complete. On the incoming side, IncomingAuthHook::after_handshake validates remote identities against an allowed_remotes set, rejecting unauthorized peers.

use iroh::{Endpoint, endpoint::presets, protocol::Router};

mod auth;

// Outgoing side – attach the hook that will perform a pre‑auth request
let (auth_hook, auth_task) = auth::outgoing(b"my‑service‑token".to_vec());
let endpoint = Endpoint::builder(presets::N0)
    .hooks(auth_hook)                // mount the hook
    .bind()
    .await?;
let _guard = auth_task.spawn(endpoint.clone());

// Accept side – install the corresponding hook and protocol
let (incoming_hook, auth_protocol) = auth::incoming(b"my‑service‑token".to_vec());
let endpoint = Endpoint::builder(presets::N0)
    .hooks(incoming_hook)
    .bind()
    .await?;
let router = Router::builder(endpoint)
    .accept(auth::ALPN, auth_protocol)   // register the auth protocol
    .spawn();

Summary

  • Iroh uses cryptographic key pairs where the public key serves as the EndpointId, ensuring verifiable peer identity at the transport level.
  • The TLS-wrapped QUIC handshake automatically verifies that peers own their claimed public keys before exchanging data.
  • Relay authentication protects relay infrastructure using bearer tokens configured via RelayConfig::with_auth_token().
  • Application-level hooks (EndpointHooks) enable custom authentication protocols through before_connect and after_handshake callbacks.
  • All authentication layers are implemented in the core codebase at paths like iroh-base/src/key.rs and iroh/src/socket/transports/relay/actor.rs.

Frequently Asked Questions

What is an EndpointId in iroh?

An EndpointId is the public-facing identity of an iroh node, defined in iroh-base/src/key.rs as a type alias for PublicKey. It represents the curve25519/ed25519 public key that corresponds to the node's private SecretKey. This identifier serves both as the network address and the cryptographic proof of identity during the TLS handshake.

How does iroh verify peer identity during the handshake?

Iroh verifies identity through a TLS-wrapped QUIC handshake where the connecting endpoint signs handshake data with its SecretKey. The receiving endpoint verifies this signature against the claimed EndpointId (public key). This process, described in iroh/src/lib.rs, ensures that only the holder of the private key can establish connections using a specific public key identity.

Can I add custom authentication to iroh connections?

Yes, iroh supports custom authentication through the EndpointHooks trait. You can implement before_connect to run checks before establishing a connection, or after_handshake to validate peers after the cryptographic handshake completes. The auth-hook.rs example demonstrates implementing a token-exchange protocol using these hooks to authorize peers based on application-specific secrets.

How do I secure a relay server in iroh?

Secure your relay by configuring a shared bearer token using RelayConfig::with_auth_token() when building your endpoint. The relay server checks this token against the HTTP Authorization header or ?token= query parameter, rejecting connections without valid credentials. According to iroh-relay/README.md, this prevents unauthorized clients from using your relay infrastructure.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →