Add 1:1 voice and video calling over the existing SAS-verified peer
connection. Audio and video tracks ride the same RTCPeerConnection as the
chat, bundled onto one DTLS-SRTP transport, so media inherits the session's
end-to-end encryption. SDP offer/answer is renegotiated in-band over the
verified data channel — no signalling server, so the media's DTLS
fingerprints are authenticated end-to-end. Calls are gated on a connected,
SAS-verified session.
Codecs & adaptation:
- Opus tuned for lossy links (in-band FEC, DTX, RED redundancy); audio is
bandwidth-prioritised and never throttled.
- VP9/AV1 single-encoding SVC with H.264/VP8 fallback; video degrades by
spatial/temporal layer.
- Runtime NetworkAdaptationController trims video bitrate on loss/RTT and
recovers as the link clears — no renegotiation. Live connection-quality
indicator (Excellent/Good/Fair/Weak) in the call UI.
In-call controls: mute, camera on/off (voice→video upgrade in-band),
camera flip, minimize-to-widget, hang up, and accept/decline for incoming
calls. Production logging disabled (DEBUG_MODE=false); temporary call
diagnostic logger removed. Codec rationale in docs/webrtc-config.md.
- config/ice-servers.prod.js: swap ExpressTURN for self-hosted coturn at
turn.securebit.chat (TURN udp/tcp on 3478, TURNS/TLS on 443). Long-lived
REST-API credential (expiry capped at int32 max for coturn compatibility).
- Add raw-IP STUN/TURN fallback (144.172.96.126): Safari's WebRTC layer fails
to resolve STUN/TURN hostnames on some networks and gathers no srflx/relay
candidates; reaching the server by IP fixes cross-browser (Safari<->Chrome)
connections. Harmless to other browsers.
- deploy/nginx.conf: never long-cache /config/ice-servers.js so clients don't
lock onto a stale server list.
- Bump version to 5.4.10 (header + init banner).
- Record voice notes in-browser, sent over the chunked AES-GCM file-transfer
channel (per-file session key + signed SHA-256 integrity).
- Captured as PCM and encoded to WAV for universal playback (incl. iOS/Safari);
auto-accepted and played inline from an in-memory blob, never written to disk.
- Composer mic button with live waveform + timer; desktop shows mic + send side
by side, mobile swaps mic to send when typing.
- CSP media-src now allows blob: so recorded/received audio can play.
- Roadmap: Desktop Edition -> 5.0, new 5.5 'Secure Voice & Calls', later
milestones shifted; version bumped to 5.4.5.
- Update README, docs (security/API/cryptography), and CHANGELOG.
- Remove send-path keyword blocklist that silently rejected legitimate
messages (e.g. "constructor", "global", "document.", literal "javascript:")
without adding protection. XSS is enforced at the rendering boundary by the
receive-side DOMPurify pass and by sanitizeMessage() before encryption.
- Preserve newlines/tabs/indentation in _sanitizeInputString; stop collapsing
all whitespace which destroyed multi-line messages and code snippets.
- Stop logging raw AAD (sessionId + keyFingerprint) on validation failure;
log length only, in both message and file-message AAD validators.
- Add Strict-Transport-Security (2y + preload) and Permissions-Policy
(camera=self for QR, rest denied) to nginx.conf and .htaccess.
- Add tests/outgoing-message-integrity.test.mjs regression suite.
- nginx: asset extensions use try_files $uri =404 so a missing file (e.g.
config/ice-servers.js) no longer serves index.html with the wrong content type
- add config/ice-servers.prod.js (public STUN, no secrets); Dockerfile copies it
to the git-ignored config/ice-servers.js so the operator-override path exists