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
- Live
- app.strug.ca ↗
- 01OBS / browser → MediaMTXRTMP with a private stream key, or WebRTC (WHIP)
- 02MediaMTX → HLSsegments served to viewers through a proxy
- 03Viewer → Watch pagehls.js playback, chat, badges
- 04Viewer → Walletgift sent: coins moved in one transaction
- 05Stream end → Azure BlobVOD archived with thumbnails and chat replay
- 06Sweeper → Sessionsorphaned streams closed and reused

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

The Strug super-app, app.strug.ca
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.