Skip to content
This repository was archived by the owner on Apr 22, 2026. It is now read-only.

Latest commit

 

History

History
90 lines (70 loc) · 4.4 KB

File metadata and controls

90 lines (70 loc) · 4.4 KB

@repo/service

The Erebus edge gateway — a Cloudflare Workers service that handles real-time WebSocket connections, pub/sub message routing, and channel management using Durable Objects.

Tech Stack

  • Cloudflare Workers — Edge runtime
  • Durable Objects (SQLite-backed) — Stateful channel coordination
  • Hono — HTTP routing framework
  • Upstash Redis — Rate limiting and caching
  • KV Namespaces — Edge caching layer

Architecture

src/
  index.ts          → Worker entry point and Hono app
  env.ts            → Environment variable interface (Env)
  objects/
    pubsub/         → Core pub/sub Durable Object implementation
      channel.ts    → ChannelV1 Durable Object (orchestrator)
      ErebusClient.ts         → WebSocket wrapper with grant context
      ErebusPubSubService.ts  → Abstract DO base (WebSocket lifecycle)
      MessageHandler.ts       → Packet parsing and routing
      MessageBroadcaster.ts   → Fan-out with pre-serialization
      MessageBuffer.ts        → Persistence with alarm-based TTL cleanup
      SubscriptionManager.ts  → In-memory cached subscriptions
      SequenceManager.ts      → ULID generation with caching
      ShardManager.ts         → Cross-region coordination
      service-utils.ts        → Storage, logging, queue utilities
      ack-utils.ts            → ACK packet factory functions
  analytics/        → Usage tracking
tests/
  pubsub.test.ts    → Integration tests (Cloudflare Workers pool)
  env.d.ts          → Test environment types

Architecture

The service uses composition over inheritance. Manager classes receive a ServiceContext and use standalone utility functions from service-utils.ts instead of extending a base class. This eliminates the previous 3-level class hierarchy and ~600 lines of duplicated code.

In-memory caching: SubscriptionManager, SequenceManager, and ShardManager cache hot data in memory with lazy hydration from storage after hibernation wakes. This eliminates redundant storage reads on every subscribe/publish operation.

DO alarm-based cleanup: MessageBuffer schedules Durable Object alarms for TTL cleanup instead of pruning on every write or during reads.

Key concepts

  • ChannelV1: A Durable Object class with SQLite storage that manages a single pub/sub channel. Handles WebSocket connections, message ordering, history, and presence.
  • Grant JWTs: Clients authenticate via short-lived JWTs signed by the web app and verified by the service using the shared public key.
  • Region-local ULIDs: Monotonic, sortable message IDs generated per-region without a central bottleneck.
  • Single-threaded actor model: No unnecessary transactions — the DO's single-threaded nature provides mutual exclusion. Transactions only used for multi-key atomic writes.

Development

# From repo root
bun run dev  # Starts all services

# From this directory
bun run dev  # Generates CF types, then starts wrangler dev on port 8787

Environment Variables

See .env.example for the full list. For local development, Cloudflare Workers uses .dev.vars for secrets:

# Copy .env.example to .dev.vars for local development
cp .env.example .dev.vars
Variable Required Description
PUBLIC_KEY_JWK Yes EC P-256 JWK for verifying grant tokens
ROOT_API_KEY Yes Admin API key
UPSTASH_REDIS_REST_URL Yes Upstash Redis REST endpoint
UPSTASH_REDIS_REST_TOKEN Yes Upstash Redis REST token
WEBHOOK_BASE_URL Yes Base URL for webhook delivery
WEBHOOK_SECRET Yes HMAC secret for webhooks

Scripts

Script Description
dev Generate types + start wrangler dev (port 8787)
build Build SDK dependency then wrangler build
test Run Vitest with Cloudflare Workers pool
deploy Deploy to Cloudflare production
preview Deploy to Cloudflare preview environment
cf-typegen Generate TypeScript types from wrangler.jsonc