Files
pubsub/docs/architecture/event-targets/iroh-hub.md
glm-5.1 371dabc20d Add per-adapter architecture docs in event-targets/ directory
- Create docs/architecture/event-targets/ with individual specs:
  in-process, redis, websocket-client, websocket-server,
  worker, iroh-spoke, iroh-hub
- Update event-targets.md to serve as index with topology model
  (symmetric vs fan-out) and adapter status table
- Update architecture.md index to reference new directory
2026-05-07 14:49:50 +00:00

2.9 KiB

status, last_updated
status last_updated
draft 2026-05-07

Iroh Hub Event Target

Import: @alkdev/pubsub/event-target-iroh-hub Peer dep: @rayhanadev/iroh (optional, NAPI-RS native addon) Status: Not yet implemented. Needs R&D on binding stability, NAPI under Deno.

P2P QUIC event target for the hub (server) side. The hub accepts incoming connections and bidirectional streams. Manages multiple connected spokes.

createIrohHubEventTarget

async function createIrohHubEventTarget<TEvent extends TypedEvent>(
  args: CreateIrohHubEventTargetArgs,
): Promise<TypedEventTarget<TEvent>>;

CreateIrohHubEventTargetArgs

Field Type Required Description
endpoint Endpoint Yes iroh endpoint (created with Endpoint.create())
alpn string No Application-layer protocol. Default: "alkpubsub/1"

How It Works

Similar to the WebSocket server adapter, the Iroh hub adapter manages multiple connections:

  • dispatchEvent → writes JSON envelope to all connected spokes' SendStreams
  • addEventListener → registers local listeners for events from any spoke
  • On incoming connection → endpoint.accept()connection.acceptBi() → new spoke tracked

Each spoke gets its own read loop that parses length-prefixed JSON messages from RecvStream and dispatches locally.

Connection Lifecycle

  1. Hub creates Endpoint and starts accepting
  2. Spoke connects → hub gets Connection from endpoint.accept()
  3. Hub accepts stream → connection.acceptBi()SendStream + RecvStream
  4. Hub creates per-spoke read loop
  5. On disconnect → RecvStream.readExact() throws → remove spoke from set
  6. Hub continues accepting new connections

Fan-Out

dispatchEvent(event) {
  const message = encodeEnvelope(event.detail);
  for (const spoke of this.spokes) {
    spoke.sendStream.writeAll(message);
  }
  return true;
}

Key Properties

  • Multi-connection — manages a set of connected spokes
  • Fan-out — dispatchEvent sends to all connected spokes
  • Accepts incoming — endpoint.accept() loop runs continuously
  • Cryptographic identity — each spoke verified by Ed25519 NodeId

R&D Needed

  1. Binding stability — same as spoke adapter. @rayhanadev/iroh needs testing.
  2. Concurrent accept — can endpoint.accept() handle multiple simultaneous connections?
  3. Stream vs. Connection per spoke — current design: one bidirectional stream per spoke on a single connection. Alternative: one connection per spoke. Need to benchmark which is better for the expected workload.
  4. 1:N fan-out — for hub to N spokes, each spoke gets its own stream. For true broadcast, iroh-gossip would be better (not yet available in TS).
  5. Connection rejection — how to reject connections from unknown NodeIds.

See ../iroh-transport.md for full protocol details, identity, and comparison with WebSocket.