Realtime apps

Live updates without a socket server

Row changes, broadcast messages and presence arrive on one connection, filtered by the same policies as your queries.

realtime
volcano cloud config deployrealtime enabled · broadcast · presence · postgres_changes✓ wss://…/realtime/v1/websocket ready# then, in the browserchannel.onPostgresChanges('INSERT', 'public', 'cards', render)INSERT public.cards { id: 'c_18f…', title: 'Ship it' }# subscribers only receive rows their policies already allow
Overview

Collaboration is a state problem, not a socket problem

Standing up a WebSocket server is the first hour. The rest is the hard part: keeping the socket's idea of the world in step with the database, deciding who may hear which event, and cleaning up presence when a laptop closes without saying goodbye.

Volcano streams the changes from Postgres itself and applies your read policies per subscriber. Add broadcast for the things you do not want to persist, like a cursor position, and presence for who is currently here — all on the same connection.

Changes

The database is the event source

Subscribe to INSERT, UPDATE or DELETE on a table and the change arrives because it was committed, not because someone remembered to publish it. A background job's write reaches the UI on the same path a user's does.

  • INSERT, UPDATE, DELETE, or all events
  • Filter by schema and table
  • Writes from anywhere reach subscribers
one write, two clients
# a scheduled function updates a rowupdate cards set status = 'done' where id = 'c_18f';browser A  UPDATE public.cards → status: donebrowser B  UPDATE public.cards → status: donenothing had to publish an event
Authorization

The stream is filtered per subscriber

A subscription is bounded by the same row-level policies as a query. Two people on the same channel see different rows if that is what your rules say, so a live feed cannot become a leak.

  • Read policies apply to the stream
  • No second permission model for events
  • Per-subscriber, not per-channel
volcano.dev/dashboard/realtime
Realtime settings with change streams, broadcast and presence enabled
Live-only data

Cursors and who is here

Broadcast carries messages you never want in a table, and presence tracks who is on the channel with automatic cleanup when a connection drops. Neither needs a row.

  • Broadcast for cursors, typing, nudges
  • Presence syncs the full roster on every change
  • Cleanup happens when the socket closes
presence on board-7
# onPresenceSync fires with the whole rostersync  ada@example.com, cleo@example.com…cleo closes her laptopsync  ada@example.com1 present · no cleanup job to write
Path to production

How it works

  1. 01

    Turn realtime on

    Enable it in volcano-config.yaml and deploy. There is no server to provision.

  2. 02

    Write the read policy

    The policy that filters a SELECT is what filters the stream, so get it right once.

  3. 03

    Subscribe to changes

    Listen for row events on the tables that drive your UI and apply them to local state.

  4. 04

    Cursors and presence

    Broadcast the things that should not persist, and track who is on the channel.

Code

Enable it, secure it, subscribe, collaborate

YAML
version: 1 realtime: enabled: true functions: - name: board-api public: true # Deploying this creates the WebSocket endpoint for the project: # wss://<api-host>/realtime/v1/websocket # # Preview what will change before applying: # volcano cloud config deploy --dry-run
Return

What you skip

0 serversto run or scale

The WebSocket endpoint appears when you enable realtime in the manifest.

1 policyfor queries and streams

The rule that filters a SELECT filters what each subscriber receives.

3 channelson one connection

Row changes, broadcast and presence share a single WebSocket.

What you get

Always in step

The UI updates because the row was committed, not because code remembered to publish.

Streams that cannot leak

Subscribers receive only rows their policies already allow.

Presence that cleans itself

A dropped connection leaves the channel without a reaper job.

Platform

Built on Volcano

Realtime
  • The connection

    Postgres change streams, broadcast and presence over one WebSocket, enabled from the manifest.

Databases and vector
  • The source of truth

    PostgreSQL 15 or 16 whose committed writes are the events, with policies filtering each stream.

Authentication
  • Who is on the channel

    Verified sessions, so presence shows real users and policies know who is subscribing.

Functions and agents
  • Server-side writes

    Functions that update rows and reach every subscriber without publishing anything.

Frequently asked questions

Read the docs
How do I turn realtime on?

Set realtime.enabled to true in volcano-config.yaml and run volcano cloud config deploy. That creates the WebSocket endpoint for the project; there is no server to provision.

Can a subscriber receive rows they cannot read?

No. Row-level security applies per subscriber, so two people on the same channel can receive different subsets of the same table.

When should I use broadcast instead of row changes?

When the message should not persist. Cursor positions, typing indicators and nudges are broadcast; anything you would want after a refresh should be a row.

What happens when a connection drops?

Presence drops that client and syncs the new roster to everyone else, so participants see them disappear without a cleanup job on your side.

Do writes from a function reach subscribers?

Yes. The stream comes from Postgres, so a scheduled job's update arrives at every subscriber exactly like a write from the browser.

How many channels can a client open?

Several, over the one WebSocket connection. A channel is typed as postgres, broadcast or presence, so a collaborative board usually opens one of each and unsubscribes from all of them when the view unmounts.

Ready to make it live?

Build, deploy, and scale on Volcano's global platform — free to start, with no infrastructure to manage.

Explore more solutions