What is ALPN in iroh Connections? Protocol Negotiation Explained
Application-Layer Protocol Negotiation (ALPN) is a TLS extension that allows iroh peers to agree on which application protocol will run over a QUIC connection, using byte-string identifiers like b"/iroh/echo/1" to route traffic to the correct handler.
In the n0-computer/iroh repository, ALPN serves as the mechanism for protocol multiplexing over QUIC, enabling a single endpoint to support multiple logical protocols simultaneously. Unlike traditional port-based multiplexing, ALPN identifies the expected protocol through a byte-vector exchanged during the TLS handshake, ensuring both peers agree on how to interpret the connection.
How ALPN Works in iroh Connections
ALPN in iroh follows the standard TLS extension pattern but operates within the QUIC handshake. When two peers connect, they exchange ALPN identifiers—arbitrary byte strings that declare which protocols they support.
According to the iroh source code in iroh/src/lib.rs (lines 130-133), ALPN represents the third critical piece of information required to open a connection, alongside the endpoint address and node identity. The negotiation process ensures that if both sides do not share a common ALPN identifier, the connection terminates before application data transfers.
Configuring ALPN Identifiers on the Endpoint
Before accepting connections, an iroh endpoint must declare which ALPN protocols it supports. This configuration happens through the Builder::alpns method in iroh/src/endpoint.rs (lines 529-537).
use iroh::{Endpoint, endpoint::presets};
let ep = Endpoint::builder(presets::N0)
.alpns(vec![b"/iroh/custom/1".to_vec()]) // <‑‑ declare the ALPN we accept
.bind()
.await?;
The alpns call registers a list of byte-vector identifiers that the endpoint will accept during incoming handshakes. Each identifier should uniquely represent your application protocol, typically using a reverse-DNS style string like /myapp/protocol/1.
The TLS Handshake and ALPN Extraction
During the connection phase, iroh embeds the ALPN string into the TLS configuration. After the handshake completes, the library extracts the negotiated identifier from the underlying QUIC connection using alpn_from_noq_conn (for incoming connections) or its async variant for outgoing connections.
This extraction logic resides in iroh/src/endpoint/connection.rs (lines 70-78). If the handshake completes without an ALPN present, iroh raises an AuthenticationError::NoAlpn, preventing ambiguous connections from proceeding.
Establishing Connections with a Specific ALPN
When initiating an outgoing connection, the client must specify which ALPN it wants to negotiate. The Endpoint::connect method (and connect_with_opts) accepts the target ALPN as the second parameter.
let remote_addr = /* obtain an EndpointAddr */;
let conn = ep.connect(remote_addr, b"/iroh/custom/1").await?;
The connection succeeds only if the remote endpoint has included b"/iroh/custom/1" in its accepted ALPN list. This bidirectional verification ensures protocol compatibility before any application data flows.
Routing Connections to Protocol Handlers
After successful negotiation, iroh uses the ALPN string to route the connection to the appropriate handler. The Router type in iroh/src/protocol.rs provides the accept method to bind handlers to specific ALPN identifiers.
use iroh::router::Router;
struct MyProtocol;
impl iroh::router::ProtocolHandler for MyProtocol {
// …implementation…
}
let router = Router::builder(ep.clone())
.accept(b"/iroh/custom/1", MyProtocol) // <‑‑ bind handler to ALPN
.spawn();
When a connection arrives, the router inspects the negotiated ALPN and forwards the Connection to the matching protocol implementation. This architecture allows a single iroh endpoint to serve multiple distinct protocols—such as echo, file transfer, or custom application logic—over shared QUIC infrastructure.
Handling ALPN Errors and Mismatches
If the client requests an ALPN that the server does not recognize, iroh rejects the connection with an "unsupported ALPN" error. As implemented in iroh/src/endpoint/connection.rs (lines 643-644), the library logs a warning and returns an error to the caller.
match ep.connect(remote_addr, b"/unknown/proto").await {
Ok(_) => println!("connected"),
Err(e) => eprintln!("connection failed: {}", e), // prints “unsupported ALPN protocol”
}
This strict validation prevents protocol confusion attacks and ensures that endpoints only process connections they explicitly understand.
Summary
- ALPN is a TLS extension that enables protocol negotiation over QUIC connections in iroh.
- Endpoints declare supported protocols via
Builder::alpnsiniroh/src/endpoint.rs. - The ALPN identifier is extracted during handshake via
alpn_from_noq_conniniroh/src/endpoint/connection.rs. - Connections require matching ALPN strings on both sides; mismatches result in
AuthenticationError::NoAlpnor "unsupported ALPN" errors. - The
Routertype routes connections to handlers based on the negotiated ALPN, enabling multiplexed protocol support.
Frequently Asked Questions
What happens if two iroh peers use different ALPN strings?
If the client and server do not share a common ALPN identifier, the TLS handshake will complete successfully, but iroh will reject the connection immediately afterward. As seen in iroh/src/endpoint/connection.rs (lines 643-644), the server logs an "unsupported ALPN" warning and closes the connection, while the client receives an error indicating protocol mismatch.
Can an iroh endpoint accept multiple ALPN protocols simultaneously?
Yes. The Builder::alpns method accepts a vector of byte-vectors, allowing a single endpoint to register multiple protocol identifiers. The Router can then dispatch incoming connections to different handlers based on which ALPN was negotiated for each specific connection.
How is ALPN different from QUIC stream IDs in iroh?
ALPN identifies the application protocol for the entire connection (e.g., "this is an echo protocol connection"), while QUIC stream IDs identify individual data streams within that connection. A single ALPN-negotiated connection can host multiple bidirectional streams, but all streams belong to the same protocol type defined by the ALPN.
Is ALPN encrypted during the iroh handshake?
Yes. Because ALPN is a TLS extension, the protocol identifiers are exchanged during the TLS handshake and benefit from the same encryption and authentication as the rest of the handshake traffic. This prevents middleboxes from tampering with protocol selection.
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 →