> ## Documentation Index
> Fetch the complete documentation index at: https://internal.softcrum.com/llms.txt
> Use this file to discover all available pages before exploring further.

# ADR-007 — Pusher for Realtime

> Status: Proposed (RECONSTRUCTION — requires validation) · Date: 2026-08-17 (reconstructed) Refs: ../constitution/founding-constitution.md.

Status: **Proposed (RECONSTRUCTION — requires validation)** · Date: 2026-08-17 (reconstructed)
Refs: [`../constitution/founding-constitution.md`](/constitution/founding-constitution)

> ⚠️ Reconstructed from the material we have, not recovered. Tags: **\[derived]** is supported by
> evidence in the repository · **\[inferred]** is deduced · **\[proposed]** is a gap I filled.

## Context

A console showing live queue depth, a member portal updating a balance, an operator watching an
import — all need server-to-client push. The options are managed (Pusher, Ably, Supabase Realtime)
or self-hosted WebSockets, which means owning connection state, scaling and reconnection.
**\[inferred]**

## Decision

**Pusher Channels, with tokens signed by our API.** \[derived]

A client never authenticates directly against Pusher. It asks our API for a signed token scoped to
the channels it may hear, which keeps channel authorization inside our permission model rather than
in a second one. \[inferred]

Realtime is a **consumer of the outbox** (ADR-004), not a parallel publish path. \[derived] Anything
delivered in realtime already happened and is already recorded.

## Consequences

* No connection state, no scaling of long-lived sockets, no reconnection logic.
* Channel authorization uses our permissions, so there is one authorization model.
  − A vendor in the hot path of a user-visible experience, and a per-connection cost.
  − Realtime is behind `RealtimePort` (ADR-003), so replacing Pusher is an adapter — which is exactly
  why the port exists. \[inferred]

## What I could not determine

Whether Supabase Realtime was considered and rejected, given Supabase is already the database. It
would be the obvious alternative and I would expect the original ADR to say why not. **\[proposed]**
