On this page
Why generic “live audio” advice failed us
A masjid Azan is not a podcast and not a Zoom call. It is a short, time-critical broadcast that must:
- Start within seconds of the Muezzin tapping Go — members are already walking to wudu.
- Wake phones that were locked, often on 4G, often with aggressive OEM battery savers.
- Prevent a random member from accidentally becoming the microphone.
- Cost a flat amount even if 400 people join Maghrib on Friday.
Per-minute vendor billing made Friday Khutbah unpredictable. We moved media to a self-hosted server and kept business logic in our API. The app only receives a short-lived token. It never holds the media secret.
The broadcast path we actually run
- The Muezzin or Imam, already role-checked on the API, starts a session of type Azan or Qutbah.
- The API creates a unique live-audio room for that session only — Azan and Khutbah stay separate.
- A publisher token is minted so only that Imam or Muezzin can use the microphone.
- A push notification wakes subscribed members of that masjid only — not the entire app install base.
- Each member’s app requests a listen-only token and joins the same room.
Publisher vs subscriber tokens
This split is the entire security model. If we issued one “room token” to everyone, a compromised member device could shout over the Azan.
| Grant | Publisher (Imam / Muezzin) | Subscriber (member) |
|---|---|---|
| Join room | Yes | Yes |
| Publish microphone | Yes | No |
| Subscribe to audio | Yes (monitor own send) | Yes |
| Publish data channel | No | No |
| Token lifetime | Short-lived (covers a long Khutbah) | Short-lived |
Tokens are minted on the server. The media API secret never sits in the Flutter binary or in Git.
What breaks on some mobile networks
WebRTC prefers UDP. Some mobile networks and office Wi-Fi drop or throttle UDP. Members would see “connecting…” while the Muezzin was already reciting.
- We keep a TLS-over-TCP fallback so audio still flows when UDP is blocked.
- The live-audio server stays warm. A cold start in the middle of Maghrib is worse than paying for a small always-on VM.
- Signaling uses a stable hostname. If we move regions, that hostname must not change, or every installed app would break.
Duration caps that match worship, not Zoom
Generic conferencing defaults (hours-long rooms) are dangerous in a masjid product. A forgotten “End” button would keep a microphone live in an empty hall.
- Azan is short. Five minutes is generous for a recitation plus a late join.
- Khutbah and Bayaan need Jumu’ah length. Ninety minutes covers a long bayaan without looking like an overnight radio station.
- The API can still close the room immediately when the Imam taps End. If the media server already auto-closed it, we treat that as success.
What we would not do again
- Do not bill live worship per minute. Friday spikes punish the masjid that is doing the most good.
- Do not let the client decide whether it is a publisher. Role checks belong on the API.
- Do not use listener counts from the chat socket. Use the media server’s participant list.
- Do not skip a TCP fallback if your users are on networks that block UDP.
Related reading: cross-platform architecture and roles · product feature list.