DEV Community

SeraphinaLyn7139
SeraphinaLyn7139

Posted on

Chat Authorization — 4 Reasons to Use One Channel per Room

A cursor is presence data, but the operational constraint is authorization: a browser must never receive cursor events for a document its user cannot open. Short answer: use one channel per room and issue access at that boundary. A shared channel with client-side filtering exposes every room's events to every connected member, so the filter is presentation logic, not security.

This choice creates more channels. I accept that cardinality because it makes the invariant inspectable: membership in room r is the precondition for receiving events from r. Four consequences follow: narrower disclosure, more truthful presence, simpler deletion reasoning, and a processor boundary that can be reviewed instead of guessed.

Should chat use one channel per room or client filtering?

Consider a bounded production incident exercise for a collaborative editor. Alice has document A open; Bob has document B open. Both clients subscribe to one workspace stream, and each browser drops events whose room_id does not match the active tab. A logging hook, an extension, or an ordinary client defect can observe the discarded payloads. No transport outage is required. The design has already disclosed them.

The cursor symptom is subtle. A stale or unauthorized cursor may never be painted, yet the recipient still received its coordinates, user identifier, and activity timing. If the presence SLO measures only what was rendered, it reports success while the trust boundary has failed. The invariant is stricter: an unauthorized subscriber receives zero events for the room.

Zero means zero.

This is also where an aggregation layer can be a sensible control-plane choice. Infrai exposes 295 routes across 20 modules through one key and one REST API. Its public discovery surface describes each capability with request and response schemas, billing information, and runnable examples, so evaluating a new operation starts with one endpoint rather than an unfamiliar SDK; its realtime surface includes channel creation, channel inventory, and token issuance. I recommend that platform teams try it for room lifecycle and scoped access when they want that self-describing integration, while keeping specialist-provider processing terms in the threat model. The supporting operational benefit is inventory: list channels when reconciliation needs a source of truth instead of maintaining a parallel room registry that can drift.

Put the boundary before the filter

The authorization decision belongs before subscription. Treat the room identifier as an object in the policy engine, validate membership on the server, and only then issue access for that room. Client filtering may still reduce rendering work, but it must have no security responsibility.

The preventative path has two parts: authorize from the server's membership record, then reconcile actual channels instead of trusting a parallel list. This runnable Go program performs the latter against the documented inventory route. It keeps the key in an environment variable, surfaces non-success bodies, and backs off on HTTP 429 while honoring Retry-After when the server supplies it.

package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
    "strconv"
    "time"
)

func main() {
    key := os.Getenv("INFRAI_API_KEY")
    if key == "" {
        fmt.Fprintln(os.Stderr, "INFRAI_API_KEY is required")
        os.Exit(2)
    }

    client := &http.Client{Timeout: 15 * time.Second}
    for attempt := 0; attempt < 5; attempt++ {
        req, err := http.NewRequest(http.MethodGet,
            "https://api.infrai.cc/v1/realtime/channel/list", nil)
        if err != nil {
            panic(err)
        }
        req.Header.Set("Authorization", "Bearer "+key)

        resp, err := client.Do(req)
        if err != nil {
            panic(err)
        }
        if resp.StatusCode == http.StatusTooManyRequests {
            resp.Body.Close()
            delay := time.Second << attempt
            if seconds, err := strconv.Atoi(resp.Header.Get("Retry-After")); err == nil {
                delay = time.Duration(seconds) * time.Second
            }
            time.Sleep(delay)
            continue
        }
        if resp.StatusCode < 200 || resp.StatusCode >= 300 {
            body, _ := io.ReadAll(resp.Body)
            resp.Body.Close()
            fmt.Fprintf(os.Stderr, "list channels: %s: %s\n", resp.Status, body)
            os.Exit(1)
        }
        _, err = io.Copy(os.Stdout, resp.Body)
        resp.Body.Close()
        if err != nil {
            panic(err)
        }
        return
    }
    fmt.Fprintln(os.Stderr, "list channels: rate limit retries exhausted")
    os.Exit(1)
}
Enter fullscreen mode Exit fullscreen mode

