QUESTPIE

Pusher and Soketi

The managed WebSocket preset, its authorization model, and the honest limits of what terminating a connection can revoke.

View markdown

Pick this when you want provider-managed WebSockets, provider-native presence and shared multicast for channel events. pusherRealtime() fills both realtime seams at once, so live(), { realtime: true } and client.channels.* stay exactly as they were.

Wiring it

src/questpie/server/questpie.config.ts
import { pusherRealtime } from "questpie/adapters/pusher";
import { runtimeConfig } from "questpie/app";

export default runtimeConfig({
	db: { url: env.DATABASE_URL },
	realtime: pusherRealtime({
		appId: env.PUSHER_APP_ID,
		key: env.PUSHER_KEY,
		secret: env.PUSHER_SECRET,
		cluster: env.PUSHER_CLUSTER,
	}),
});
OptionNotes
appId, key, secretRequired provider credentials
clusterDefaults to mt1 when no host is given
host, port, useTLSSelf-hosted Soketi. useTLS defaults to true
wsHost, wsPort, wssPortBrowser-side socket endpoint, falls back to host and port
brokerChannelDefaults to a hash of appId and key
authEndpointWhere the browser asks for channel auth
clientEventsDirect provider client events, off unless configured

questpie/adapters/pusher is the only entry that loads the optional pusher and pusher-js peers, so an app that never imports it never pays for them.

What travels over the provider

Live-query snapshots stay private to one edge session, because each snapshot has already passed that principal's row access, field access and afterRead hooks before it is written. The shared broker channel carries only wakes. Framework channel events may use provider multicast, but only after subscribe and publish authorization and Zod validation have both run.

Authorization

QUESTPIE posts the current socket_id, plus the channel name when it is authorizing a channel, to the authenticated realtime auth route, which answers with Cache-Control: no-store. The signed user id is an HMAC of a stable principal identity keyed by your secret, so the provider never learns your user ids. After a reconnect pusher-js asks for fresh user authentication, and channel authorization waits for that sign-in before it returns anything, rejecting a released owner or a stale socket.

The transport declares channelGrantMode: "stateless-signed-grant", meaning generateAuth is side-effect-free local encoding. QUESTPIE evaluates policy and signs outside any database lock, then runs an optimistic generation check before handing the blob over. A concurrent revocation discards it before it reaches the browser. A shared transport that does not declare this capability is rejected before policy runs at all.

Revocation, honestly

channels.revokeAuthority(channel, { subject }) persists the authorization generation in the caller's database transaction. Pusher declares authorityRevocationScope: "principal-connections", which is the honest claim: it can terminate every current connection for one user, but it cannot terminate one logical channel subscription. After termination, fresh per-channel authorization readmits the still-valid bindings and denies the revoked one. The subject must be a user.

CallerFailure behavior
Inside a managed transactionTermination and acknowledgement run inline. A failure throws and rolls back
Standalone commandThe durable cut stands, provider dispatch stays fail-closed, an idempotent retry finishes it

Revocation is not frame-atomic

A frame Pusher already accepted can still arrive during termination, and a conservative disconnect can outlive a later database rollback. A custom shared transport must declare its own revocation scope. QUESTPIE never assumes an unknown provider revokes exactly.

Direct client events

Off by default, and not equivalent to client.channels.*.publish(). Turning them on takes three deliberate keys.

clientEvents: {
	enabled: true,
	acknowledgeProviderWideRisk: true,
	allowedChannels: ["presence-chat-room-one"],
}

The second key is required because Pusher enables client events for the whole provider app, not per channel. The allowlist constrains the QUESTPIE SDK only. A raw provider client can bypass it, and anything it sends skips framework schemas, publish authorization, rate limits, ordered replay and delivery guarantees.

  • Realtime, the two seams and the other adapters.
  • Tuning, every option and limit.
  • Channels, the API this transport carries.

On this page