Threat model & controls
This page summarises MOQOM’s security posture for integrators and security reviewers. Report vulnerabilities to security@moqom.cloud — see Responsible disclosure.
Assets
Section titled “Assets”| Asset | Why it matters |
|---|---|
| Media content | Private calls, paid broadcasts, minors in age-gated rooms |
| Participant identity and IP addresses | Stalking and doxxing risk, especially for creators |
| API keys | A backend key administers a whole tenant |
| Client tokens | Admit a device to a room with specific rights |
| Moderation state and audit log | Safety decisions and appeals depend on them |
| Prepaid credit | Direct financial value |
Threats and controls
Section titled “Threats and controls”| Threat | Controls |
|---|---|
| Eavesdropping on media | TLS 1.3 on every QUIC connection. SFrame E2EE is required by default: the SDK will not publish media without a key and drops unencrypted media it receives, so relays carry only ciphertext. Only a backend can turn it off, per room, with a reason that is written to the audit log. |
| Silently weakening a room | A room’s encryption policy is carried in the signed client token (e2ee claim); a token without the claim is treated as required. Turning encryption off needs actor.reason and is recorded as a policy_change in the moderation log. |
| Tampering with media in transit | QUIC/TLS integrity; with SFrame, altering an object’s group, number, priority or properties fails authentication. |
| Stolen client token | Lifetimes ≤ 15 minutes; tokens are bound to a key generated on the device; scopes limited to one tenant, room and set of grants; checked on every request. |
| Leaked API key | mqk_ prefix for secret scanning; secrets stored only as hashes; revoke instantly with DELETE /v1/keys/{id}; separate relay keys that can only report usage. |
| Privilege escalation via minting | A service account can never mint a token more powerful than its own ceiling; ownership is not grantable; the device-facing join endpoint never mints moderation or ownership. |
| Moderator abuse | Capabilities are sets with a strict-superset rule; owners are immune; every action is attributed (service_account, on_behalf_of) in an append-only audit log; reading the log is a separate capability. |
| Evading a ban | Bans by account, device or address; checked on every subscribe and publish; a ban ends the live session, not only the next join. |
| Replayed moderation from a retrying queue | Idempotency keys on every mutating call. |
| Hidden observers | Recorders and classifiers must be attached as watchdogs, which are disclosed to the room; rooms can prohibit recording entirely. |
| Cross-tenant access | Every call names a tenant and must match the credential’s; resources in other tenants return NOT_FOUND, so existence cannot be probed. |
| Credential probing | Authentication failures never say which part of a credential was wrong; comparisons are constant-time. |
| Abuse of public endpoints | Signups limited per address; the join endpoint is rate-limited per credential and body-size capped. |
| IP exposure between peers | Media goes through relays by default, so participants never learn each other’s addresses. The planned peer-to-peer mode is opt-in, E2EE-only, and never offered to audiences. |
| Denial of wallet | Prepaid credit: spend is bounded by your balance; auto top-up only runs one charge at a time. |
| Malformed protocol input | Every decoder is fuzzed continuously; strict body parsing rejects unknown fields. |
Authentication on every track
Section titled “Authentication on every track”- Tokens refresh automatically. Give
DeviceCredentialsarenewclosure that asks your backend for a new token. Two minutes before the current one expires, the SDK fetches the next and presents it to the relay inside the open session, without reconnecting, retrying every 15 seconds if your backend is briefly unreachable. Without arenewclosure the session ends when its token does. - Every stream carries auth. The relay checks every announce, subscribe and fetch against the session’s token: tenant, room, user, device and grants. Media and data tracks use the same namespaces, so chat, game state and telemetry are authorized exactly like audio and video.
- Renewal can’t escalate. A renewed token must name the same holder and the same permissions. Promotions and room moves arrive as control-plane directives, not as a swapped token.
Things to know
Section titled “Things to know”- The relay admin port is unauthenticated and off by default. If you self-host, bind it to a private interface only.
- E2EE key management today is a shared room key you distribute yourself. MLS-based group keys
are designed but not yet built. Until your app calls
useEncryption, a room that requires encryption will not let it publish. - Server-side features vs E2EE. In end-to-end encrypted rooms, server-side recording and
classification cannot run, because the servers never see plaintext; on-device taps still work. A room that needs them has
to be set to
e2eeoptional by your backend — see Turning it off for a room.
Your responsibilities
Section titled “Your responsibilities”- Authenticate your users before minting tokens. The join endpoint is not authentication.
- Keep backend keys on servers. Never ship one in an app.
- Give each self-hosted relay its own relay key and rotate on staff or host changes.
- Pass
on_behalf_ofon moderation calls so the audit trail names the human. - Disclose any media you tap and send off-device; attach a watchdog to do it.
- Perform age verification yourself before asserting
age_verified.
Data residency
Section titled “Data residency”Each room has a residency_zone, fixed at creation, that governs where the room’s data —
including recordings, inference outputs and moderation evidence — may live.