Understanding ALPN in Iroh: Purpose and Configuration Guide
ALPN (Application-Layer Protocol Negotiation) in iroh serves as a TLS-authenticated mechanism for routing incoming connections to protocol handlers and identifying protocols post-handshake, configured via EndpointBuilder::alpns() for servers and the connect() method for clients.
Application-Layer Protocol Negotiation is a critical TLS extension in the iroh networking stack that enables secure protocol multiplexing over QUIC connections. Because ALPN values are exchanged during the TLS handshake and cryptographically authenticated, they provide a tamper-proof method for determining which application protocol a connection should use. This article explains how iroh leverages ALPN for connection routing and demonstrates how to configure custom ALPN identifiers in your own applications.
What is ALPN and Why Does Iroh Use It?
In iroh/src/protocol.rs, the ALPN value performs two essential functions that enable the router to manage multiple protocols on a single endpoint.
Routing Incoming Connections
The primary purpose of ALPN in iroh is to route incoming connections to the appropriate protocol handler. As implemented in iroh/src/protocol.rs at line 219, the router accepts connections only for ALPN identifiers that it has been explicitly configured to handle. When a peer connects, the endpoint inspects the remote's ALPN and dispatches the connection to the corresponding handler, allowing a single endpoint to multiplex different protocols securely.
Protocol Identification After Handshake
After the TLS handshake completes, the negotiated ALPN is exposed to the application layer to verify that the connection uses the expected protocol. For example, the echo protocol in iroh declares ECHO_ALPN = b"/iroh/echo/1" (see iroh/src/protocol.rs:696), which both the client and server use to confirm they are speaking the same version of the protocol. Because ALPN is part of the TLS handshake, it is guaranteed authenticated and cannot be spoofed by a man-in-the-middle.
How to Set Custom ALPNs in Iroh
Iroh allows you to specify ALPN identifiers on both the accept side (server) and the connect side (client). The configuration differs depending on whether you are accepting incoming connections or initiating outgoing ones.
Configuring ALPNs on the Server (Accept Side)
To accept connections for a specific protocol, use the EndpointBuilder::alpns method in iroh/src/endpoint.rs (lines 529-536). This method sets the list of ALPN identifiers the endpoint will accept, overwriting any previous configuration. At least one ALPN must be configured before the endpoint can accept connections.
use iroh::endpoint::Endpoint;
let ep = Endpoint::builder()
.alpns(vec![b"my/custom/alpn".to_vec()]) // accepted ALPNs
.build()
.await?;
Once the endpoint is built, you can register a protocol handler using the router:
ep.router()
.accept(b"my/custom/alpn", MyProtocolHandler)
.spawn();
Specifying ALPNs on the Client (Connect Side)
When connecting to a remote endpoint, pass the desired ALPN to the connect method. The ALPN supplied here will be advertised during the TLS handshake and must match one of the server's accepted ALPNs.
let conn = endpoint.connect(server_addr, b"my/custom/alpn").await?;
If you need the client to advertise additional ALPN identifiers while still using a primary one for the connection, configure them via the builder as well:
let ep = Endpoint::builder()
.alpns(vec![
b"my/custom/alpn".to_vec(),
b"legacy/alpn".to_vec(),
])
.build()
.await?;
The negotiated ALPN can be inspected after the connection is established by examining the connection object in iroh/src/endpoint/connection.rs, where the handshake extracts the ALPN and propagates it to the application.
Complete Code Examples
The following examples demonstrate a complete server and client configuration using custom ALPNs.
Server Implementation
This server accepts only connections using the custom ALPN b"my/custom/alpn":
use iroh::endpoint::{Endpoint, Builder};
use iroh::protocol::Router;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Build an endpoint that only accepts connections using our custom ALPN.
let ep = Builder::new()
.alpns(vec![b"my/custom/alpn".to_vec()])
.build()
.await?;
// Register a protocol handler for the ALPN.
ep.router()
.accept(b"my/custom/alpn", MyProtocolHandler)
.spawn()
.await?;
println!("Server listening...");
// The endpoint now listens for incoming connections...
Ok(())
}
Client Implementation
This client connects to the server using the matching ALPN:
use iroh::endpoint::Endpoint;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let client = Endpoint::builder().build().await?;
let server_addr = // ... obtain the server's address
// Connect, advertising the same ALPN the server expects.
let conn = client.connect(server_addr, b"my/custom/alpn").await?;
println!("Connected with ALPN: {:?}", conn.alpn());
// Use the connection...
Ok(())
}
Summary
- ALPN authenticates protocols: In iroh, ALPN values are exchanged during the TLS handshake and cannot be spoofed, providing secure protocol negotiation.
- Server configuration: Use
EndpointBuilder::alpns()iniroh/src/endpoint.rsto define which protocols your endpoint accepts. - Client configuration: Pass the ALPN byte string to
endpoint.connect()to advertise your protocol during the handshake. - Router integration: The router in
iroh/src/protocol.rsuses ALPN values to dispatch incoming connections to the correct protocol handlers. - Multiple protocols: An endpoint can accept multiple ALPNs by passing a vector to the builder, enabling multiplexed protocol support on a single endpoint.
Frequently Asked Questions
What happens if the client and server ALPNs don't match?
If the client advertises an ALPN that the server does not have configured in its alpns list, the TLS handshake will fail and the connection will be rejected. According to the implementation in iroh/src/endpoint.rs, the endpoint only accepts connections for ALPN identifiers it has been explicitly configured to handle, ensuring that mismatched protocols cannot connect.
Can an iroh endpoint accept multiple ALPNs?
Yes. You can pass a vector of multiple ALPN byte strings to EndpointBuilder::alpns(), allowing a single endpoint to handle several different protocols simultaneously. The router will then dispatch incoming connections to the appropriate handler based on the negotiated ALPN, as shown in iroh/src/protocol.rs where the router accepts connections for arbitrary ALPN protocols.
Is ALPN encryption secure in iroh?
ALPN values in iroh are secure because they are transmitted as part of the TLS handshake and are therefore cryptographically authenticated. This means a man-in-the-middle cannot spoof or alter the ALPN without breaking the TLS connection, making it a reliable mechanism for protocol selection in iroh's QUIC-based transport.
How do I inspect the negotiated ALPN after a connection is established?
After a connection is established, you can inspect the negotiated ALPN through the connection object. The implementation in iroh/src/endpoint/connection.rs extracts the ALPN from the TLS handshake and exposes it to the application, allowing you to verify which protocol was actually negotiated between the peers.
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 →