Jobs, Video & Files
Three planes self-hosted + Cloudflare: durable PostgreSQL-native jobs (pg-boss), video ingest/egress (Cloudflare Stream + LiveKit), and private object storage (R2 + Cloudflare Images/CDN). PostgreSQL owns authorization and lifecycle; Cloudflare owns media bytes.
Video Ingest/Egress Paths
Three distinct media paths cover VOD course video, live broadcast, and interactive rooms. Each has different latency, reliability, and cost profiles.
pg-boss Jobs Architecture
All async work flows through a transactional boundary that prevents silent failures and ensures every domain event is processed exactly once. The domain mutation, event emission, and job creation are atomic.
domain mutation → outbox_events row
↓
dispatcher (polls)
↓
pg-boss jobs table (pg-boss)
↓
worker: validate → effect → receipt → ack
Transactional Boundary
- Domain transaction: updates domain rows + inserts immutable outbox event
- Dispatcher: claims that event in a separate process
- Second PostgreSQL transaction: inserts pg-boss jobs + marks delivery rows dispatched
- Worker: validates payload (Zod), performs idempotent effect, records receipt, acknowledges
pg-boss vs Graphile Worker
| Feature | pg-boss | Graphile Worker |
|---|---|---|
| Queue policies | singleton, debounce, throttle, strict-FIFO | job keys (debounce, replacement) |
| Cron support | Yes | Yes |
| Bun compatibility | Spike required (Node 22.12+ documented) | Spike required |
| Transaction adapter | ORM adapters (Kysely, Prisma) | graphile_worker.add_job() SQL function |
| Completed job retention | Configurable; records kept | Deleted on completion (key not permanent receipt) |
| Dashboard | Yes — dashboard package | No |
pg-boss after Bun compatibility spike (S8). Graphile Worker is the fallback if the Bun spike fails. The completed-job retention and dashboard package tip the balance toward pg-boss when Bun is confirmed compatible.
Files Plane
| Component | Technology | Role |
|---|---|---|
| Object storage | Cloudflare R2 (private) | Canonical file bytes; bytes never hit app server |
| Transformation | Cloudflare Images | Variants, resize, format conversion (WebP/AVIF) |
| Delivery | Cloudflare CDN/Workers | Edge serving, signed URL access control |
| Control plane | TypeScript service (in-repo) | Metadata, lifecycle, authorization decisions |
| Processing workers | self-hosted workers | Virus scan, ffmpeg, thumbnail generation |
| Authorization | PostgreSQL | Every access decision; lifecycle state machine |
File State Machine
Every uploaded file progresses through an explicit lifecycle. State transitions are driven by worker events and tracked in PostgreSQL.
Direct upload flow: Client requests a presigned R2 URL from the control plane. Client uploads directly to R2 — bytes never hit the app server. Control plane receives the R2 webhook, starts a processing pg-boss job, and transitions file state. Workers scan, transcode, generate thumbnails, then mark the file ready.
Cloudflare Stack Decision
Production runs self-hosted. The following Cloudflare services are first-class production components under the enterprise contract: Stream, R2, Images, Workers, CDN.
All other third-party SaaS is prototype-only. The sole exception is the tax calculation API adapter, which enters the billing service through a verified event interface.