Files
pubsub/docs/architecture/event-targets/iroh-hub.md
glm-5.1 e60f0a1aa0 Mark Iroh adapters as deferred pending fork of iroh-ts
Update peer dep from @rayhanadev/iroh to @alkdev/iroh. Document
platform strategy: Linux native + WASM fallback, Windows/macOS
deferred. Fix outdated single-import reference in iroh-transport.md
to split spoke/hub imports. Resolve binding stability and NAPI/Deno
R&D items as covered by the fork path.
2026-05-08 03:59:35 +00:00

4.3 KiB

status, last_updated
status last_updated
draft 2026-05-08

Iroh Hub Event Target

Import: @alkdev/pubsub/event-target-iroh-hub Peer dep: @alkdev/iroh (optional, NAPI-RS native addon — pending fork of iroh-ts) Status: Deferred. Pending fork of iroh-ts with Linux + WASM platform targets.

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

Unlike the WebSocket server adapter (where the caller passes connections via addConnection), the Iroh hub adapter manages connections automatically via endpoint.accept(). This is a deliberate design difference: Iroh QUIC connections are accepted by the endpoint, not passed in by a framework. The hub has no addConnection/removeConnection API — connections are internal to the adapter.

  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

As a fan-out adapter, the Iroh hub must implement topic-based fan-outdispatchEvent sends only to spokes subscribed to that topic, not to all connected spokes. This requires a subscriptions map (Map<string, Set<Spoke>>) updated by __subscribe/__unsubscribe control events from spokes. See ADR-003 and WebSocket server adapter for the established pattern.

dispatchEvent(event) {
  // event.type is the full topic string, e.g. "message.sent:conv-123"
  // This matches the topics that spokes subscribe to via __subscribe
  const message = encodeEnvelope(event.detail);

  // Send only to spokes subscribed to this topic
  const subscribers = this.subscriptions.get(event.type);
  if (subscribers) {
    for (const spoke of subscribers) {
      spoke.sendStream.writeAll(message);
    }
  }
  return true;
}

Key Properties

  • Multi-connection — manages a set of connected spokes
  • Topic-based fan-out — dispatchEvent sends only to spokes subscribed to the event type
  • Subscription tracking — maintains topic-to-spoke mapping, updated by __subscribe/__unsubscribe control events
  • Accepts incoming — endpoint.accept() loop runs continuously
  • Cryptographic identity — each spoke verified by Ed25519 NodeId

R&D Needed

  1. Fork of iroh-ts — same as spoke adapter. Pending fork as @alkdev/iroh.
  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. iroh-gossip — for true broadcast to many spokes, iroh-gossip would be more efficient than per-spoke streams. Not yet available in TS. The current subscription-tracked fan-out design works for moderate fan-out; gossip would be an optimization for very large fan-out later.
  5. Connection rejection — how to reject connections from unknown NodeIds.

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