Files
pubsub/docs/architecture
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
..

status, last_updated
status last_updated
draft 2026-05-01

@alkdev/pubsub Architecture

Type-safe publish/subscribe with pluggable event target adapters. The core (createPubSub + TypedEventTarget + EventEnvelope + operators) has no transport dependency. Each adapter (Redis, WebSocket, Worker, Iroh) is an isolated module that only imports its own peer dependency.

This package is a transport layer only. It carries events between processes and does not prescribe what those events mean or how downstream systems coordinate. Higher-level protocols (call/response, operation invocation, workflow coordination) belong in downstream packages like @alkdev/operations.

Core Principle

The TypedEventTarget interface is the contract. All transports implement the same addEventListener / dispatchEvent / removeEventListener surface. createPubSub doesn't know or care which transport is in use — it just dispatches events to whatever TypedEventTarget it was given.

The EventEnvelope is the cross-platform format. Every event dispatched through pubsub is { type, id, payload }. This is a minimal, JSON-serializable envelope that any transport adapter can route and any downstream consumer can interpret. Domain-specific data lives in payload. Correlation lives in id. The event type lives in type. No parent field — causal relationships are managed by downstream coordination layers, not the transport.

Swapping transports is a one-line config change:

const pubsub = createPubSub<MyEventMap>({
  eventTarget: createRedisEventTarget({ publishClient, subscribeClient }),
});

Why This Exists

Extracted from @alkdev/alkhub_ts/packages/core/pubsub/, which itself was adapted from @graphql-yoga/subscription and @graphql-yoga/typed-event-target. The pubsub module was already self-contained within alkhub — zero cross-module imports from operations, config, logger, or MCP. Extracting it into a standalone package:

  1. Reduces coupling — alkhub depends on pubsub, not the other way around
  2. Enables reuse — multiple alkhub packages can share the same pubsub instance
  3. Isolates peer deps — Redis and Iroh are heavy native dependencies; consumers that don't need them shouldn't carry them
  4. Matches established pattern@alkdev/taskgraph and @alkdev/typemap already use the standalone-package pattern

What This Package Provides

  • CorecreatePubSub, TypedEventTarget, TypedEvent, EventEnvelope, stream operators (filter, map, pipe, take, reduce, toArray, batch, dedupe, window, flat, groupBy, chain, join), Repeater (inlined from @repeaterjs/repeater)
  • Adapters (each is a peer-dep island, importable via sub-path export):
    • In-process (default EventTarget, no adapter needed)
    • Redis (@alkdev/pubsub/event-target-redis, peer dep: ioredis)
    • WebSocket (future: @alkdev/pubsub/event-target-websocket)
    • Worker (future: @alkdev/pubsub/event-target-worker)
    • Iroh (future: @alkdev/pubsub/event-target-iroh, peer dep: @rayhanadev/iroh)

What This Package Does NOT Provide

  • Call protocol — request/response coordination, PendingRequestMap, CallEventSchema, and CallError have been moved to @alkdev/operations. The pubsub transport is substrate-agnostic.
  • Workflow coordination — causal chains, parent/child relationships, and abort cascading are domain-level concerns managed by downstream packages.
  • Abort/cancellation primitives — these belong in the coordination layer, not the transport. The EventEnvelope intentionally omits a parent field to avoid conflating transport with coordination semantics.

Consumer Context

alkhub (hub-spoke coordinator)

The hub uses pubsub for event routing between operations, runners, and the SSE interface. Transport choice depends on deployment:

Deployment Transport
Single-process hub In-process (default)
Hub + worker processes Redis
Hub + remote spokes WebSocket or Iroh

Downstream packages

  • @alkdev/operations uses createPubSub with its own event maps for call/response coordination. It defines its own event schemas and PendingRequestMap on top of the pubsub transport.
  • @alkdev/taskgraph will use pubsub events for task lifecycle notifications and workflow coordination.

Threat Model

  • Fork provenance — core pubsub and typed event target are adapted from graphql-yoga (MIT). All original copyright notices are preserved in file headers. See ADR-001.
  • Peer dep isolation — Redis and Iroh are optional peer dependencies. A consumer that only needs in-process transport installs zero extra packages. A consumer using Redis but not Iroh installs ioredis only.
  • Type-only importsevent-target-redis.ts imports ioredis types only at compile time. At runtime, the consumer must provide the actual Redis/Cluster instances.
  • Minimal envelope — the EventEnvelope format ({ type, id, payload }) is intentionally minimal and JSON-serializable. Any platform that supports JSON can produce or consume these events (Rust, Python, etc.).

Architecture Documents

Document Content
api-surface.md createPubSub factory, EventEnvelope, PubSub types, operators, TypedEventTarget types
event-targets.md In-process, Redis, WebSocket, Worker adapters — interface, configuration, limitations
iroh-transport.md Iroh P2P QUIC transport — protocol, framing, identity, hub/spoke sides, reconnection
build-distribution.md Dependencies, project structure, tree-shaking, sub-path exports, targets

Document Lifecycle

Architecture documents use YAML frontmatter with status and last_updated fields:

---
status: draft | stable | deprecated
last_updated: YYYY-MM-DD
---
Status Meaning Transitions
draft Under active development. Content may change. stable when implementation is complete and tests verify API contract.
stable API contracts are locked. Changes require review cycle. deprecated when superseded.
deprecated Superseded. Kept for reference. Removed when no longer referenced.

References

  • Source: @alkdev/alkhub_ts/packages/core/pubsub/
  • Upstream: @graphql-yoga/subscription and @graphql-yoga/typed-event-target (MIT)
  • alkhub pubsub-redis doc: @alkdev/alkhub_ts/docs/architecture/pubsub-redis.md
  • alkhub spoke-runner doc: @alkdev/alkhub_ts/docs/architecture/spoke-runner.md
  • Migration research: docs/research/migration.md
  • Research: Event sourcing types — docs/research/event_sourcing/ (not in this repo, in global workspace)