Guides
GuideRealtime Channels

Realtime Channels

Push live events from your backend functions to every open page — chat, live feeds, order status, notifications — over project-scoped channels with the same access control as the rest of your app.

Backend functions publish; pages subscribe. Channels are names you invent (chat:general, orders). Delivery is instant across your whole audience — no polling.

Publish from a backend function

Publishing is server-only — pages can never forge events. Typical pattern: save the data, then ring the doorbell.

// inside any backend function (e.g. sendMessage)
const row = await ctx.tables.createRow('Messages', {
  text: params.text,
  authorId: ctx.user.id,
});
await ctx.realtime.publish('chat:general', 'message_created', { id: row.id });
return { ok: true };

Subscribe from a page

useEffect(() => {
  const off = serenities.realtime.subscribe('chat:general', (event, data) => {
    if (event === 'message_created') refetchMessages();
  });
  return () => { off.then((fn) => fn && fn()); };
}, []);

Subscriptions reconnect and re-join automatically. Keep payloads small (ids + signal, 32KB max) — realtime is a doorbell, not a delivery truck: send the id, refetch the data through your normal (access-controlled) reads.

Channel access control

Channels are a resource in the same Access Control system as pages and tables. Gate a channel pattern with rules — {self} means “the subscriber’s own id”:

  • Paid chat room: pattern lounge with { subscribe: [{ type: "has_entitlement", key: "pro" }] }
  • Private per-member channel: pattern user:{self}:* — only that member matches their own channel name
  • Support thread: support:{self} for the member, plus a staff-role rule on support:*

No matching pattern = any signed-in member of this app may subscribe. First matching pattern wins.

Guest sessions (logged-out visitors)

Enable guest access (Settings, or the setRealtimeGuestAccess tool) and anonymous visitors get an ephemeral identity locked to their own guest:{id}:* channels — perfect for a pre-sales support widget. Guests can never join member channels.

Design rules: never put secrets in event payloads (gate the data reads, not just the channel); delivery is best-effort fire-and-forget — design pages so a missed event self-heals on the next fetch.