Commit Graph
11 Commits
Author SHA1 Message Date
Jannis Braun 024833c470 fix: security hardening and Safari stability
- Remove hardcoded JWT_SECRET fallback (crash on boot if unset)
- Make LiveKit config optional with 503 guard on token endpoint
- Add REST rate limiting via @fastify/rate-limit (auth 10/15m, messages 5/5s, uploads 10/1m, global 60/1m)
- Add WebSocket token bucket rate limiter (30 burst, 2/sec refill)
- Add DM channel ownership (ownerId) with migration, enforce on add-member
- Require friendship to add users to group DMs
- Add silent 20Hz oscillator to prevent Safari AudioContext suspension
- Move WebSocket heartbeat to Web Worker to bypass Safari background throttling
2026-02-24 04:34:36 +01:00
Jannis Braun 82eee04946 fix: client-side speaking detection replacing LiveKit's conservative server VAD
LiveKit's server-side VAD has a high, non-configurable threshold that misses
conversational speech. Replace it with a SpeakingDetector singleton that uses
Web Audio AnalyserNodes to read actual RMS levels per participant (50ms poll,
0.008 threshold, 250ms hysteresis hold).
2026-02-23 02:53:40 +01:00
Jannis Braun 176f4db27e chore: remove 62 tsc emit artifacts from src/, add noEmit to tsconfig
tsc was emitting compiled .js files directly into packages/web/src/
alongside the .tsx source files because noEmit was not set. These
artifacts were never used — Vite compiles from .tsx source directly.

- Add noEmit: true to packages/web/tsconfig.json (tsc = type-check only)
- Delete all 62 orphaned .js files from src/ (-6,129 lines)
- Add packages/web/src/**/*.js to .gitignore as safeguard
2026-02-23 00:54:51 +01:00
Jannis Braun dc57856506 fix: RNNoise mono-left-only audio via explicit ChannelMerger stereo upmix
RNNoise worklet outputs mono (1ch) which played only in the left ear.
Replace unreliable automatic up-mixing (channelCount/channelCountMode on
inputGain) with a ChannelMergerNode that duplicates the mono signal to
both L and R channels — guaranteed stereo by the Web Audio spec.
2026-02-23 00:37:50 +01:00
Jannis Braun ccf2001047 feat: RNNoise WASM AudioWorklet for ML-based noise suppression (Phase 3)
Inject @sapphi-red/web-noise-suppressor into AudioManager's input pipeline
as a toggleable AudioWorkletNode. The worklet is loaded lazily on first
enable, then kept alive — toggling bypasses by rewiring the graph without
destroying the WASM instance. Browser NS is forced off when RNNoise is
active to avoid double-processing. InputGain forced to stereo up-mix to
prevent mono-left-only output from the worklet. Also wires Phase 2 output
device routing (setSinkId) into AppLayout/ChannelSidebar.
2026-02-23 00:28:29 +01:00
Jannis Braun 708bdf5468 fix: auto-disable AEC during screen share to prevent Chrome voice ducking
Chrome's AEC uses getDisplayMedia audio as a reference signal and
aggressively ducks the microphone even when headphones are used.
This adds a screenShareActive flag to AudioManager that forces
echoCancellation off during screen share, plus Chromium-specific
goog* constraints as belt-and-suspenders.
2026-02-20 15:22:20 +01:00
Jannis Braun 25f72aef3b fix: address sender-side voice ducking with voice processing controls
Disable AGC by default to prevent Chrome from crushing mic sensitivity
when stream audio is playing. Add user-facing toggles for echo
cancellation, noise suppression, and auto gain control. Decouple voice
and stream audio by routing through ctx.destination instead of shared
compressor. Track mic stream generation to re-publish when settings change.
2026-02-20 15:00:08 +01:00
Jannis Braun 5c38f076d3 fix: eliminate voice ducking caused by rogue LiveKit audio elements
Add MutationObserver to neutralize LiveKit's re-attached <audio> elements,
mark our keep-alive elements with data-opencord, detach tracks on unsubscribe,
remove dangerous blanket .play(), and soften compressor to transparent limiter.
2026-02-20 07:03:41 +01:00
Jannis Braun 1aa40cca15 feat: implement Discord-like stream widget system with separate tiles
Streams now appear as separate tiles in the voice grid alongside the
user's camera/avatar tile, matching Discord's model. Each stream tile
has independent volume, mute, watch/unwatch controls, quality badges,
and stream attenuation that ducks audio when someone speaks.
2026-02-20 03:59:28 +01:00
Jannis Braun 9a79ddf7eb feat: implement global audio effects and presence grace period 2026-02-19 23:46:46 +01:00
Jannis Braun 95217f0343 Overhaul audio architecture for robust cross-browser reliability 2026-02-19 22:13:48 +01:00