syntax = "proto3"; package chatto.api.v1; import "buf/validate/validate.proto"; import "chatto/api/v1/rooms.proto"; import "chatto/api/v1/users.proto"; import "google/protobuf/timestamp.proto"; // Room-scoped voice and video calls. service VoiceCallService { // Lists member rooms that currently have active calls as a finite // runtime snapshot. Rooms the caller is not a member of are omitted. // // Returns an empty list when LiveKit is not configured. rpc ListActiveCalls(ListActiveCallsRequest) returns (ListActiveCallsResponse); // Gets the current active call snapshot for one room. // // The caller must be a member of the room. Returns NOT_FOUND when the room // does not exist, currently has no active call, or voice calls are not // configured. The returned call_id lets clients ignore stale realtime call // events from previous calls in the same room. rpc GetActiveCall(GetActiveCallRequest) returns (GetActiveCallResponse); // Gets current active call snapshots for many rooms. // // Rooms that do not exist, are inaccessible, or currently have no active call // are omitted. Results preserve first-seen request order and repeated room // IDs are de-duplicated. Returns an empty list when LiveKit is not configured. rpc BatchGetActiveCalls(BatchGetActiveCallsRequest) returns (BatchGetActiveCallsResponse); // Lists participants currently observed in a room's active call as a finite // runtime snapshot. // // The caller must be a member of the room. // Returns NOT_FOUND when the room does not exist and PERMISSION_DENIED when // the caller is not a room member. rpc ListCallParticipants(ListCallParticipantsRequest) returns (ListCallParticipantsResponse); // Records the caller's intent to join a room call. // // Requires room membership and call.join. If no call is active, also // requires call.start. Publishing permissions do not grant admission. // // Returns joined=false when LiveKit is not configured. rpc JoinCall(JoinCallRequest) returns (JoinCallResponse); // Issues a LiveKit token for joining the room's active call. // // Requires room membership and call.join. A call must already be active. // The token permits microphone, camera, and screen-share sources only when // call.voice, call.camera, and call.screenshare respectively allow them. // A caller without publishing permissions can listen and watch. // Tokens expire after five minutes. Expiry does not end an established call. // Chatto checks connected participant permissions on its 30-second LiveKit // reconciliation cycle. Failed checks retry on subsequent cycles. // Returns NOT_FOUND when the room does not exist, PERMISSION_DENIED when the // caller is not a room member, and FAILED_PRECONDITION when no call is active // or voice and video calls are not configured. rpc CreateCallToken(CreateCallTokenRequest) returns (CreateCallTokenResponse); // Issues a short-lived LiveKit token for a native companion publisher owned // by the caller. Companion publishers contribute media to the caller's // logical participant without becoming call participants themselves. // // Requires room membership, call.join, call.screenshare, and participation // in the active call. Captured application audio does not need call.voice. // The credential expires after one minute; an established publisher is // checked by the same permission reconciliation as the main participant. // Returns // FAILED_PRECONDITION when no call is active, the caller has not joined it, // or voice and video calls are not configured. rpc CreateCallMediaPublisherToken(CreateCallMediaPublisherTokenRequest) returns (CreateCallMediaPublisherTokenResponse); // Records the caller's intent to leave a room call. // // Requires room membership, but not call.join or a publishing permission. // // Returns left=false when LiveKit is not configured. rpc LeaveCall(LeaveCallRequest) returns (LeaveCallResponse); } // Request for active room call snapshots. message ListActiveCallsRequest {} // Finite runtime snapshot of active room calls. message ListActiveCallsResponse { // Active calls in room order returned by the call-state projection. repeated ActiveCall calls = 1; } // Current active call snapshot for one room. message ActiveCall { reserved 1; reserved "room_id"; // Room containing the active call. RoomSummary room = 4; // Active call session ID. string call_id = 2; // Participants currently projected for this call. repeated CallParticipant participants = 3; } // Request for one room's active call snapshot. message GetActiveCallRequest { // Required. Room whose active call should be inspected. string room_id = 1 [(buf.validate.field).string.min_len = 1]; } // Response for one room's active call snapshot. message GetActiveCallResponse { // Current active call. ActiveCall call = 1; } // Request active call snapshots for many rooms. message BatchGetActiveCallsRequest { // Required room IDs. Unknown, inaccessible, and inactive rooms are omitted. repeated string room_ids = 1 [(buf.validate.field).repeated = { min_items: 1 max_items: 100 items: { string: {min_len: 1} } }]; } // Active call batch response. message BatchGetActiveCallsResponse { // Active calls in first-seen request order. repeated ActiveCall calls = 1; } // Request for participants in a room call. message ListCallParticipantsRequest { // Required. Room whose active call should be inspected. string room_id = 1 [(buf.validate.field).string.min_len = 1]; } // Finite runtime snapshot of participants in one room call. message ListCallParticipantsResponse { // Participants currently projected for the room call. repeated CallParticipant participants = 1; } // User currently participating in a room call. message CallParticipant { // Participant user. User user = 1; // When the user joined this call. google.protobuf.Timestamp joined_at = 2; // Active call session ID. string call_id = 3; } // Request to record joining a room call. message JoinCallRequest { // Required. Room whose call is being joined. string room_id = 1 [(buf.validate.field).string.min_len = 1]; } // Response from recording a join intent. message JoinCallResponse { // True when a join fact was recorded. bool joined = 1; } // Request for a LiveKit token for a room call. message CreateCallTokenRequest { // Required. Room whose active call should be joined. string room_id = 1 [(buf.validate.field).string.min_len = 1]; } // LiveKit token details for joining a room call. message CreateCallTokenResponse { // LiveKit JWT token. string token = 1; // Shared E2EE key for this active call. string e2ee_key = 2; // Active call session ID. string call_id = 3; } // Native companion publisher kind. enum CallMediaPublisherKind { // The publisher kind was not specified. CALL_MEDIA_PUBLISHER_KIND_UNSPECIFIED = 0; // Native game-window video and application audio. CALL_MEDIA_PUBLISHER_KIND_GAME_SHARE = 1; } // Request for a native companion publisher credential. message CreateCallMediaPublisherTokenRequest { // Required. Room whose active call receives the media. string room_id = 1 [(buf.validate.field).string.min_len = 1]; // Required. Kind of native media publisher being started. CallMediaPublisherKind kind = 2 [(buf.validate.field).enum = {defined_only: true not_in: [0]}]; } // Credential for one native companion publisher connection. message CreateCallMediaPublisherTokenResponse { reserved 4; reserved "publisher_identity"; // Short-lived LiveKit JWT token. string token = 1; // Shared E2EE key for this active call. string e2ee_key = 2; // Active call session ID. string call_id = 3; } // Request to record leaving a room call. message LeaveCallRequest { // Required. Room whose call is being left. string room_id = 1 [(buf.validate.field).string.min_len = 1]; } // Response from recording a leave intent. message LeaveCallResponse { // True when a leave fact was recorded. bool left = 1; }