Realtime (Socket.IO)
The always-on channel that powers live rooms and chat, and the rules that keep it consistent.
Last updated Thu Jul 16 2026 00:00:00 GMT+0000 (Coordinated Universal Time)
TL;DR -Alongside ordinary requests, the backend keeps a live connection open to each app so it can push updates the moment they happen — a viewer joining, a gift animation, a new chat message. Money always moves over the ordinary request path first; the live channel only announces what already happened.
Who this is for -Product & Business: read "What realtime is for" for the concept. Engineering & Backend: the two gateways, the fan-out model, and the ordering/delivery rules.
What realtime is for
Some things can't wait for the app to ask again. When a gift lands or a viewer joins, everyone in the room should see it instantly. To make that possible, the app opens one always-on connection to the backend and leaves it open for the whole session. The backend can then push events down that pipe the instant they occur.
The technology behind this is Socket.IO, a library for two-way,
real-time messaging over a WebSocket (a persistent connection, unlike a
normal request that opens, answers, and closes). The Flutter app keeps one
socket per session (lib/modules/signaling/), authenticated with the same
JWT login token it uses for ordinary requests.
The backend organizes this real-time work into gateways -the socket equivalent of the controllers used for ordinary requests. There are two.
Gateways
LiveGateway (backend/src/modules/live/live.gateway.ts)
Handles everything that happens inside a live room in real time:
- Presence -join/leave events maintain the room's viewer and
participant counts; the service reconciles
viewerCountwith the actual gateway audience so counts cannot drift. Eachviewer_joined/viewer_leftevent carries an authoritativeaudienceCount(the deduped live audience computed after the socket (un)registered); clients display that value verbatim rather than applying local +/-1 deltas, which previously drifted when events were missed or arrived out of order. - Stage management -guest requests, host approval, guest media choices (audio-only vs audio+video), stage position updates.
- Gift broadcast -after the REST gift-send succeeds (money moves over REST, never over the socket), the gift event is fanned out to the room so every viewer sees the animation.
- PK battles -battle start/score/end updates.
- Producer resync -when a host or guest publishes a new media track, other clients are told to consume it; clients skip producers they own.
- Actor names -the JWT carries only the user id, so the gateway
resolves the sender's profile
displayNameonce per socket (cached on the socket) and stamps it on chat, reactions, stage requests, and gifts. Anonymous sockets fall back to "Viewer".
ChatGateway (backend/src/modules/chat/chat.gateway.ts)
- Message delivery into conversation rooms.
- Typing indicators (
chat:typing_started/ stopped). - Read receipts.
Room naming & fan-out
"Fan-out" means delivering one event to many connected clients at once. To keep that efficient, the backend groups sockets into rooms -one per live room or conversation -and only sends an event to the sockets that are actually in it.
Socket.IO rooms are keyed by domain id (live room id, conversation id). A client joins the socket room when it enters the screen and leaves when it navigates away, so fan-out cost tracks actual audience.
Ordering and delivery rules
Live connections are inherently unreliable -sockets drop, events arrive late or out of order. These rules make sure that never corrupts real state, especially anything involving money.
- Money over REST, events over sockets. A gift is charged via
POST /wallet/gifts/send(idempotent, transactional) and only then announced via the gateway. If the socket drops, the money is still correct and the UI catches up on next fetch. - Reconnection -socket_io_client auto-reconnects; on reconnect the client re-joins its rooms and re-syncs state via REST (single source of truth), so missed events cannot corrupt state.
- Diagnostics -with
ENABLE_LIVE_DIAGNOSTICS=true, room lifecycle events are persisted toLiveRoomEventLogand surfaced in the admin diagnostics page. Payloads are sanitized before storage.
The QA simulator
Testing real-time behavior on real phones is slow, so there's a stand-in.
tools/live-room-simulator/ is a Next.js console that speaks the same
Socket.IO + mediasoup protocol as the mobile app. Use it to exercise
signaling, publish test media, and watch viewer consumption without any
device -invaluable for regression-testing gateway changes.
Related pages
- Media & SFU -how the actual audio/video flows once signaling over this channel sets it up.
- System Architecture -where realtime fits among the three request paths.
- Backend Module Map -the
liveandchatmodules that own these gateways.