Feature: Voice Channels #22
Labels
No labels
area:api
area:core
area:docs
area:infra
area:ux
dependencies
documentation
duplicate
good first issue
help wanted
invalid
question
rust
status:complete
status:partial
status:planned
type:bug
type:design
type:feature
type:infra
type:refactor
type:research
type:ux
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
icub3d/decentcom#22
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Migrated from GitHub issue icub3d/decentcom#22
Original Author: @icub3d
Original Date: 2026-04-15T14:15:48Z
Feature: Voice Channels
Overview
Voice channels allow users to join a real-time audio session within a channel using WebRTC. The server acts as a Selective Forwarding Unit (SFU), receiving each participant's audio stream and forwarding it to all other participants. This avoids the O(n^2) bandwidth cost of full-mesh peer-to-peer connections and is the standard architecture used by Discord, Teams, and similar platforms.
Background
The architecture doc (
docs/design/architecture.md) defines a Media / WebRTC SFU component as a first-class part of the server. The server-model doc (docs/design/server-model.md) specifies that voice channels are a feature-flagged capability (voice_channels, enabled by default) with configurable max participants, recording opt-in, and noise suppression requirements. The SFU strategy (build vs. integrate) is listed as an open question in the architecture doc. This feature assumes we integrate an existing SFU library rather than building one from scratch — specifically, using thewebrtcRust crate (pure-Rust WebRTC implementation) to build a minimal SFU within the server process.Depends on: all Phase 1 and Phase 2 features (especially
channels,gateway,roles,client-shell).Requirements
voice_channelsfeature flag — disabled servers return an errorDesign
API / Interface Changes
REST endpoints:
/api/v1/channelstype: "voice"and optionalmax_participants/api/v1/voice/{channel_id}/join/api/v1/voice/{channel_id}/leave/api/v1/voice/{channel_id}/state/api/v1/voice/{channel_id}/participantsWebSocket events (via gateway):
VOICE_STATE_UPDATEVOICE_OFFERVOICE_ANSWERVOICE_ICE_CANDIDATETauri IPC commands:
voice_joinvoice_leavevoice_mutevoice_deafenData Model Changes
New columns on
channelstable:channel_type"text"or"voice"(default"text")max_participantsNew table:
voice_states(ephemeral, could be in-memory only):channel_iduser_idsession_idmuteddeafenedjoined_atVoice state is ephemeral. On server restart, all voice states are cleared. This table may be kept entirely in memory rather than in SQLite/PostgreSQL.
Component Changes
Server (
server/):server/src/models/channel.rs— addchannel_typeandmax_participantsfieldsserver/src/sfu/— new module for the SFU implementationserver/src/sfu/mod.rs— SFU manager: tracks active voice sessions, routes mediaserver/src/sfu/session.rs— per-participant WebRTC session: SDP negotiation, ICE, media tracksserver/src/sfu/router.rs— media routing: receives audio from each participant, forwards to othersserver/src/routes/voice.rs— REST endpoints for join/leave/stateserver/src/gateway/events.rs— add voice event typesserver/src/gateway/handler.rs— handle voice signaling events over WebSocketserver/src/config.rs— addvoice_channelsfeature flag checkClient (
client/):client/src-tauri/src/commands/voice.rs— Tauri IPC commands for voice operationsclient/src/hooks/useVoice.ts— React hook managing WebRTC peer connection lifecycleclient/src/components/VoiceChannel.tsx— voice channel UI in channel list (shows participant count)client/src/components/VoiceControls.tsx— mute/deafen/disconnect buttonsclient/src/components/VoiceParticipants.tsx— participant list with mute/deafen indicatorsclient/src/stores/voiceStore.ts— Zustand store for voice connection stateDependencies:
webrtccrate (server-side WebRTC)Task List
Phase A: Server SFU Foundation
channel_typeandmax_participantscolumns to the channels schema and storage traitwebrtccrate dependency to the serverCargo.toml(deferred — SFU library choice unresolved)server/src/sfu/mod.rswith the SFU manager struct (deferred — SFU library choice unresolved)server/src/sfu/session.rs— WebRTC peer connection per participant (deferred)server/src/sfu/router.rs— media forwarding (deferred)VoiceStateMapinserver/src/voice/state.rs)Phase B: Server API & Signaling
server/src/voice/handlers.rswith join, leave, state, and participants endpointsVOICE_STATE_UPDATE,VOICE_OFFER,VOICE_ANSWER,VOICE_ICE_CANDIDATE)forward_sdp/forward_ice_candidate)voice_channelsis disabledREAD_MESSAGESpermission for the voice channelmax_participantslimit on joinPhase C: Client Integration
client/src/stores/voiceStore.ts— track connection state, current channel, mute/deafenclient/src/hooks/useVoice.ts— manage RTCPeerConnection lifecycle, handle SDP offer/answer exchange, ICE candidate exchange via gatewayclient/src-tauri/src/commands/voice.rs(if microphone permissions need native handling)client/src/components/VoiceChannel.tsx— voice channel entry in channel list showing participant avatars/countclient/src/components/VoiceControls.tsx— persistent bottom bar with mute, deafen, disconnect buttonsclient/src/components/VoiceParticipants.tsx— sidebar showing current participants with state indicatorsVOICE_STATE_UPDATEevents to update the participant list in real timeTest List
join_full_channel_returns_409)voice_channelsfeature flag is disabled (join_disabled_voice_channel_returns_403)join_text_channel_returns_400)update_mute_deafen_state,update_voice_state_when_not_joined_returns_404)multiple_users_join_same_channel)voice_channels_appear_in_channel_list)Open Questions
webrtccrate provides a pure-Rust WebRTC stack. Alternatively, we could run mediasoup or livekit as a sidecar process. The in-process approach is simpler to deploy but may be less mature. Decision needed before implementation begins.UI/Sidebar Consistency (Ref: #35)
This feature must align with the consolidated sidebar layout (#35):
UI/Sidebar Consistency (Ref: #35)
This feature must align with the consolidated sidebar layout (#35):