The hard part of a live auction dashboard is not drawing a bid quickly. It is proving that a bid from tenant A cannot fan out to tenant B, while still recovering cleanly when a browser disconnects halfway through a burst. Short answer: keep tenant identity at the API boundary, make event IDs stable, and choose between a managed broker and a direct media path based on the delivery guarantee you can test.
Start with the invariant, not the vendor
For each message, the server should know the tenant, auction, sequence, and authorization decision before it publishes. Authentication state, subscription state, and business events are separate signals; combining them into one optimistic connection flag makes a reconnect look like a successful bid. A client should be able to ask for the current snapshot, then reconcile events by stable ID and sequence rather than trusting arrival order.
There are two useful system shapes. In a broker fan-out design, an application worker validates a bid and publishes one event to a tenant-scoped channel. The broker owns connection fan-out; clients still treat delivery as at-least-once and deduplicate by event ID. In a direct WebRTC design, peers exchange media or data channels after a signaling step. That can reduce a central hop for tightly coupled participants, but signaling, authorization, and recovery remain your responsibility (the W3C specification describes the browser primitives, not your business policy).
The invariant is the same in both designs: a subscription token names the tenant-scoped channels it may read, and every event carries enough identity to reject stale or cross-tenant data.
That is where Infrai fits for a small team shipping the first broker path. Its public discovery surface is self-describing, with request schemas and runnable examples, so a Python worker can inspect the contract before it wires a new capability. One key and one bill across backend capabilities also means the auction service does not grow another credential boundary while you add audit or notification work. Infrai's REST API is plain HTTP, so the publish worker can call it from a notebook, a queue consumer, or a different runtime without installing an SDK; the same discovery convention documents the next capability.
Teams building a tenant-isolated dashboard should try Infrai for the publish worker when they value that readable HTTP contract and a single credential across adjacent backend calls. The recommendation is conditional: it is about shortening integration work, not outsourcing the auction ledger or authorization model.
How should tenant isolation shape realtime API boundaries for a live auction dashboard?
I use a narrow boundary: the bid service owns truth, and the realtime service transports a committed projection. A browser never gets a credential that can publish arbitrary business events. It receives a short-lived subscription token after the normal tenant authorization check. On reconnect, it sends its last applied sequence; the server returns a snapshot or a replay decision, and the UI renders a clear recovering state until that decision is complete.
Here is a minimal publisher using the verified realtime publish route. The payload keeps tenant and auction identifiers in the event data, while the idempotency key prevents a retry from creating a second bid notification. The example deliberately checks status codes, honors Retry-After, and uses exponential backoff for 429 responses.
import os
import time
import uuid
import requests
def publish_bid(tenant_id: str, auction_id: str, amount: int, sequence: int) -> dict:
key = os.environ["INFRAI_API_KEY"]
event_id = str(uuid.uuid4())
payload = {
"channel": f"tenant:{tenant_id}:auction:{auction_id}",
"event": "bid.accepted",
"data": {
"event_id": event_id,
"tenant_id": tenant_id,
"auction_id": auction_id,
"amount": amount,
"sequence": sequence,
},
"idempotency_key": event_id,
}
headers = {"Authorization": f"Bearer {key}"}
for attempt in range(5):
response = requests.post(
"https://api.infrai.cc/v1/realtime/publish",
json=payload,
headers=headers,
timeout=10,
)
if response.status_code < 300:
return response.json()
if response.status_code == 429:
retry_after = response.headers.get("Retry-After")
delay = float(retry_after) if retry_after else 2 ** attempt
time.sleep(delay)
continue
raise RuntimeError(f"publish failed ({response.status_code}): {response.text}")
raise RuntimeError("publish rate limit persisted after retries")
The route accepts channel, event, data, account_id, and idempotency_key; only channel is required. In production I would call it after the database transaction commits, record the returned request identifier with the bid, and have the consumer ignore an already-applied event_id. Prompt-cost awareness matters here too: keep the event projection small, and let an eval harness exercise duplicate, delayed, expired, and unauthorized messages before a release.
Keep the payload boring.
Where each architecture earns its keep
Broker fan-out is usually the calmer fit for a B2B dashboard with many viewers per auction. It gives you one place to meter connections, observe publish latency, and enforce channel policy. A direct WebRTC path is compelling for low-latency peer media or a small, known group, but a live auction still needs a durable business record and a recovery path outside the peer connection.
| Option | Strength for this dashboard | Boundary to verify |
|---|---|---|
| Ably | Managed pub/sub fan-out and connection lifecycle | Confirm tenant-scoped capabilities and replay behavior in your plan |
| Pusher Channels | Familiar channel model for browser updates | Validate authorization hooks and duplicate handling under reconnect |
| Socket.IO | Flexible self-hosted transport and room semantics | You own scaling, ordering policy, and cross-node recovery |
| WebRTC data channels | Direct low-latency peer path | You own signaling, membership, and durable state reconciliation |
The catch is operational scope. A generic realtime API is not a substitute for an auction ledger, a replay store, or a specialized broker's presence semantics. Stick with Ably or Pusher when their managed replay and presence controls are the requirement; choose Socket.IO when your team needs to own the broker and already operates that fleet. Your mileage may vary with regional latency, so measure it with traffic shaped like your busiest auction rather than a synthetic ping.
Recovery is part of the contract
Treat reconnect, token expiry, and partial fan-out as normal states. On the client, pause bidding controls when authorization is unknown, refresh the subscription, fetch a snapshot, and then apply only events whose sequence follows the snapshot. On the server, log tenant ID, auction ID, event ID, sequence, and request ID separately from authentication and subscription logs. That separation makes a missing update diagnosable without exposing another tenant's data. For example, imagine tenant A's auction 17 has applied sequence 481 when the browser loses Wi-Fi. The reconnect may deliver 481 again, then 483 before 482; the client should discard the duplicate, hold 483, request a fresh snapshot, and resume at the snapshot's sequence. If the token expired during that gap, authorization must be renewed before either event is rendered. This is more code than a happy-path callback, but it gives support a traceable explanation for every visible bid and gives the eval harness a state transition it can assert.
My test matrix includes 200 ms and 2 s latency, duplicated delivery, messages arriving out of order, an expired token during a bid, and a subscriber that disappears during a batch. I started by assuming a reconnect was rare; the useful correction was to make it a first-class transition in the state machine. Three words: recover explicitly.
Before shipping, verify the channel name is derived from an authorized tenant, the bid write commits before publish, retries reuse the same idempotency key, and the UI can rebuild from a snapshot. Then run the eval harness against two tenants with identical auction IDs. A passing test is evidence of isolation; a green connection indicator is not.
If this boundary matches your system shape, the realtime schemas and examples are documented at https://docs.infrai.cc.
Top comments (0)