Social LiveDocumentationPLATFORM DOCS

System Architecture

How the whole system fits together -the pieces, how they talk, and the rules that keep money safe.

Last updated Thu Jul 16 2026 00:00:00 GMT+0000 (Coordinated Universal Time)

TL;DR -Social Live is one backend server plus a mobile app, an admin website, and a couple of helper sites, all running on a single machine behind a web server. The backend is the boss: it holds the database, moves all the money, and relays live video. Apple and Google -not us -are the final word on whether a purchase really happened.

Who this is for -Executive, Product & Business: read "The big picture" for a plain mental model. Engineering, Backend, DevOps: keep going for the topology diagram, the three request paths, and the design invariants.

The big picture

Think of Social Live as a live-streaming app with a built-in economy. People open the mobile app, watch and host live rooms, chat, send virtual gifts, and buy and cash out virtual currency. Everything they do is coordinated by a single backend -the central program that stores data, enforces the rules, and connects people to each other in real time.

Around that backend sit a few smaller pieces:

  • The mobile app (built with Flutter) is what users actually touch.
  • The admin portal is an internal website staff use to review reports, users, and cash-outs. It never touches the database directly -it always asks the backend.
  • A couple of helper sites: a live-room simulator for testing, and this docs portal you're reading now.

Two ideas are worth holding onto before the technical detail:

  1. The backend is the single source of truth. The app on your phone never decides how many coins you have. It asks the backend, and the backend's answer wins.
  2. Real money lives outside our walls. When someone buys coins, the purchase is verified with Apple or Google before we hand anything over. They are the authority on payments; we check with them every time.

Production topology

Everything runs on a single server (a VPS -a rented virtual machine) behind nginx, a web server that handles secure connections and routes each incoming request to the right internal service. The whole set of services is started and wired together by Docker Compose (docker-compose.prod.yml), a tool that runs each piece in its own container.

The diagram below shows who talks to whom. Boxes are running services; arrows are connections, labelled with the port number or protocol they use.

Rendering diagram…

Key points:

  • One backend container serves REST (/api/*), Socket.IO gateways, and mediasoup media. WebRTC media bypasses nginx: UDP ports 40000–40100 are published straight from the container (SFU_ANNOUNCED_IP tells clients the public address).
  • The admin portal never talks to PostgreSQL. It proxies everything through the backend's /api/admin/* endpoints with an admin token.
  • External money truth lives outside. Apple and Google are the source of truth for purchases; the backend verifies against their APIs before any coins move, and they push refund notifications back in.

The three request paths

The app and backend talk over three separate channels, each suited to a different job. In plain terms: one for ordinary requests (like loading your profile), one for live real-time updates (like a gift animation appearing), and one for the actual audio and video of a live stream.

1. REST (request/response)

The everyday path: the app asks a question, the backend answers. Used for all the normal stuff -logging in, loading profiles, checking your wallet, browsing rooms.

Flutter ApiClient → nginx → NestJS controller → service → Prisma → PostgreSQL

All CRUD, auth, wallet, and discovery traffic. JSON over HTTPS with a JWT bearer token. A global ValidationPipe (whitelist + forbid-non-whitelisted) rejects unknown fields at the boundary.

2. Realtime (Socket.IO)

A always-on connection that lets the backend push updates the instant they happen, without the app having to keep asking. Socket.IO is the library that keeps this live channel open. Long-lived WebSocket per app session. Gateways:

  • Live gateway -room join/leave, viewer counts, stage/guest flow, gift broadcast events, PK battle updates.
  • Chat gateway -message delivery, typing indicators, read receipts.

See Realtime for event catalogs.

3. Media (WebRTC + mediasoup)

The heavy path: the actual live audio and video. WebRTC is the browser/phone technology for real-time media, and mediasoup is the media server on our side that relays one broadcaster's stream out to many viewers.

Offer/answer and transport negotiation ride over Socket.IO signaling; actual audio/video flows peer-to-SFU over UDP. See Media & SFU.

Design invariants worth knowing

These are the non-negotiable rules the system is built around. If you remember nothing else technical, remember these -most bugs that touch money come from breaking one of them.

  • Server is the money authority. Clients never decide balances; every coin/diamond movement is a LedgerTransaction row created inside a Prisma $transaction with an idempotency key.
  • Idempotency everywhere money moves. "Idempotent" means doing the same operation twice has the same effect as doing it once. Gift sends, coin purchases, and refunds all carry unique keys so retries and replays can never double-apply.
  • Lazy settlement. Pending diamonds mature into withdrawable balance on read (no cron), always inside a transaction.
  • Dev-shaped fallbacks. Several client repositories fall back to local fixture data when the API is unreachable, keeping the app demoable offline. Production debugging should always check the backend logs, not just what the app displays.