Voice configuration

Configure and troubleshoot server-relayed voice channels.

Voice configuration

Ceili voice uses a server-relay topology. Each participant creates one WebRTC peer connection with the chatserver’s Pion voice service. The server forwards media tracks to other members of the same voice channel; clients do not connect directly to one another.

Bootstrap and ICE

The client requests GET /voice/bootstrap before joining voice. The response contains the server peer ID and the configured ICE servers:

{
  "peer_id": "voice-sfu",
  "ice_servers": [
    {"urls":["stun:stun.l.google.com:19302"]}
  ]
}

Clients must target voice-sfu for offer, answer, and ICE signaling. Configure STUN and TURN through the variables in Server configuration. TURN is recommended for users on networks that block direct UDP traversal.

UDP and NAT

The voice service needs a reachable UDP path. When deploying behind NAT or in a container:

  1. Set CHATSERVER_VOICE_EXTERNAL_IP to the public address advertised for media when required.
  2. Set CHATSERVER_VOICE_UDP_PORT_MIN and CHATSERVER_VOICE_UDP_PORT_MAX to a bounded range.
  3. Publish the same UDP range through the host firewall, load balancer, or container runtime.
  4. Configure a TURN server for networks where direct ICE candidates cannot connect.

Keep the HTTP/WebSocket TCP port and voice UDP range distinct in firewall rules. Health checks do not verify that the media path is reachable.

Lifecycle

A client sends voice_join with a voice channel ID, then negotiates with voice-sfu. The server broadcasts voice_state and voice_members updates to the room. A client can occupy one voice channel at a time; joining another channel leaves the current channel first. voice_leave, WebSocket disconnect, and room switching tear down the server-side voice membership.

Voice activity events are room-scoped and include the voice channel, member ID, levels, active state, and timestamp.

Troubleshooting

The client connects but voice has no audio

Check that the server returns an ICE server list, the client targets voice-sfu, and the voice UDP range is reachable. Add TURN configuration when users are on restrictive networks.

Users can join but cannot hear one another

Confirm that each client is in the same room and voice channel. Check offer/answer and ICE state transitions without logging raw SDP or candidate values. The server should have a peer connection for each participant and must be able to renegotiate when another participant’s track arrives.

Voice fails only after deployment

Check the external IP, published UDP range, reverse proxy TLS configuration, and TURN credentials. ws:// is suitable for local development; public clients should use wss://.

Server logs are too sensitive

Do not record complete SDP, ICE candidates, TURN credentials, or private message content. Prefer payload lengths, peer IDs, room/channel IDs, and lifecycle states.

Current status

Voice relay and signaling are implemented through the Pion service. Authentication, moderation, client-side mesh voice, and extension hooks are not part of the current supported voice API. The separate userserver project is not required for voice setup.