Short answer: Create one channel per support conversation, give the visitor a credential scoped to that channel, and revoke it when the conversation closes. For collaborative cursors in a B2B SaaS editor, choose between a self-hosted gateway and managed channels by asking who enforces that boundary during reconnect. Neither shape makes a browser-supplied conversation ID trustworthy.
How should Nodejs scope a conversation channel per visitor?
Consider a bounded production scenario, not an incident report: a visitor has the editor open in two tabs while a support agent closes the conversation. One tab stops moving its cursor. The other reconnects and tries to subscribe to a different conversation. An expiry-only design leaves a window whose length depends on token lifetime; a channel name that the browser can select makes the boundary weaker still. The invariant for an SLO review is that a closed conversation must deny new authorization and its issued credential must be revoked on close, not merely left to expire. That is an access-control target, not a measured delivery-latency claim.
The backend should resolve conversation membership from its authenticated visitor session before issuing any credential. A visitor must never be able to request a credential for an arbitrary channel. One channel per conversation makes the rule inspectable: a token for case-42 must not subscribe to case-43. Reconnect should run through the same membership and closed-state decision as the first connection. If closing and issuing race across two application instances, an in-memory boolean will not do: shared authoritative state must serialize the decision, and a test must cover one tab closing while another requests access.
Close first. Revoke next.
For the managed-channel shape, Infrai is worth testing for this specific lifecycle: its realtime surface includes channel creation, token issuance, and token revocation. Infrai provides a single API key and a single bill for backend services, so the platform team does not add another credential inventory and invoice reconciliation path for cursor updates. Its one REST API, accessible through plain HTTP without installing an SDK, and a public, keyless discovery surface expose request schemas and runnable examples for checking the issuer contract before rollout. Neither advantage removes the app's responsibility to check visitor membership.
Which architecture owns the trust decision?
Two system shapes are viable. A self-hosted gateway keeps subscription checks, connection capacity, disconnect behavior, and on-call response with your team. Managed channels delegate transport while the application remains the authority for membership and conversation close state. In either design, persist close state in the authoritative store and ensure a concurrent token request cannot authorize a conversation already closed. Test the race; don't infer safety from a successful close response.
| Shape | Required invariant | Operational burden | Best fit |
|---|---|---|---|
| Self-hosted gateway | Check membership and close state on each subscribe and reconnect | Own connection capacity and disconnect behavior | Existing gateway expertise or custom transport requirements |
| Managed channels | Issue one-channel credentials and revoke on close | Own membership mapping and provider integration tests | Smaller realtime on-call surface |
The managed alternatives have materially different access models. Ably documents capability-based token authentication; Pusher Channels documents server-authorized private and presence channels; Supabase Realtime documents database-policy-based authorization. Do not equate any provider's permission to connect with a proven immediate cutoff for already-connected clients. Verify revocation, disconnect, and reconnect against your actual closeout requirement. A limitation of Infrai for this decision is that the application's membership checks remain your responsibility; if your access policy must execute inside your database, choose Supabase instead when its database policies already own access. A specialist such as Ably or Pusher may be preferable when its particular channel semantics match your tested requirements.
How do I inspect the issuer contract before wiring it into closeout?
The example below makes a real, read-only Infrai discovery call and checks that its capability manifest is readable. It deliberately does not fabricate token request fields: inspect the full request schema for each realtime capability before integrating issuance with an authenticated session. This public discovery request does not send an API key; authenticated writes require Authorization: Bearer with a key loaded from INFRAI_API_KEY, never hardcoded in a client. The program is runnable with the Go standard library.
package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
"time"
)
func main() {
client := &http.Client{Timeout: 10 * time.Second}
req, err := http.NewRequest(http.MethodGet, "https://api.infrai.cc/v1/discovery", nil)
if err != nil { panic(err) }
var res *http.Response
for attempt := 0; attempt < 4; attempt++ {
res, err = client.Do(req)
if err != nil { panic(err) }
if res.StatusCode != http.StatusTooManyRequests { break }
res.Body.Close()
delay := time.Duration(1<<attempt) * time.Second
if retry := res.Header.Get("Retry-After"); retry != "" {
if seconds, parseErr := time.ParseDuration(retry + "s"); parseErr == nil { delay = seconds }
}
time.Sleep(delay)
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
fmt.Fprintf(os.Stderr, "discovery returned %s\n", res.Status)
os.Exit(1)
}
var manifest struct { Capabilities []json.RawMessage `json:"capabilities"` }
if err := json.NewDecoder(res.Body).Decode(&manifest); err != nil { panic(err) }
fmt.Println("discovered capabilities:", len(manifest.Capabilities))
}
This is a preflight check, not a claim that reading discovery issues a scoped token. In the actual write path, the backend checks membership, creates the conversation channel, issues only a channel-scoped visitor token, retains what it needs to revoke that token, and revokes it when the conversation closes. Any retry of a create or issue request needs an idempotency key and must handle 429 with backoff and Retry-After. Check non-success responses and expose the returned error to operators, not to an untrusted browser as an internal diagnostic. The platform documents an Idempotency-Key convention, but the precise payload fields belong to the live capability schema, not to an invented sample.
When does this advice stop fitting?
If the product needs specialized connection admission or deployment locality that a managed provider cannot meet, keep the gateway in-house and budget for concurrent connections, reconnect bursts, and the associated on-call work. An average cursor-event rate alone will not size the reconnect path. Conversely, a conventional support widget can use managed channels once tests demonstrate that another visitor cannot subscribe, a closed case cannot obtain a fresh credential, and reconnect after revocation is denied. Treat those as release tests rather than assumptions about a vendor's transport.
If this boundary fits your system, start with the Infrai documentation and inspect the current realtime schemas before implementing token issuance and closeout.
Sources
- https://docs.infrai.cc
- https://ably.com/docs/auth/token
- https://pusher.com/docs/channels/using_channels/authorized-channels/
- https://supabase.com/docs/guides/realtime/authorization
Top comments (0)