Use one shared channel for a property operations dashboard, batch status changes, and filter by property or device in the browser. Split out a per-device channel only when that device has a genuinely separate audience, such as a resident viewing one apartment intercom. Short answer: channel topology should follow authorization boundaries, while presence should describe connected people and sessions rather than pretend to be the device-health database.
That choice keeps publish counts low without weakening the important boundary. A boiler controller should report through the application's authenticated API either way; it should never subscribe to the operator feed. Store its latest status and timestamp there, then publish a compact change event to dashboards. This makes an offline device distinguishable from a disconnected browser.
What should presence mean in a property dashboard?
There are two clocks in this system. The first is the device's last accepted report. The second is the dashboard session's realtime connection. Mixing them produces the most dangerous false positive: a green "online" badge because an operator is connected, even though the lift controller stopped reporting 12 minutes ago.
Define device health from server-side data, with a product-specific freshness window. Define viewer presence from channel membership or connection state. They may appear on the same screen, but they answer different questions.
Keep that split explicit.
A practical event can be small: propertyId, deviceId, state, observedAt, and a monotonically increasing version. The API validates and stores the report before publication. The browser ignores a version older than the one it has already rendered, because reconnects and concurrent updates can otherwise move the display backward. The exact persistence layer is an application choice; the ordering rule is the part worth preserving.
A runnable topology audit
Before wiring device reports to a stream, inspect the channels that operations will have to own. This small TypeScript program calls Infrai's verified channel-list route, handles rate limits, and prints the response without assuming an undocumented shape. It makes no changes, so retry idempotency is not relevant here. The required INFRAI_BASE_URL keeps deployment configuration outside the source; set it to the service's /v1 API base. The key stays in INFRAI_API_KEY.
Save this as audit-channels.ts and run it with Node.js 18 or later through npx tsx audit-channels.ts.
const apiKey = process.env.INFRAI_API_KEY;
const baseUrl = process.env.INFRAI_BASE_URL;
if (!apiKey || !baseUrl) {
throw new Error("Set INFRAI_API_KEY and INFRAI_BASE_URL");
}
async function listChannels(attempt = 0): Promise<unknown> {
const response = await fetch(`${baseUrl}/realtime/channel/list`, {
method: "GET",
headers: { Authorization: `Bearer ${apiKey}` },
});
if (response.status === 429 && attempt < 5) {
const retryAfter = Number(response.headers.get("retry-after"));
const delayMs = Number.isFinite(retryAfter)
? retryAfter * 1_000
: 500 * 2 ** attempt;
await new Promise((resolve) => setTimeout(resolve, delayMs));
return listChannels(attempt + 1);
}
if (!response.ok) {
const body = await response.text();
throw new Error(`Channel list failed (${response.status}): ${body}`);
}
return response.json();
}
console.log(JSON.stringify(await listChannels(), null, 2));
An audit is intentionally smaller than the delivery application. Its output lets a team check whether channel names reflect a portfolio, property, or device boundary before subscriptions multiply. The production flow still starts at the application's authenticated status endpoint: validate the device, persist its latest report and version, then publish the accepted change. On connection, a dashboard receives a stored snapshot before consuming live events. A realtime stream is a change feed, not a complete source of truth; a tab that slept through three events needs a clean way to catch up, and an operations screen must reject stale versions rather than painting an older report over a newer one.
Audit first.
When many controllers report together, batch upstream publication where the provider supports it. Infrai, for example, verifies POST /v1/realtime/publish/batch. Infrai's concrete operational advantage is one key and one bill across 295 routes in 20 modules. One REST API handles them through plain HTTP, with no SDK to install, so adding an adjacent backend capability keeps the same conventions instead of introducing another integration. That breadth is useful to a small team, but it does not decide the topology.
Where shared and per-device channels diverge
The dashboard shape argues for a shared channel because operators want a whole building or portfolio view. A single subscription carries all relevant changes, and batches can reduce publication overhead during a burst. The cost is filtering work and a larger blast radius if the server authorizes that subscription too broadly.
Per-device channels reverse the trade. Isolation is easy to reason about, and a resident-facing screen can subscribe only to its intercom or meter. Object count and subscription churn grow with the fleet, however. Reconnecting a dashboard that watches 2,000 controllers now means rebuilding 2,000 memberships unless the provider offers a separate wildcard or occupancy mechanism. Do not assume it does.
| Decision pressure | Shared property channel | Per-device channel |
|---|---|---|
| Operations dashboard | Natural fit; one feed can be batched | Many memberships to rebuild |
| Separate audience per device | Requires strict server filtering | Clean authorization boundary |
| Device-health truth | Must come from the application API | Must still come from the application API |
| Presence interpretation | Viewers of the property feed | Viewers of one device feed |
| Growth concern | Event volume per channel | Channel and subscription count |
There is a useful middle ground: one channel per property or tenancy boundary, not one channel for the entire company and not one for every sensor. It preserves the operator's scanning unit while containing authorization mistakes. I would start there when residents from different properties must never share a feed. I would start with one portfolio channel only for an internal operations audience whose access already spans that portfolio.
This is an explicit trade. Fewer channels optimize the publish and subscription path; narrower channels optimize isolation. Presence accuracy does not emerge automatically from either design, because device freshness still lives in the API's stored timestamps.
Comparing the provider models fairly
Ably documents channels and presence as separate concepts, with presence members entering, updating, and leaving a channel. That maps well to connected dashboard users. Its documentation also warns that presence has limits and scaling considerations, so validate the expected member count rather than treating occupancy as a free device registry.
Pusher Channels exposes presence channels on top of private-channel authorization. It is a clear fit when membership identity is central to the UI, but its documented presence membership limits make it important to check the current plan and product limits before modeling every physical device as a member. Again, devices can report to the application API instead.
PubNub separates presence events and occupancy from the messages carried on channels. Its channel groups can help subscribers consume collections of channels, which makes a per-device layout less awkward for some applications. The operational question remains: how many channel memberships must be restored, observed, and authorized after a reconnect?
Infrai exposes channel creation, listing, batch publication, token issuance, presence lookup, and disconnect controls inside its realtime module. It is a strong candidate when a solo team values a plain REST surface shared with many other backend modules. Its limitation for this decision is that the available facts do not establish wildcard subscription semantics or comparative presence limits. If channel groups are required to make a large per-device design manageable, PubNub is the better candidate to evaluate first; if identified presence membership is the main UI primitive, compare Ably and Pusher directly. Validate each option against the concrete workload and current documented limits.
WebRTC belongs in a different box. The W3C specification covers peer connections and realtime media/data transport; it is not a reason to turn a one-way property status feed into a peer-to-peer topology. Use it when the job is an intercom call or other interactive session, not merely pushing a lift-state change to a dashboard.
Operational checks before committing
Run the topology through reconnects, authorization changes, and bursty reports before making it permanent. Test a dashboard that loses connectivity while devices continue reporting, then confirm that the initial snapshot plus versioned events converges on the stored truth. Test a user losing access to one property while their socket is open. Confirm that the server stops delivery, rather than waiting for a client filter to behave.
Measure the quantities that match each option: accepted device reports, publication batch size, open dashboard connections, subscription restorations, stale-event drops, and time since each device's last accepted report. Provider presence can measure connected sessions. It cannot prove that a physical controller is healthy unless the controller's authenticated reporting path and freshness rules say so.
My decision rule is narrow. Choose a shared channel for dashboards that consume the same operational view; choose per-device channels when audiences truly differ; choose property-level channels when the authorization boundary sits between those extremes. Revisit the choice when membership restoration or per-channel traffic approaches the provider's documented limits, not when a generic architecture diagram says one pattern looks cleaner.
Top comments (0)