Commit Graph
17 Commits
Author SHA1 Message Date
jevb 795ed48ec9 docs: v1.0.0 release prep — version bump, license, README overhaul
- Bump version to 1.0.0 across package.json, Cargo.toml, tauri.conf.json
- Add AGPL-3.0 LICENSE file
- Update README with missing features (2FA, DMs, video grid, stream preview,
  theming, auto-login, account deletion, observability)
- Remove internal Copilot Assets section from README
- Remove soundboard reference (not implemented)
- Add account deletion and video grid docs to CLAUDE.md
2026-03-30 20:54:05 +02:00
jevbandclaude-flow a0731cf030 fix: voice audio pipeline — autoplay unlock and GainNode-based VAD
- Add --autoplay-policy=no-user-gesture-required to WebView2 config
  so remote participants' audio plays immediately on join (desktop app
  doesn't need browser autoplay restrictions)
- Add AudioPlaybackStatusChanged handler with click-to-unlock fallback
  for browsers that still block autoplay
- Replace broken VAD implementation that used setMicrophoneEnabled/
  mediaStreamTrack.enabled (both fought LiveKit's track lifecycle) with
  a unified GainNode audio pipeline:
    rawMic → AnalyserNode (VAD) → GainNode (volume × gate) → sender
- VAD now gates by setting gain=0 instead of touching the track —
  analyser always sees real audio, no stale track references
- Merge input volume and VAD into single pipeline (always active)
- Add voice settings UI: draggable sensitivity threshold on mic meter,
  input/output volume sliders (0-200%), audio processing toggles

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-03-25 19:07:52 +01:00
jevb e03456527b fix: resolve LiveKit voice issues — duplicate audio, tunnel effect with 5+ users
- Detach existing audio elements before attaching to prevent double playback on reconnects
- Remove webAudioMix to eliminate Web Audio overhead compounding with multiple participants
- Use participant.setVolume() for full 0-200% per-user volume range
- Add pli_throttle and active_loopback_prevention to LiveKit server config
- Bump version to 1.3.0
2026-03-22 21:10:02 +01:00
jevb 3236918012 refactor: server hardening + client decomposition + protocol resilience
Server:
- Split monolithic voice_handlers.go into voice_join/leave/controls/broadcast
- Add metrics endpoint (admin-IP-restricted /api/v1/metrics)
- Add orphaned attachment cleanup in maintenance loop
- Add sentinel errors (db/errors.go, ws/errors.go)
- Add ring buffer for event replay on reconnect
- Add heartbeat monitoring with stale connection sweep
- Improve hub with panic recovery, graceful shutdown, seq tracking
- Typed message structs replace raw map[string]interface{}

Client:
- Decompose MainPage into ChatArea + SidebarArea controllers
- Add disposable.ts lifecycle management pattern
- Add member list right-click context menu (kick/ban/role)
- Tighten CSP (media-src, font-src, object-src, base-uri)
- Improve store with shallowEqual, 500-msg cap, batch updates
- Add search API endpoint wiring
- Fix LiveKit session cleanup and reconnection

Docs:
- Add CODEMAPS for architecture, backend, frontend, data, deps
- Add protocol-schema.json (machine-readable, 36 message types)
- Add platform research report
- Update PROTOCOL.md with seq/replay fields
2026-03-21 10:08:44 +01:00
jevb 2ac8e886a9 fix: camera button delay and video feed flickering + security hardening
Camera fixes:
- Optimistic setLocalCamera(true) before async setCameraEnabled for instant
  button highlight, with revert on failure
- Only call checkVideoMode when camera-relevant state changes, not on every
  speaking poll tick (100ms)
- VideoGrid.addStream updates existing cells in place instead of
  destroy+recreate to prevent black frame flashes
- VideoModeController tracks localTileAdded to avoid redundant addStream calls

Security & hardening (from prior session review):
- Settings store key allowlist prevents arbitrary key writes
- Certificate fingerprint validates SHA-256 colon-hex format
- CredentialData Debug impl redacts token and password
- LiveKit config file written with 0600 permissions
- Token TTL reduced from 24h to 4h
- Null-check on client.user before token generation
- Thread-safe getChannelID/trySendMsg helpers on Client
- Warn on default dev LiveKit credentials
- Devtools feature-gated behind cfg(feature = "devtools")
- Updater uses configure_client for self-signed cert acceptance
- Clamp voice sensitivity input to 0-100 range
- Clear lastConnectToken/Host on logout
- Fix animation frame leak in VoiceAudioTab mic meter
2026-03-20 12:30:12 +01:00
jevb 3f642a77e0 fix: speaking ring, devtools, CSP, and connection fixes
Speaking detection:
- Replace unreliable ActiveSpeakersChanged with per-participant
  IsSpeakingChanged events (fires locally, more responsive)
- Wire speaking detection on local + remote participants
- setSpeakers updates all users including local (LiveKit is sole
  authority, no local VAD)

DevTools:
- Add F12/Ctrl+Shift+I shortcut to open WebView2 DevTools
- Enable devtools feature in Tauri release builds
- Add open_devtools Rust command

CSP:
- Allow http://ipc.localhost for Tauri IPC protocol
- Allow http/ws://localhost:* for LiveKit signal connection

Connection:
- Fix LiveKit API secret minimum 32 chars
- Remove TURN TLS config that crashed LiveKit server
- Add reverse proxy at /livekit/* (kept for future use)
2026-03-20 07:11:45 +01:00
jevb 682e6cbae9 fix: resolve LiveKit connection issues
- Add http://localhost:* and ws://localhost:* to CSP connect-src
  (WebView2 was blocking LiveKit signal connection)
- Add connection retry (3 attempts, 2s delay) for LiveKit server
  startup race condition ("could not find any available nodes")
- Remove broken TURN TLS config from generated livekit.yaml
- Send direct LiveKit URL instead of proxy path (localhost is
  treated as secure context in Chromium/WebView2)
- Set LiveKit server host from API config for URL resolution
2026-03-20 06:27:43 +01:00
jevb 7ebf2faecd chore: bump version to 1.2.0 for LiveKit migration 2026-03-20 05:07:37 +01:00
jevb f3734bf827 fix: voice rejoin failure, SDP race, deafen bypass + add server voice logging
- Fix SDP signaling race condition: add per-client negoMu to serialize
  renegotiateParticipant / handleVoiceOffer / handleVoiceAnswer so
  concurrent OnTrack goroutines don't race through rollback
- Fix handleVoiceLeave triple-fire: early return when clearVoice()
  returns zeros so ICE callbacks don't re-enter and corrupt state
- Fix SQLite SQLITE_BUSY errors: add busy_timeout=5000 pragma and
  SetMaxOpenConns(1) for file-based databases
- Fix deafen bypass: new remote audio elements now respect localDeafened
  state so late-arriving streams are muted immediately
- Add debug-level logging for SDP negotiation, track fan-out, ICE
  candidates, voice state changes, room lifecycle, and participant
  add/remove
- Bump version to 1.1.1
2026-03-19 21:35:18 +01:00
jevb ffab36a291 chore: update Tauri updater public key for v1.1.0 signing keypair 2026-03-19 19:22:23 +01:00
jevb c784d7950c chore: bump version to 1.1.0
Video chat, GIF picker, push-to-talk, desktop notifications,
compact mode, and admin IP restriction.
2026-03-19 18:44:29 +01:00
jevb 86c218211c fix: use v1Compatible for createUpdaterArtifacts (v2Compatible not valid) 2026-03-19 07:41:58 +01:00
jevb 08f3623777 fix: enable Tauri updater artifacts and bump client to v1.0.0
- Add createUpdaterArtifacts: "v2Compatible" to bundle config
  so CI generates .nsis.zip + .nsis.zip.sig for auto-updates
- Bump client version from 0.1.0 to 1.0.0
2026-03-19 07:33:59 +01:00
jevb 5311d0a7e3 feat: fix voice chat over NAT, audio pipeline, and add comprehensive debugging
Voice was broken over NAT due to multiple issues across the audio pipeline:

- Fix GainNode silence: WebView2 silences remote WebRTC streams routed through
  Web Audio createMediaStreamSource→GainNode→createMediaStreamDestination.
  Replaced with direct HTMLAudioElement playback for remote audio.
- Fix NAT traversal: Add Google public STUN server (stun.l.google.com:19302)
  so remote clients can discover their public IP for ICE connectivity.
- Fix signaling race: Catch createOffer InvalidStateError when server
  renegotiation offer arrives before client's initial offer is sent.
- Fix device switch: Use replaceTrack() instead of removeTrack+addTrack
  to avoid SDP renegotiation. Safe rollback on failure (stop old last).
- Fix speaking flicker: setSpeakers skips local user (VAD is sole authority).
- Fix VAD sample rate: Force 48kHz AudioContext instead of system default
  (192kHz) which spread FFT bins too wide for voice frequency detection.
- Fix CSP for WASM: Add wasm-unsafe-eval to script-src for RNNoise.
- Fix clearAuth leak: leaveVoice() called before resetVoiceStore().
- Fix ICE rate limit: Separate limit for ICE candidates (50/s vs 20/s).
- Fix stale ICE errors: Silently drop voice_ice with no PeerConnection.
- Fix audio play() race: Deferred to queueMicrotask after DOM attachment.

Debugging infrastructure:
- Logs tab: Copy All button, Voice Diagnostics panel with live session
  state, Probe Audio Levels (measures actual signal at 3 pipeline points),
  Test Direct Playback button, Copy Diagnostics button.
- Client logging: WebRTC (PeerConnection lifecycle, ICE candidates with
  type/address, track events, negotiation), VAD (start/threshold/destroy),
  Audio (device acquisition with settings, device changes), noise suppression
  (WASM load timing, worklet vs fallback path), voiceSession (remote stream
  parsing failures, deafen state, audio element playback events).
- Server logging: SFU init config, voice room mode transitions/track
  lifecycle/close, RTP forwarding with packet counts and first-packet
  detection, 5s no-packet warning, track fan-out counts, subscriber
  transceiver state, ICE candidate details, voice credentials issued.
- Logger: Error objects now serialize .message and .stack instead of {}.

Per-user volume right-click now works on voice user rows in sidebar.
RNNoise ML noise suppression with AudioWorklet + ScriptProcessor fallback.
Tests: 7 new test cases (replaceTrack, setSpeakers skip-local, clearAuth).
2026-03-18 23:02:06 +01:00
jevb 01e4d4bec3 feat: client auto-update with Ed25519 signing and dynamic server URL
- Add tauri-plugin-updater and tauri-plugin-process for in-app updates
- Rust commands (check_client_update, download_and_install_update) build
  updater with dynamic endpoint at runtime for self-hosted compatibility
- Server endpoint GET /api/v1/client-update/{target}/{version} translates
  GitHub Releases into Tauri updater JSON format with .sig content
- UpdateNotifier banner component with install/dismiss controls
- CI workflow produces signed .nsis.zip + .sig updater artifacts
- Self-signed TLS support via dangerousAcceptInvalidCerts config
2026-03-18 17:47:59 +01:00
jevb 13be0fd6d3 feat: file uploads, URL previews, emoji search, voice mute fixes, UX improvements
Server:
- Add POST /api/v1/uploads and GET /api/v1/files/{id} endpoints
- Add CreateAttachment DB method for file upload records
- Allow empty message content when attachments are present
- CORS headers on file serving for WebView2 compatibility

Client — File uploads & attachments:
- Clipboard paste (Ctrl+V) and attach button (+) for file uploads
- Preview bar above input with thumbnail, spinner, and remove button
- Images fetched via Tauri HTTP plugin as base64 data URIs (bypasses
  self-signed cert rejection in WebView2)
- Three-layer image cache: memory → IndexedDB → network
- In-flight deduplication prevents duplicate concurrent fetches
- Image lightbox with click-to-zoom, scroll wheel zoom, pan, keyboard shortcuts

Client — URL previews & embeds:
- URLs in messages rendered as clickable links
- YouTube embeds with thumbnail, play button, video title via oEmbed API
- Generic link previews with OG metadata (title, description, image)
- Fetched via Tauri HTTP plugin with Facebook crawler User-Agent
- YouTube title cache and OG metadata cache prevent re-fetch on re-render
- Links open in default browser via tauri-plugin-opener

Client — Voice & audio fixes:
- Mute uses replaceTrack(null) for reliable RTP-level muting in WebView2
- Deafen also mutes mic; undeafen/unmute unmutes both
- Muted users show crossed mic icon, deafened show crossed mic + headphone
- Re-apply mute state after input device switch

Client — UX improvements:
- Disable browser context menu globally (only custom menus show)
- Emoji search now matches by keyword names (smile, heart, fire, etc.)
- Emoji picker closes on click outside
- User bar status text moved below username
- Messages sorted chronologically (oldest first, newest at bottom)
- Scroll to bottom on initial load with deferred retries for layout shifts
- Image attachments constrained to 400x350px with click-to-lightbox
2026-03-18 14:13:52 +01:00
jevb 77626e136b feat: add Tauri v2 desktop client with full chat UI and security hardening
Complete Tauri v2 client implementation migrated from WPF/.NET 8:
- Rust backend: WS proxy with TLS cert bypass for self-signed servers,
  settings storage, system tray, global hotkeys
- TypeScript frontend: login/register, chat messaging, channel sidebar,
  member list, voice channel UI, settings overlay with log viewer,
  server profiles, quick switcher, emoji picker, file uploads
- 21 test suites (364 tests) covering stores, services, and components
- Security: bounded WS channel, wss:// URL validation, TLS signature
  verification, profile import validation, token redaction, HTTPS-only
  HTTP scope

Also updates CLAUDE.md to correct API path rule (/api/v1/) and adds
Tauri client CI workflow.
2026-03-15 19:44:02 +01:00