Skip to content
OpenPulseDocs

Build

Realtime

Chat rooms, typed streams, presence and fan-out events on one connection.

One session, many lanes. connect() returns the shared session for your key or seat; each lane opens on first use and all of them ride one connection.

TypeScript
import { connect } from '@openpulse/sdk'          // servers: reads OPENPULSE_REALM_KEYimport { connect } from '@openpulse/sdk/browser'  // browsers: connect({ getToken })

Typed messages#

Give every kind of message a schema. defineExtension names it and parses it, so a subscriber only gets values of that type.

TypeScript
import { defineExtension } from '@openpulse/sdk'export const Message = defineExtension<{ from: string; text: string }>({  schema: 'app.message@v1',  codec: { parse: (v) => v as { from: string; text: string } }, // or a zod schema's parse})

Chat#

TypeScript
const room = pulse.chat('support-42')const off = room.subscribe(Message, (msg, event) => render(msg, event.sequence))await room.publish(Message, { from: '@ana', text: 'On my way' })

Delivery to readers is free: one message to a thousand people counts once. Readers that reconnect pick up from their last sequence.

Any stream#

For live data that is not a conversation (orders, prices, job progress), open a stream by name.

TypeScript
const orders = pulse.stream('orders')                    // preset api_event_streamorders.subscribe(Order, (order) => update(order))await orders.publish(Order, { id: 'o-17', status: 'packed' })

Presence#

Who is here and what they are doing. Each participant sets their own state; the room keeps only the newest per participant, handles heartbeats and expiry, and tells everyone when the list changes. Presence updates cost a tenth of a message.

TypeScript
const here = pulse.presence<{ cursor: number; typing: boolean }>('doc-42', { id: user.did, ttlMs: 3000 })here.onChange((peers) => drawCursors(peers))await here.set({ cursor: 118, typing: true })// …await here.close()

For a shared place where people and agents work together, pulse.space() adds names, what each participant is looking at and doing, and which person an agent acts for.

Events#

pulse.events(scope) is a fan-out lane for invalidations: “order o-17 changed, refetch”. Keep your data in your own database and use events to tell clients when to look.

Connection state#

TypeScript
pulse.onStatus(({ state, transport }) => {  // state: 'live' | 'degraded' | 'auth-error'   transport: how pushes arrive right now  banner.show(state !== 'live')})

Limits to know#

An event counts once per started 64 KiB. Each realm may post up to its plan’s fair-use rate (Hobby 1,000 events a second, Pro 10,000); past it the relay answers 429 and the SDK resends after a second. Details in usage, limits and billing.