Skip to main content

Calls — Requirements

Overview

SAPOT supports peer-to-peer voice and video calls using WebRTC. The server relays only signalling messages (SDP offer/answer, ICE candidates) — media streams flow directly between devices on the LAN.


User Stories

IDAs a…I want to…So that…
CA-01userinitiate a voice call with another user on the LANI can speak with them directly
CA-02userinitiate a video call with another user on the LANI can see and speak with them directly
CA-03useraccept or decline an incoming callI control whether I answer
CA-04userend a call at any timeI can leave the conversation when I'm done
CA-05usertoggle my microphone on or off during a callI control what I transmit
CA-06usertoggle my camera on or off during a video callI control what I transmit
CA-07userswitch audio output between earpiece, speakerphone, and Bluetooth headsetI can use the call in different situations
CA-08userhave call records (who called whom, when, duration) storedI can see my call history
CA-09userbe notified of an incoming call even when the app is in the backgroundI don't miss calls while multitasking

Functional Requirements

FR-CA-01 — Call initiation

  • Caller sends { type: "offer", to: <peer_id>, sdp: ... } to the server via WebSocket.
  • Server relays the offer to the target peer's WebSocket connection.
  • Callee responds with { type: "answer", to: <caller_id>, sdp: ... }.
  • Both peers exchange ICE candidates via { type: "ice-candidate", to: <peer_id>, candidate: ... }.
  • Once signalling completes, media flows directly between devices (see ADR 0004).

FR-CA-02 — Call record

  • A call record is created when a call is initiated:
    • conversation_id — the conversation this call belongs to.
    • initiator_id — the user who started the call.
    • typevoice or video.
    • statusringing, ongoing, ended, or missed.
  • callparticipant records track who joined, with joined_at and left_at (nullable until they leave).

FR-CA-03 — Audio/video controls

  • Microphone mute/unmute: toggle local audio track enabled state.
  • Camera on/off: toggle local video track enabled state.
  • Audio routing managed via react-native-incall-manager (earpiece / speakerphone / Bluetooth).

FR-CA-04 — Background calls

  • An Android foreground service keeps the current WebSocket connection and call notification handling active while the app process remains alive in the background.
  • Force-killing the app stops call signaling. Killed-app notifications require the planned Firebase Cloud Messaging (FCM) replacement.

FR-CA-05 — Call history

  • call and callparticipant are SyncableModel — synced to WatermelonDB via pull/push sync.
  • Call history is displayed from local WatermelonDB, not fetched on demand.

Non-Functional Requirements

IDRequirement
NFR-CA-01Both devices must be reachable on the LAN — the server does not relay media
NFR-CA-02No STUN or TURN server is required on a single LAN subnet
NFR-CA-03Calls require CAMERA and RECORD_AUDIO Android permissions
NFR-CA-04The server must never store or forward audio/video streams

Out of Scope

See design.md#non-goals for what this feature explicitly does not cover.