Ahmed Reyyan
← All work

04 — Live streaming and calls in a super-app

Strug

Strug is a Canadian super-app: social feeds, videos, live streaming, rideshare, food, a shop and bookings in one product, built by a large team. Over ten weeks I built most of the viewer and creator side of its live streaming, plus voice calls, security fixes and the plumbing that keeps it running across replicas.

Role
Full-stack engineer, live streaming
Period
Jul – Sep 2026
Stack
Node.js, Express, Prisma, PostgreSQL, Socket.IO, Redis, MediaMTX, WebRTC / WHIP, HLS, React 19, Azure
A stream, from camera to viewer
  1. 01OBS / browser → MediaMTXRTMP with a private stream key, or WebRTC (WHIP)
  2. 02MediaMTX → HLSsegments served to viewers through a proxy
  3. 03Viewer → Watch pagehls.js playback, chat, badges
  4. 04Viewer → Walletgift sent: coins moved in one transaction
  5. 05Stream end → Azure BlobVOD archived with thumbnails and chat replay
  6. 06Sweeper → Sessionsorphaned streams closed and reused
Live browse · VOD · clips · subscriptions · Stream Manager
Strug Live: browse, categories and Go Live, the area I built

Strug Live: browse, categories and Go Live, the area I built

The Strug super-app, app.strug.ca

The Strug super-app, app.strug.ca

83
commits across backend and frontend in ten weeks
24 / 33
commits in the live-streaming frontend are mine
500
simultaneous voice calls in the load test, 0 errors

Live streaming

Streamers go live from OBS over RTMP, and the media server turns that into HLS for viewers; that ingest pipeline existed when I joined. I built the product on top of it: category and VOD browsing, the watch page with hls.js playback through an HLS proxy, past broadcasts stored in Azure Blob with view counts, clips, chat replay, thumbnails and a Stream Manager dashboard. Stream keys are never returned on public routes, and orphaned sessions are swept and reused.

I also added one-tap go-live from the browser: the camera or screen is published to MediaMTX over WebRTC (WHIP) and reaches viewers as HLS, with no OBS needed. Both sides have unit tests.

Money and community

Coin wallets, gifts and a gift leaderboard, subscriptions and follows, on new Prisma models with wallet changes inside database transactions. Chat badges for subscribers, moderators and streamers, bans and timeouts, schedules and community posts.

Voice calls

Self-hosted one-to-one calls over WebRTC: Socket.IO signalling with a Redis adapter so callers on different replicas reach each other, a coturn relay with short-lived HMAC credentials, and a call state machine in Postgres. In my load test it held 500 simultaneous calls (1,000 sockets) across two replicas with zero errors, and two-browser tests passed real audio both ways. It lives on its own branch and has not yet been tried on real phones and networks.

Hardening the platform

Authorisation fixes across food, courier, payments, lessons and AI chat, with integration tests; startup checks for missing secrets; a rate limiter that keeps working without Redis. Video uploads that hold up under load: the server pushes back when the processing queue is full, per-user limits, chunked Azure uploads with real progress, and a 50-account stress test. Health probes, crash reporting, correlated logs, cron jobs that run once across replicas, bounded list endpoints, and ESLint in CI for both repositories.

NextSportsfixtures