Design real-time client delivery for a multi-tenant SaaS — WebSocket / SSE / database-change subscriptions / presence: the channel/topic model, authorize-at-subscribe-time (the per-tenant AND per-user leak boundary, re-checked as authority changes — never trusting a client-named channel), fan-out, scaling stateful connections across nodes, backpressure and slow-consumer handling, reconnect with replay/resume, and presence/typing state. Produces the channel taxonomy, the subscribe-time authorization contract, and a connection-scaling plan. Use when adding live updates, presence, or a subscription feed to clients, or when a live channel leaks another tenant's data. Do NOT use for the internal server-to-server event backbone (streaming-event-architect), the request/response API or outbound webhooks (api-event-architect), the offline write-sync engine (offline-first-sync-architect), or notification UX (notification-webhook-ux-designer).
Installation
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Design real-time client delivery for a multi-tenant SaaS — WebSocket / SSE / database-change subscriptions / presence: the channel/topic model, authorize-at-subscribe-time (the per-tenant AND per-user leak boundary, re-checked as authority changes — never trusting a client-named channel), fan-out, scaling stateful connections across nodes, backpressure and slow-consumer handling, reconnect with replay/resume, and presence/typing state. Produces the channel taxonomy, the subscribe-time authorization contract, and a connection-scaling plan. Use when adding live updates, presence, or a subscription feed to clients, or when a live channel leaks another tenant's data. Do NOT use for the internal server-to-server event backbone (streaming-event-architect), the request/response API or outbound webhooks (api-event-architect), the offline write-sync engine (offline-first-sync-architect), or notification UX (notification-webhook-ux-designer).
Realtime Subscription Architect
Purpose
Real-time features fail in two directions: they leak — a client subscribes
to a channel it names and receives another tenant's data — and they fall
over — stateful connections pile up on one node, a slow consumer backs up
the whole fan-out, and a reconnect storm after a blip takes the service
down. This skill designs live client delivery so neither happens: a channel
model whose authorization is checked at subscribe time (and re-checked as
authority changes), fan-out that scales across nodes, backpressure that
sheds or slows without collapsing, and reconnect with a defined replay/resume
window. The deliverable is a channel taxonomy, a subscribe-time
authorization contract naming the per-tenant AND per-user boundary, and a
connection-scaling plan. This skill owns the push to CLIENTS; the moment the
question is server-to-server event flow it belongs to
streaming-event-architect.
Use When
Use when: adding live updates, a subscription feed, collaborative
presence, or typing/cursor state delivered to clients over WebSocket, SSE,
or a database-change subscription.
Use when: a live channel leaks data — a client receives events for a
tenant, user, or resource it should not see — i.e. authorization is done
at connect only, or not at all, or trusts a client-named channel.
Use when: stateful connections do not scale — they pin to one node,
reconnect storms overwhelm, or a slow client backs up delivery to others.
Use when: designing reconnect semantics — what a client missed while
disconnected and how much history it may replay on resume.
Do NOT use when: the subject is the INTERNAL server-to-server event
backbone — topics, partitions, consumer groups, DLQ, CDC — that is
streaming-event-architect; this skill may CONSUME that backbone to push
to clients, but it does not design it.
Do NOT use when: the subject is request/response API design or OUTBOUND
webhooks to partner servers — that is api-event-architect; webhooks are
server-to-server push, not live client subscriptions.
Do NOT use when: the subject is the client's OFFLINE write queue, optimistic
apply, and conflict resolution — that is offline-first-sync-architect;
this skill is live push while the client is ONLINE (the two compose: the
realtime channel is a common online transport for the sync engine).
Do NOT use when: the subject is the UX of notifications/toasts/in-app
inbox — that is notification-webhook-ux-designer; this owns transport,
not presentation.
Inputs to Inspect
What must go live and to whom: the events, their audience (one user, a
tenant, a shared resource's participants), volume, and per-client rate.
Current transport and authorization: WebSocket/SSE/DB-change subscription
in use, and where (if anywhere) a subscription is authorized — connect
only, per-subscribe, or per-message.
The tenant + authorization model (multi-tenant-data-architect /
authorization-matrix-designer output): what defines whether an actor
may see a given channel's stream, and whether authority can change
mid-connection (role revoked, removed from a resource).
The connection topology: how many concurrent connections, on how many
nodes, behind what load balancer, and whether any per-connection state
lives in node memory today.
The event source feeding delivery: the internal backbone / DB change feed
(from streaming-event-architect or the database) the channels fan out.
Reconnect reality: current reconnect behavior, whether clients miss
events on a blip, and any incident history of reconnect storms.
Workflow
Model channels around authority, not convenience. Define the channel
namespace so the thing that is authorized IS the channel key — e.g.
tenant:<id>:resource:<id>. The server derives the tenant/resource from
trusted state; a client MUST NOT be able to subscribe to an arbitrary
channel string it constructs. Enumerate channel types and their audience.
Authorize at subscribe time — and re-check as authority changes. The
security property: a subscribe request is authorized server-side against
the same policy the write path uses, at subscribe time. Connect-time-only
authorization is insufficient because authority changes mid-connection
(a user removed from a workspace must stop receiving its events). Define
the re-authorization trigger (on authority-change event, on token
refresh, or periodic) and the tear-down of now-forbidden subscriptions.
State the leak boundary explicitly. Per-tenant isolation is necessary
but NOT sufficient: within a tenant, a user may see only channels they
are entitled to. Write the boundary as "tenant AND per-user/per-resource"
and make every fan-out filter honor it — never broadcast a tenant-wide
stream and filter on the client.
Design fan-out. How an event reaches every entitled connection across
nodes: a shared pub/sub or presence backbone that any node can publish to
and every node subscribes to for its local connections. State the source
(consume the internal backbone; do not re-invent it) and the delivery
filter that applies the leak boundary at the edge.
Scale stateful connections. Connections are long-lived and pin
resources: state the per-connection memory cost, the node fan-out limit,
how the load balancer handles sticky vs any-node connections, and how a
node draining (deploy/scale-in) hands its connections off — clients
reconnect and resume, they are not silently dropped. (Statelessness /
drain review composes horizontal-scalability-reviewer.)
Handle backpressure and slow consumers. A client that cannot keep up
must not back-pressure everyone else: per-connection send buffer with a
bound, and a policy when it overflows (drop-oldest for state-transfer
channels, disconnect-and-let-resume for others, or coalesce). Name the
choice per channel type; unbounded server-side buffering is a memory-leak
outage waiting to happen.
Design reconnect and replay. On reconnect: does the client get a
fresh snapshot, or replay events since a cursor/offset? State the resume
window (how far back), the cursor mechanism, and dedup so replay does not
double-apply. Add reconnect jitter/backoff to prevent thundering-herd
reconnect storms after a blip.
Design presence where needed: how presence/typing state is tracked
(heartbeat + TTL), that it is soft state (rebuilt on reconnect, never the
source of truth), and its own per-tenant/per-resource authorization.
Deliver the channel taxonomy, subscribe-time authz contract, and
scaling plan in the Output Format, with every source and handoff named.
Channel keys are server-derived from trusted state; a client cannot
subscribe to an arbitrary channel string it constructs.
Authorization happens at subscribe time and is re-checked as authority
changes — connect-time-only is called out as insufficient.
The leak boundary is stated as tenant AND per-user/per-resource, and
the fan-out filter enforces it server-side (no client-side filtering
of a tenant-wide broadcast).
Fan-out crosses nodes via a named backbone; the internal event source
is consumed, not re-designed here.
Stateful connections have a per-node limit and a drain/handoff plan;
clients reconnect-and-resume rather than being silently dropped.
Backpressure has a bounded per-connection buffer and an overflow policy
per channel type — no unbounded server-side buffering.
Reconnect defines the resume window, cursor/dedup, and backoff to
prevent reconnect storms.
Presence is treated as soft state with its own authorization.
Gotchas
Authorizing at connect only is the classic realtime leak: the user removed
from a workspace keeps receiving its live events until they disconnect.
Authority is re-checked, or the boundary is a lie between refreshes.
Broadcasting a tenant-wide stream and filtering on the client hands every
client every tenant-mate's data over the wire — the filter must be
server-side, at the fan-out edge.
A client-constructed channel name (subscribe("tenant:42:...")) is IDOR
over WebSocket; the channel key must be derived from the actor's trusted
scope, not accepted from the frame.
Unbounded per-connection send buffers turn one slow mobile client into an
OOM: bound the buffer and define overflow behavior, or a bad network is a
server outage.
Reconnect without jitter is a thundering herd: a 2-second blip reconnects
every client at once and the reconnect load exceeds the steady load.
Presence treated as source of truth strands ghosts: a crashed client's
presence lingers without a heartbeat TTL. Presence is soft state, rebuilt
on reconnect.
Sticky sessions "solve" stateful connections until a node dies with all
its state; design the drain-and-resume path, don't assume the node lives.
Stop Conditions
The authorization model that decides who may subscribe to a channel does
not exist or cannot express per-resource visibility → obtain it from
authorization-matrix-designer / multi-tenant-data-architect before
designing channels; a subscribe-time check against an undefined policy is
a false assurance.
The request is actually to design the internal server-to-server event
backbone (topics, partitions, DLQ, CDC) → route to
streaming-event-architect; designing it here creates a second,
conflicting pipeline.
Delivering the required volume with the required per-user isolation exceeds
what the connection topology can carry (e.g. millions of concurrent
connections with per-message authz) → present the scaling tradeoff and
stop for a human infrastructure decision rather than asserting it scales.
Asked to reconfigure live load-balancer/connection infrastructure → this
skill DESIGNS the topology and drain plan; executing infra changes follows
the repo's approval path.
Supporting Files
evals/evals.json — behavior cases: the live-feed design, the presence +
fan-out edge, the authorize-only-at-connect leak refusal, and the
internal-backbone non-trigger.
evals/trigger-evals.json — discrimination against
streaming-event-architect (THE seam — internal backbone vs client push),
api-event-architect, offline-first-sync-architect (the in-batch
online-push vs offline-sync seam), and notification-webhook-ux-designer.
No references/ — the channel/authz/scaling procedure above is complete;
detail lives in the produced artifacts.