realtime.proto 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. syntax = "proto3";
  2. package chatto.realtime.v1;
  3. import "chatto/api/v1/member_directory.proto";
  4. import "chatto/api/v1/room_directory.proto";
  5. import "chatto/api/v1/server.proto";
  6. import "chatto/api/v1/voice_calls.proto";
  7. import "chatto/realtime/v1/events.proto";
  8. import "google/protobuf/duration.proto";
  9. import "google/protobuf/timestamp.proto";
  10. // Starts one authorized public realtime subscription.
  11. //
  12. // The client sends this message as the first binary WebSocket message. The
  13. // client does not send more application messages on the socket.
  14. message RealtimeSubscribe {
  15. // Behavioral protocol version requested by the client. The only supported
  16. // version is 4.
  17. uint32 protocol_version = 1;
  18. // Optional bearer token. When present, it takes precedence over cookie auth.
  19. optional string bearer_token = 2;
  20. // Opaque cursor from a previously received replayable event or `caught_up`
  21. // frame. A usable cursor receives bounded authorized events after
  22. // that position. Cursors expire 15 minutes after issue.
  23. optional string resume_cursor = 3;
  24. // Required fallback when the cursor is absent or cannot resume safely.
  25. RealtimeInitialState initial_state = 4;
  26. }
  27. // Server-to-client frame for Chatto's protobuf WebSocket realtime protocol.
  28. message RealtimeServerFrame {
  29. oneof frame {
  30. // One authorized public event copy.
  31. RealtimeEvent event = 1;
  32. // Application-level heartbeat used for liveness checks.
  33. RealtimeHeartbeat heartbeat = 2;
  34. // Server-requested close with reconnect guidance.
  35. RealtimeClose close = 3;
  36. // Confirms that recovery reached the live-stream boundary.
  37. RealtimeCaughtUp caught_up = 4;
  38. // One exact authorized server-content snapshot.
  39. RealtimeSnapshot snapshot = 5;
  40. }
  41. }
  42. // Startup behavior when a subscription cannot resume from its cursor.
  43. enum RealtimeInitialState {
  44. // Invalid value. The server rejects subscriptions that use it.
  45. REALTIME_INITIAL_STATE_UNSPECIFIED = 0;
  46. // Start at the current event boundary without sending current resources.
  47. REALTIME_INITIAL_STATE_LIVE_ONLY = 1;
  48. // Send an exact authorized snapshot before later public events.
  49. REALTIME_INITIAL_STATE_SNAPSHOT = 2;
  50. }
  51. // One authorized snapshot of the current bounded server content.
  52. //
  53. // The server sends at most one snapshot before `caught_up`. The client must
  54. // discard the snapshot if the socket closes before `caught_up`. List values
  55. // replace the complete local family.
  56. // Large and paginated resources, such as message history, are not part of the
  57. // snapshot and remain available through ConnectRPC.
  58. message RealtimeSnapshot {
  59. // Current public server profile.
  60. chatto.api.v1.ServerPublicProfile server = 1;
  61. // All rooms visible to the caller at the snapshot boundary.
  62. repeated chatto.api.v1.RoomWithViewerState rooms = 2;
  63. // All room groups visible to the caller at the snapshot boundary.
  64. repeated chatto.api.v1.RoomGroup room_groups = 3;
  65. // The viewer and public users referenced by another snapshot family.
  66. repeated chatto.api.v1.DirectoryMember users = 4;
  67. // All active calls visible to the caller at the snapshot boundary.
  68. repeated chatto.api.v1.ActiveCall active_calls = 5;
  69. }
  70. // Confirms that recovery is complete and live delivery has begun.
  71. //
  72. // Clients can retain `cursor` after they apply every preceding event.
  73. message RealtimeCaughtUp {
  74. // Opaque cursor at the recovery-to-live handoff boundary.
  75. string cursor = 1;
  76. // Actual recovery result, including a successful replay with no events.
  77. RealtimeRecovery recovery = 2;
  78. }
  79. // How the server reached the live-delivery boundary.
  80. enum RealtimeRecovery {
  81. // Invalid value. The server always supplies a recovery result.
  82. REALTIME_RECOVERY_UNSPECIFIED = 0;
  83. // The requested cursor was accepted and all available authorized events followed it.
  84. REALTIME_RECOVERY_RESUMED = 1;
  85. // The server sent a replacement snapshot.
  86. REALTIME_RECOVERY_SNAPSHOT = 2;
  87. // The server started at the current boundary without past events or resources.
  88. REALTIME_RECOVERY_LIVE_ONLY = 3;
  89. }
  90. // One authorized public event with its optional cursor.
  91. //
  92. // Each payload uses a realtime-owned public message. The union is the public
  93. // event catalogue. An internal event that is absent from this union is not
  94. // part of the realtime API. The server creates a fresh caller-specific value
  95. // after authorization. An event has a resume cursor when it can be replayed.
  96. // An event without a cursor is safe to miss, or its state can be restored from
  97. // a snapshot or a ConnectRPC resource.
  98. message RealtimeEvent {
  99. // Universal event identifier (NanoID).
  100. string id = 1;
  101. // Time when the source event was created.
  102. google.protobuf.Timestamp created_at = 2;
  103. // ID of the user that caused the event. Absence means no user actor.
  104. optional string actor_id = 3;
  105. // Opaque cursor safe to retain after this complete event is accepted.
  106. optional string cursor = 4;
  107. reserved 35 to 45;
  108. reserved "room_group_created", "room_group_updated", "room_group_deleted", "room_added_to_group", "room_removed_from_group", "rooms_in_group_reordered", "sidebar_link_added_to_group", "sidebar_link_updated", "sidebar_link_removed_from_group", "sidebar_group_entries_reordered", "room_groups_reordered";
  109. // Authorized semantic payload.
  110. oneof event {
  111. RoomCreatedEvent room_created = 10;
  112. RoomUpdatedEvent room_updated = 11;
  113. RoomDeletedEvent room_deleted = 12;
  114. RoomArchivedEvent room_archived = 13;
  115. RoomUnarchivedEvent room_unarchived = 14;
  116. RoomUniversalChangedEvent room_universal_changed = 15;
  117. RoomSlowModeChangedEvent room_slow_mode_changed = 16;
  118. RoomThreadingModeChangedEvent room_threading_mode_changed = 17;
  119. UserJoinedRoomEvent user_joined_room = 18;
  120. UserLeftRoomEvent user_left_room = 19;
  121. VoiceCallParticipantJoinedEvent voice_call_participant_joined = 20;
  122. VoiceCallParticipantLeftEvent voice_call_participant_left = 21;
  123. VoiceCallStartedEvent voice_call_started = 22;
  124. VoiceCallEndedEvent voice_call_ended = 23;
  125. MessagePostedEvent message_posted = 24;
  126. MessageEditedEvent message_edited = 25;
  127. MessageRetractedEvent message_retracted = 26;
  128. MessagePinnedEvent message_pinned = 27;
  129. MessageUnpinnedEvent message_unpinned = 28;
  130. ThreadCreatedEvent thread_created = 29;
  131. AssetProcessingStartedEvent asset_processing_started = 30;
  132. AssetProcessingSucceededEvent asset_processing_succeeded = 31;
  133. AssetProcessingFailedEvent asset_processing_failed = 32;
  134. AssetDeletedEvent asset_deleted = 33;
  135. ServerMotdChangedEvent server_motd_changed = 34;
  136. UserAccountCreatedEvent user_account_created = 46;
  137. UserProfileChangedEvent user_profile_changed = 47;
  138. UserAccountDeletedEvent user_account_deleted = 48;
  139. ReactionAddedEvent reaction_added = 49;
  140. ReactionRemovedEvent reaction_removed = 50;
  141. ViewerPreferencesChangedEvent viewer_preferences_changed = 51;
  142. ThreadViewerStateChangedEvent thread_viewer_state_changed = 52;
  143. ServerProfileChangedEvent server_profile_changed = 53;
  144. UserTypingEvent user_typing = 54;
  145. PresenceChangedEvent presence_changed = 55;
  146. NotificationOccurrencesChangedEvent notification_occurrences_changed = 56;
  147. NotificationUnreadStateChangedEvent notification_unread_state_changed = 57;
  148. RoomReadStateChangedEvent room_read_state_changed = 58;
  149. RoomLayoutChangedEvent room_layout_changed = 59;
  150. RoleCreatedEvent role_created = 60;
  151. RoleUpdatedEvent role_updated = 61;
  152. RoleDeletedEvent role_deleted = 62;
  153. RolesReorderedEvent roles_reordered = 63;
  154. RoleAssignedEvent role_assigned = 64;
  155. RoleRevokedEvent role_revoked = 65;
  156. RolePermissionsChangedEvent role_permissions_changed = 66;
  157. ViewerPermissionsChangedEvent viewer_permissions_changed = 67;
  158. ViewerPresencePreferenceChangedEvent viewer_presence_preference_changed = 68;
  159. }
  160. }
  161. // Application-level heartbeat.
  162. message RealtimeHeartbeat {
  163. // Fresh resume cursor for the latest replay boundary delivered on this
  164. // socket. A client can retain it only after all earlier frames have been
  165. // applied.
  166. optional string cursor = 1;
  167. }
  168. // Stable server-requested close category.
  169. enum RealtimeCloseCode {
  170. // Invalid or unknown close code.
  171. REALTIME_CLOSE_CODE_UNSPECIFIED = 0;
  172. // The subscription message is malformed or contains an invalid value.
  173. REALTIME_CLOSE_CODE_INVALID_REQUEST = 1;
  174. // The requested behavioral protocol version is not supported.
  175. REALTIME_CLOSE_CODE_UNSUPPORTED_PROTOCOL = 2;
  176. // The server cannot accept or continue a realtime session at present.
  177. REALTIME_CLOSE_CODE_TEMPORARILY_UNAVAILABLE = 3;
  178. // The caller is no longer authenticated.
  179. REALTIME_CLOSE_CODE_AUTHENTICATION_REQUIRED = 4;
  180. // The caller must renew its session before reconnecting.
  181. REALTIME_CLOSE_CODE_SESSION_RENEWAL_REQUIRED = 5;
  182. // The client must discard its cursor and synchronize current state again.
  183. REALTIME_CLOSE_CODE_RESYNC_REQUIRED = 6;
  184. // The user's session ended and the client must return to sign-in.
  185. REALTIME_CLOSE_CODE_SESSION_TERMINATED = 7;
  186. // Privileged mode expired. Reconnect and refresh effective viewer and room permissions.
  187. REALTIME_CLOSE_CODE_PRIVILEGED_MODE_EXPIRED = 8;
  188. }
  189. // Close instruction sent before the socket closes when possible.
  190. message RealtimeClose {
  191. // Stable machine-readable close code.
  192. RealtimeCloseCode code = 1;
  193. // Safe human-readable close message.
  194. string message = 2;
  195. // Whether the client should reconnect automatically.
  196. bool reconnect = 3;
  197. // Suggested delay before reconnect.
  198. google.protobuf.Duration retry_after = 4;
  199. }