Broadcast
Cross-replica reactivity over a pub/sub bus (Redis / NATS) for non-postgres dialects — closes the single-instance gap so a write on one pod surfaces on another.
@voltro/plugin-broadcast closes the cross-replica reactivity gap for dialects that have no native cross-instance change feed. Postgres fans out natively via LISTEN/NOTIFY and mariadb via binlog CDC; every other dialect (mysql / mssql / cockroach / planetscale / azure) emits change events in-process on the writing instance only. Without a bus, a write on one pod never wakes a subscription on another pod.
Status: ✓ shipped.
For the full cross-replica reactivity story — when you need it, the dialect matrix, and the boot-log signals — see Database → Multi-replica. This page documents the plugin itself.
Wiring
// app.config.ts
import { broadcastPlugin } from '@voltro/plugin-broadcast'
export default {
type: 'api' as const,
name: 'api',
plugins: [broadcastPlugin()],
}Broadcast is opt-in — it never turns on from a shared REDIS_URL alone. The bus enables only on an explicit signal: BROADCAST_URL, BROADCAST_REDIS_URL, BROADCAST_PROVIDER, or the connection / provider / url option. So a deployment running the cache on REDIS_URL keeps broadcast on the single-process memory bus until you deliberately switch it on. This differs from ctx.cache / ctx.kv, which are gated by their own CACHE_BACKEND / KV_BACKEND selectors — same principle, explicit per concern.
Once enabled on redis, the connection resolves own → shared: an explicit url / broker-agnostic BROADCAST_URL → BROADCAST_REDIS_URL (this connection's own) → the shared REDIS_URL. A redis:// / rediss:// URL selects Redis; a nats:// URL selects NATS.
broadcastPlugin({ connection: 'broadcast' }) // opt in + share the app's redis: BROADCAST_REDIS_URL → REDIS_URL
broadcastPlugin({ provider: 'redis', url: process.env.BROADCAST_URL }) // or an explicit url / providerThe connection option follows the same <NAME>_REDIS_URL → REDIS_URL convention as the cache, KV and rate limiter — but setting it (or another explicit signal) is the deliberate opt-in; the shared REDIS_URL is only the connection detail. NATS stays broker-agnostic via BROADCAST_URL.
BroadcastPluginOptions: provider ('redis' | 'nats' | 'memory' or a BroadcastProvider), url, connection (named redis connection, default 'broadcast'), and name (disambiguates multiple instances of the plugin in one app — the second instance's name becomes @voltro/plugin-broadcast#<name>).
How it works
The plugin is mostly a carrier: it resolves a BroadcastProvider at construction and exposes it so the serve pipeline can attach the bus to the live DataStore after the store is built (the bus needs the store's onChange + injectExternalChange seam, which doesn't exist at plugin-activation time).
The bus is additive to the inline emit path:
- It publishes
{ origin, event }on thevoltro:changeschannel. - It injects remote events into every other replica's store, skipping its own origin so there's no double-emit.
- A broker outage degrades cross-replica fan-out only — local reactivity keeps working.
The plugin declares the network:outbound:* permission. The boot banner names the resolved tier (cross-instance via redis/nats, or off for the dialect when no broker is configured).
Caveat — app-mutation changes only
The bus carries changes written through ctx.store (app mutations). It does not capture out-of-band DB writes (a psql session, another service) — only postgres LISTEN/NOTIFY and mariadb binlog observe those. Without a broker URL the plugin falls back to the in-process memory bus (single-process only) and warns at boot.