docs(architecture): clarify iroh ALPN integration — use Endpoint directly, not Router

iroh's Endpoint natively supports ALPN negotiation and set_alpns(). Our
HandlerRegistry dispatches exactly like iroh's own ProtocolMap/Router
pattern, but shared across both quinn and iroh connection sources. We
use iroh::Endpoint directly (not iroh::Router) because our HandlerRegistry
and AuthContext are shared across sources.
This commit is contained in:
2026-06-16 12:44:19 +00:00
parent 5c8448ff86
commit e3d1a504da
2 changed files with 22 additions and 7 deletions

View File

@@ -93,14 +93,20 @@ loop {
### iroh accept loop (P2P relay-assisted)
iroh's `Endpoint` natively supports ALPN negotiation (step 4 of its connection establishment). The `iroh::Endpoint::set_alpns()` method configures which ALPNs the endpoint advertises — the same mechanism iroh's own `Router` uses internally with its `ProtocolMap`.
We use `iroh::Endpoint` directly (not iroh's `Router`) because our `HandlerRegistry` is shared between quinn and iroh connection sources, and our `AuthContext` construction differs per source. Our accept loop replaces iroh's `Router` accept loop with our own dispatch:
```
loop {
tokio::select! {
incoming = iroh_endpoint.accept() => {
let connection = incoming.await; // iroh QUIC connection + ALPN
match connection {
Ok(conn) => dispatch(conn),
Err(e) => { /* log connection error, continue */ }
// incoming is an iroh::endpoint::Incoming
let accepting = incoming.accept(); // Accepting state
let alpn = accepting.alpn().await; // ALPN from TLS handshake
match alpn {
Ok(alpn) => dispatch(alpn, accepting),
Err(e) => { /* log handshake failure, continue */ }
}
}
_ = shutdown.changed() => break,
@@ -108,6 +114,8 @@ loop {
}
```
See iroh's `protocol.rs` (`/workspace/iroh/iroh/src/protocol.rs`) for the reference implementation of this pattern — `handle_connection()` reads the ALPN, looks up the handler in `ProtocolMap`, and calls `handler.accept(connection)`. Our dispatch is the same pattern with our `HandlerRegistry`.
### Dispatch function (shared)
```

View File

@@ -34,11 +34,18 @@ The reference implementation's "stealth mode" is **SSH-over-TLS on port 443**. T
In the new ALPN model, this concept maps to: the endpoint speaks QUIC+TLS with ALPN, and the `alknet/http` handler can serve a decoy website on `h2`/`http/1.1` while real services use `alknet/ssh`, `alknet/call`, etc. The ALPN router does the "stealth" job — unknown ALPNs get the HTTP handler, which can serve whatever fronting content is desired. No byte-peeking needed.
### iroh produces QUIC connections
### iroh produces QUIC connections with ALPN
iroh's `Endpoint::accept()` produces incoming QUIC connections. These connections have ALPNs. They can feed directly into the same `HandlerRegistry` dispatch. The iroh endpoint and the quinn endpoint both produce QUIC connections — the difference is how they're established (relay-assisted vs direct), not how handlers consume them.
iroh's `Endpoint::accept()` produces incoming QUIC connections with ALPN negotiation (step 4 of iroh's connection establishment). The `iroh::Endpoint` supports `set_alpns()` to configure which ALPNs the endpoint advertises — the same mechanism iroh's own `Router` uses internally.
This means: **the ALPN router is transport-agnostic**. It dispatches by ALPN string. It doesn't care whether the connection came from a quinn endpoint or an iroh endpoint. Both produce connections that handlers can use via the same `Connection` type.
This means the iroh integration is not a separate dispatch path. It uses the **same ALPN dispatch** as the quinn path. The `iroh::Endpoint` accepts connections, negotiates ALPN, and our `HandlerRegistry` dispatches to the right handler — exactly like iroh's own `Router` does with its `ProtocolMap`.
We do NOT wrap iroh's `Router`. We use `iroh::Endpoint` directly and run our own accept loop, because:
- Our `HandlerRegistry` is shared between quinn and iroh connection sources
- Our `AuthContext` construction differs per connection source
- Our shutdown and error handling patterns are our own
The relationship is: **iroh's Router is a reference implementation of the pattern we're building.** Our `AlknetEndpoint` generalizes it to support multiple connection sources with the same dispatch.
### Key design questions