Files
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

87 lines
4.3 KiB
Markdown

---
status: draft
last_updated: 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`
```ts
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' `SendStream`s
- `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-out**`dispatchEvent` 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](../decisions/003-subscription-control-protocol.md) and [WebSocket server adapter](websocket-server.md) for the established pattern.
```ts
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 `NodeId`s.
See [../iroh-transport.md](../iroh-transport.md) for full protocol details, identity, and comparison with WebSocket.