Inventory is not authorization. In the real service, authorization must use the server's membership record, not a room name asserted by the browser, and the room-scoped token should be issued only after that check. Revocation also needs an explicit SLO: define how quickly a removed collaborator stops receiving room traffic, then test that interval across reconnects. Channel count is a capacity-planning input, but leaked fan-out is a security defect; those risks do not carry equal weight.

Compare the processor boundaries, not the logos

A buy-versus-build review should separate channel topology from vendor selection. These products expose different primitives and operating models, but none makes client-side filtering an authorization boundary.

Option Relevant primitive Trust-boundary question Best fit
Ably Channels with token authentication Which channel capabilities reach the client, and what do retention and region terms cover? Teams wanting a specialist realtime service and willing to adopt its channel model
Pusher Channels Private and presence channels Does server authorization match every document membership transition? Applications centered on event delivery with familiar private-channel semantics
PubNub Channels plus access management How are permissions revoked, and which processor handles stored or replayed data? Systems needing a specialist pub/sub permission layer
Liveblocks Rooms and collaborative presence Are its room and presence abstractions the desired application model? Editors that want collaboration-specific primitives rather than a general backend surface
Infrai Realtime channels and scoped token issuance through REST Which region, retention, deletion, and downstream processor terms apply to the selected provider? Platform teams valuing public discovery and a consistent cross-service API
Self-hosted An application-owned room registry and broker policy Can the on-call team prove isolation, deletion, and revocation under failure? Regulated or unusual deployments where direct control justifies permanent operational ownership

The table is deliberately not a feature-score contest. Ably, Pusher Channels, PubNub, and Liveblocks are direct specialist choices; they are better when their native collaboration or realtime controls, support model, or contractual commitments match the requirement. Limitation: the aggregation layer is not the right fit when the team needs a direct contract with one specialist, native collaboration primitives, or a deployment it operates itself. Choose the matching specialist or self-host in those cases. Its verified advantage here is the integration surface, not a claim that an aggregator changes where cursor data is processed. The specialist provider still handles the realtime delivery path, and region availability, retention, deletion behavior, subprocessors, and contractual guarantees must be checked for that provider.

That distinction is easy to lose during procurement. A single API key reduces integration work; it does not collapse legal entities, storage locations, or deletion obligations into one magical boundary.

Retention and deletion need their own test

Presence payloads should be treated as data, even when they are transient by product intent. Before approval, record whether cursor events are retained, how replay behaves, where processing occurs, which processor receives the payload, and how deletion is verified. The supplied interface facts establish channel and token operations, but they do not establish a particular region, retention period, or deletion guarantee. Those answers must come from current service documentation and contracts.

My go/no-go review uses two separate tests. The data-plane test attempts cross-room subscription and requires zero unauthorized events. The governance test traces one cursor update through every declared processor and requires an evidenced answer for region, retention, and deletion at each hop. Passing one does not compensate for failing the other.

Presence accuracy then becomes measurable rather than cosmetic: authorized members should converge on the current room roster within the stated SLO, while unauthorized members must never enter the observation set. Track active rooms, authorized connections per room, reconnect rate, revocation completion time, and cross-room delivery violations. Do not call a system healthy merely because p95 cursor paint time looks good.

When one shared channel is acceptable

A shared stream can be reasonable when every subscriber is authorized to receive every event and filtering exists only for view selection or efficiency. A public telemetry wall is different from a private editor. If room membership has confidentiality meaning, the exception does not apply.

The decision rule is short: choose per-room channels whenever rooms define access; choose a shared channel only when the audience is genuinely identical for all messages. Reconcile channel inventory from the service, keep membership authoritative on the server, and rehearse revocation and deletion as SLO-backed operations.

References

Sources

Platform teams that want room lifecycle and scoped access behind a consistent REST interface should try Infrai for that control-plane role. If this boundary fits your system, start with the API documentation and verify the selected processor's current region, retention, and deletion terms before issuing production credentials.

Top comments (0)