Build
Tenants and seats
Serve many customers from one realm: short-lived seats scoped to the workspaces a person belongs to.
Most apps serve many customers from one realm. Give each signed-in person a seat: a short-lived token that reaches only the tenants (workspaces, teams, accounts) your own membership check allows. The relay enforces it on every read, write and live subscription.
Issue seats from your backend#
import { issueSeat } from '@openpulse/sdk/server'import { auth } from '@/auth'export async function POST(request: Request) { const { session } = await auth.getSession(request) if (!session) return new Response(null, { status: 401 }) const workspaces = await workspacesOf(session.user.did) // your rules return Response.json(await issueSeat({ did: session.user.did, realm: 'acme', tenants: workspaces }))}issueSeat needs a service token with the wire_mint capability in OPENPULSE_SERVICE_TOKEN. It never runs in a browser.
Use them in the browser#
const pulse = await connect({ getToken: () => fetch('/api/pulse/seat', { method: 'POST' }).then((r) => r.json()),})pulse.chat('general') // becomes realm/acme/t/<workspace>/chat:generalThe SDK asks for a new seat about a minute before the old one expires, and again if the relay rejects it. The first tenant in the list is the session’s default; pass tenant to issueSeat to choose another.
Revoking access#
Remove someone in your system and they lose the tenant within one seat lifetime (ttlSeconds, 30 to 3,600, default 900), open subscriptions included: the relay closes a connection whose seat expired, and your endpoint refuses the renewal.