Skip to content

Voice calls in DMs (1:1 / group) — allow voice join + add call signaling #32

Description

@krazyjakee

Summary

There is no way to make a voice call in a DM (1:1 or group DM). Voice is currently a space-only feature, and the server actively rejects voice on DM channels. This is the blocking dependency for DM calling in the Flutter client (DaccordProject/daccord).

Current behaviour

POST /channels/{id}/voice/join (src/routes/voice.rs) hard-fails before issuing LiveKit credentials:

if channel.channel_type != "voice" {
    return Err(AppError::BadRequest("channel_not_voice"));
}
let space_id = channel.space_id.as_deref()
    .ok_or_else(|| AppError::BadRequest("channel_has_no_space"))?;

A DM channel is type: "dm" / "group_dm" with space_id = NULL, so it fails both guards.

There is also no call signaling of any kind:

  • Gateway voice events (gateway/intents.rs) are presence-only: voice.state_update, voice.server_update, voice.signal.
  • voice.state_update is broadcast to the space, not to individual recipients — there is no path to notify DM participants.
  • No call / ring / incoming / accept / decline concept exists anywhere in the codebase.

What needs to happen (server)

  • Allow voice join on dm / group_dm channels — relax the channel_type != "voice" and space_id guards for DM channels, gated by require_dm_access() participant check instead of space permissions.
  • Issue LiveKit credentials for DM channels (room naming already keyed on channel_id in src/voice/livekit.rs, so channel_{dm_id} works).
  • Add call signaling so the other party knows: ring / incoming-call gateway event targeted at DM participants, plus accept / decline / cancel / timeout. Decide whether this is a new event family (e.g. call.ring, call.accept, call.decline, call.end) or an extension of the voice events.
  • Route DM voice voice.state_update events to DM participants rather than a space.
  • Update protocol docs (accordserver-mcp).

Context

Investigated alongside the client. The accordkit SDK's client.voice.join(channelId) is already channel-agnostic, so once the server allows it the SDK needs little/no change — but signaling is net-new on both sides.

Client-side tracking issue: DaccordProject/daccord (linked).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions