realtime.proto 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  1. syntax = "proto3";
  2. package chatto.realtime.v1;
  3. import "chatto/api/v1/notification_preferences.proto";
  4. import "chatto/api/v1/presence.proto";
  5. import "chatto/api/v1/viewer.proto";
  6. import "google/protobuf/timestamp.proto";
  7. // Client-to-server frame for Chatto's protobuf WebSocket realtime protocol.
  8. //
  9. // Clients send binary protobuf frames to `/api/realtime`. The first frame must
  10. // be `hello`; after the server replies with `hello`, clients send
  11. // `subscribe_events` to start the authenticated live event stream.
  12. message RealtimeClientFrame {
  13. reserved 4;
  14. reserved "ack";
  15. oneof frame {
  16. // Opens the protocol session and optionally carries a bearer token.
  17. RealtimeClientHello hello = 1;
  18. // Starts the caller's authorized live-event stream.
  19. RealtimeSubscribeEvents subscribe_events = 2;
  20. // Application-level ping. The server replies with `pong`.
  21. RealtimePing ping = 3;
  22. }
  23. }
  24. // Server-to-client frame for Chatto's protobuf WebSocket realtime protocol.
  25. message RealtimeServerFrame {
  26. oneof frame {
  27. // Confirms protocol version, server version, and capabilities.
  28. RealtimeServerHello hello = 1;
  29. // Confirms the live-event stream has started.
  30. RealtimeSubscribed subscribed = 2;
  31. // A live event authorized for the connected user.
  32. RealtimeEventEnvelope event = 3;
  33. // Application-level heartbeat used for liveness checks.
  34. RealtimeHeartbeat heartbeat = 4;
  35. // Protocol or authorization error.
  36. RealtimeError error = 5;
  37. // Server-requested close with reconnect guidance.
  38. RealtimeClose close = 6;
  39. // Reply to a client ping.
  40. RealtimePong pong = 7;
  41. }
  42. }
  43. // Initial client hello.
  44. message RealtimeClientHello {
  45. reserved 3;
  46. reserved "resume_cursor";
  47. // Protocol version requested by the client. The current version is 1.
  48. uint32 protocol_version = 1;
  49. // Optional bearer token. When present, it takes precedence over cookie auth.
  50. optional string bearer_token = 2;
  51. }
  52. // Initial server hello.
  53. message RealtimeServerHello {
  54. reserved 3;
  55. reserved "resume_supported";
  56. // Protocol version accepted by the server.
  57. uint32 protocol_version = 1;
  58. // Chatto server software version.
  59. string server_version = 2;
  60. // Approximate heartbeat interval clients should expect.
  61. uint32 heartbeat_interval_seconds = 4;
  62. // Stable protocol capability keys supported by this server. Current keys:
  63. // `chatto.realtime.events.live.v1`, `chatto.realtime.heartbeat.v1`, and
  64. // `chatto.realtime.ping.v1`.
  65. repeated string capabilities = 5;
  66. }
  67. // Request to start the caller's authorized live event stream.
  68. message RealtimeSubscribeEvents {}
  69. // Confirms event streaming has started.
  70. message RealtimeSubscribed {
  71. reserved 1;
  72. reserved "cursor";
  73. }
  74. // Application-level ping.
  75. message RealtimePing {
  76. // Client-chosen opaque value echoed in the pong.
  77. string nonce = 1;
  78. }
  79. // Application-level pong.
  80. message RealtimePong {
  81. // Echo of the ping nonce.
  82. string nonce = 1;
  83. }
  84. // Application-level heartbeat.
  85. message RealtimeHeartbeat {
  86. // Stable event ID for this heartbeat.
  87. string id = 1;
  88. // Time the heartbeat was emitted.
  89. google.protobuf.Timestamp created_at = 2;
  90. }
  91. // Protocol error.
  92. message RealtimeError {
  93. // Stable machine-readable error code.
  94. string code = 1;
  95. // Human-readable diagnostic message.
  96. string message = 2;
  97. // True when the server will close the socket after sending this error.
  98. bool fatal = 3;
  99. }
  100. // Close instruction sent before the socket is closed when possible.
  101. message RealtimeClose {
  102. // Stable machine-readable close code.
  103. string code = 1;
  104. // Human-readable diagnostic message.
  105. string message = 2;
  106. // True when the client should reconnect after a delay.
  107. bool reconnect = 3;
  108. // Suggested reconnect delay in milliseconds.
  109. uint32 retry_after_ms = 4;
  110. }
  111. // One authorized live event delivered over the realtime WebSocket.
  112. //
  113. // Realtime events are invalidation signals first: payloads carry stable IDs and
  114. // small inline hints, while durable resource state is hydrated through
  115. // chatto.api.v1 ConnectRPC services. Event-specific messages document the
  116. // intended hydration path when a referenced resource may need refreshing.
  117. message RealtimeEventEnvelope {
  118. reserved 4;
  119. reserved "cursor";
  120. // Stable event ID.
  121. string id = 1;
  122. // Time the event was created.
  123. google.protobuf.Timestamp created_at = 2;
  124. // User or system actor that caused the event, when known.
  125. optional string actor_id = 3;
  126. oneof event {
  127. // A message was posted in a room or thread.
  128. RealtimeMessagePostedEvent message_posted = 10;
  129. // A message body or renderable metadata changed.
  130. RealtimeMessageEditedEvent message_edited = 11;
  131. // A message was retracted.
  132. RealtimeMessageRetractedEvent message_retracted = 12;
  133. // A user added a reaction to a message.
  134. RealtimeReactionEvent reaction_added = 20;
  135. // A user removed a reaction from a message.
  136. RealtimeReactionEvent reaction_removed = 21;
  137. // A user is typing in a room or thread.
  138. RealtimeTypingEvent user_typing = 30;
  139. // A user's live presence changed.
  140. RealtimePresenceChangedEvent presence_changed = 31;
  141. // A room was created.
  142. RealtimeRoomEvent room_created = 40;
  143. // A room's profile or settings changed.
  144. RealtimeRoomEvent room_updated = 41;
  145. // A room was deleted.
  146. RealtimeRoomEvent room_deleted = 42;
  147. // A room was archived.
  148. RealtimeRoomEvent room_archived = 43;
  149. // A room was restored from the archive.
  150. RealtimeRoomEvent room_unarchived = 44;
  151. // A user joined a room visible to the current user.
  152. RealtimeRoomEvent user_joined_room = 45;
  153. // A user left a room visible to the current user.
  154. RealtimeRoomEvent user_left_room = 46;
  155. // A room's universal visibility flag changed.
  156. RealtimeRoomUniversalChangedEvent room_universal_changed = 47;
  157. // A notification was created for the current user.
  158. RealtimeNotificationCreatedEvent notification_created = 60;
  159. // A notification was dismissed for the current user.
  160. RealtimeNotificationDismissedEvent notification_dismissed = 61;
  161. // The current user's notification level changed.
  162. RealtimeNotificationLevelChangedEvent notification_level_changed = 62;
  163. // The current user's thread follow state changed.
  164. RealtimeThreadFollowChangedEvent thread_follow_changed = 63;
  165. // The current user's room read state changed.
  166. RealtimeRoomMarkedAsReadEvent room_marked_as_read = 64;
  167. // A thread was created in a room visible to the current user.
  168. RealtimeThreadCreatedEvent thread_created = 65;
  169. // The server profile/config changed.
  170. RealtimeServerUpdatedEvent server_updated = 70;
  171. // A user profile changed.
  172. RealtimeUserProfileUpdatedEvent user_profile_updated = 71;
  173. // A user's custom status was set or changed.
  174. RealtimeUserCustomStatusSetEvent user_custom_status_set = 72;
  175. // A user's custom status was cleared.
  176. RealtimeUserCustomStatusClearedEvent user_custom_status_cleared = 73;
  177. // The current user's display preferences changed.
  178. RealtimeServerUserPreferencesUpdatedEvent server_user_preferences_updated = 74;
  179. // Room-group layout changed.
  180. RealtimeRoomGroupsUpdatedEvent room_groups_updated = 75;
  181. // A server member was deleted.
  182. RealtimeServerMemberDeletedEvent server_member_deleted = 76;
  183. // Asset processing started for a message attachment.
  184. RealtimeAssetProcessingEvent asset_processing_started = 80;
  185. // Asset processing completed successfully for a message attachment.
  186. RealtimeAssetProcessingEvent asset_processing_succeeded = 81;
  187. // Asset processing failed for a message attachment.
  188. RealtimeAssetProcessingEvent asset_processing_failed = 82;
  189. // An asset attached to a message was deleted.
  190. RealtimeAssetDeletedEvent asset_deleted = 83;
  191. // A voice call started in a room.
  192. RealtimeCallEvent call_started = 84;
  193. // A participant joined a voice call.
  194. RealtimeCallEvent call_participant_joined = 85;
  195. // A participant left a voice call.
  196. RealtimeCallEvent call_participant_left = 86;
  197. // A voice call ended.
  198. RealtimeCallEvent call_ended = 87;
  199. // The current user was mentioned in a room.
  200. RealtimeMentionNotificationEvent mention_notification = 88;
  201. // The current user received a new direct message.
  202. RealtimeNewDirectMessageNotificationEvent new_direct_message_notification = 89;
  203. // The current user's session was terminated.
  204. RealtimeSessionTerminatedEvent session_terminated = 90;
  205. }
  206. }
  207. // Message-posted signal.
  208. //
  209. // Hydrate the affected timeline with `RoomService.GetRoomEventsAround`
  210. // for room messages, or `ThreadService.GetThreadEventsAround` when
  211. // `thread_root_event_id` is set.
  212. message RealtimeMessagePostedEvent {
  213. // Room containing the message.
  214. string room_id = 1;
  215. // Event ID of the posted message.
  216. string message_event_id = 2;
  217. // Root message event ID when the message was posted in a thread.
  218. optional string thread_root_event_id = 3;
  219. }
  220. // Message-edited signal.
  221. //
  222. // Refresh the affected timeline window with
  223. // `RoomService.GetRoomEventsAround` for room messages, or
  224. // `ThreadService.GetThreadEventsAround` when the local message belongs to
  225. // a thread.
  226. message RealtimeMessageEditedEvent {
  227. // Room containing the message.
  228. string room_id = 1;
  229. // Event ID of the edited message.
  230. string message_event_id = 2;
  231. }
  232. // Message-retracted signal.
  233. //
  234. // Refresh or patch the affected timeline window with
  235. // `RoomService.GetRoomEventsAround` for room messages, or
  236. // `ThreadService.GetThreadEventsAround` when the local message belongs to
  237. // a thread.
  238. message RealtimeMessageRetractedEvent {
  239. // Room containing the message.
  240. string room_id = 1;
  241. // Event ID of the retracted message.
  242. string message_event_id = 2;
  243. // Retraction reason, when one was supplied.
  244. optional string reason = 3;
  245. }
  246. // Reaction signal.
  247. //
  248. // Refresh or patch the affected message in its timeline window. Use
  249. // `RoomService.GetRoomEventsAround` for room messages, or
  250. // `ThreadService.GetThreadEventsAround` when the local message belongs to
  251. // a thread. `message_event_id` is canonical: reactions made through a channel
  252. // echo of a thread reply report the original reply event ID. Clients that show
  253. // channel echoes should also refresh or patch visible echo rows whose
  254. // `echo_of_event_id` matches this ID.
  255. message RealtimeReactionEvent {
  256. // Room containing the reacted-to message.
  257. string room_id = 1;
  258. // Canonical event ID of the reacted-to message.
  259. string message_event_id = 2;
  260. // Reaction emoji.
  261. string emoji = 3;
  262. }
  263. // Typing signal.
  264. //
  265. // This is an ephemeral signal. `room_id` and `thread_root_event_id` identify
  266. // where to display typing state; clients normally do not hydrate it.
  267. message RealtimeTypingEvent {
  268. // Room where the actor is typing.
  269. string room_id = 1;
  270. // Thread root event ID when the actor is typing in a thread.
  271. optional string thread_root_event_id = 2;
  272. }
  273. // Presence-changed signal.
  274. //
  275. // The latest presence status is inline. Use `UserService.GetUser` when
  276. // the surrounding user profile or custom status also needs refreshing.
  277. message RealtimePresenceChangedEvent {
  278. // User whose presence changed.
  279. string user_id = 1;
  280. // Latest presence status.
  281. chatto.api.v1.PresenceStatus status = 2;
  282. }
  283. // Room lifecycle or membership signal.
  284. //
  285. // Hydrate visible room state with `RoomDirectoryService.GetRoom` or
  286. // `RoomDirectoryService.BatchGetRooms`. Unknown/deleted rooms return
  287. // `NOT_FOUND` from singular reads, hidden rooms return `PERMISSION_DENIED`, and
  288. // batch reads omit unknown, deleted, hidden, and inaccessible rooms.
  289. message RealtimeRoomEvent {
  290. // Room affected by the event.
  291. string room_id = 1;
  292. }
  293. // Room universal-visibility signal.
  294. //
  295. // The latest universal flag is inline. Hydrate the rest of the visible room with
  296. // `RoomDirectoryService.GetRoom` or `RoomDirectoryService.BatchGetRooms`.
  297. message RealtimeRoomUniversalChangedEvent {
  298. // Room whose universal setting changed.
  299. string room_id = 1;
  300. // Latest universal setting.
  301. bool universal = 2;
  302. }
  303. // Notification-created signal for the connected user.
  304. //
  305. // Hydrate full notification rows with `NotificationService.GetNotification` or
  306. // `NotificationService.BatchGetNotifications`. Use `RoomService` or
  307. // `ThreadService` anchor reads when `event_id` or `in_reply_to_id` must be
  308. // opened in context.
  309. message RealtimeNotificationCreatedEvent {
  310. // Notification ID.
  311. string notification_id = 1;
  312. // Room associated with the notification, when the notification navigates to a room.
  313. optional string room_id = 2;
  314. // Event ID associated with the notification, when the notification navigates to an event.
  315. optional string event_id = 3;
  316. // Parent event ID for reply notifications, when known.
  317. optional string in_reply_to_id = 4;
  318. // True when the notification should not alert.
  319. bool silent = 5;
  320. }
  321. // Notification-dismissed signal for the connected user.
  322. //
  323. // Remove the local notification row by ID. A later singular notification read
  324. // returns `NOT_FOUND`; batch reads omit dismissed or missing notifications.
  325. message RealtimeNotificationDismissedEvent {
  326. // Notification ID.
  327. string notification_id = 1;
  328. }
  329. // Notification-level signal for the connected user.
  330. //
  331. // The updated level is inline. Use
  332. // `NotificationPreferencesService.GetRoomNotificationPreference` when clients
  333. // need the complete preference resource.
  334. message RealtimeNotificationLevelChangedEvent {
  335. // Room whose notification level changed.
  336. string room_id = 1;
  337. // Explicit room notification level.
  338. chatto.api.v1.NotificationLevel level = 2;
  339. // Effective notification level after defaults are applied.
  340. chatto.api.v1.NotificationLevel effective_level = 3;
  341. }
  342. // Thread-follow signal for the connected user.
  343. //
  344. // The updated follow state is inline. Use `ThreadService.GetThreadEvents`
  345. // or `ThreadService.GetThreadEventsAround` when the thread itself needs
  346. // refreshing.
  347. message RealtimeThreadFollowChangedEvent {
  348. // Room containing the thread.
  349. string room_id = 1;
  350. // Root message event ID of the thread.
  351. string thread_root_event_id = 2;
  352. // True when the user now follows the thread.
  353. bool following = 3;
  354. }
  355. // Thread-created signal.
  356. //
  357. // Hydrate the new thread with `ThreadService.GetThreadEventsAround` using
  358. // `thread_root_event_id` as the anchor.
  359. message RealtimeThreadCreatedEvent {
  360. // Room containing the thread.
  361. string room_id = 1;
  362. // Root message event ID that identifies the thread.
  363. string thread_root_event_id = 2;
  364. }
  365. // Room-read signal for the connected user.
  366. //
  367. // This is a current-user read-state invalidation signal. Clients can patch local
  368. // unread state for the room; no separate hydration is normally needed.
  369. message RealtimeRoomMarkedAsReadEvent {
  370. // Room marked read.
  371. string room_id = 1;
  372. }
  373. // Server-profile signal.
  374. //
  375. // Public server profile fields are inline. Use `ServerDiscoveryService.GetServer`
  376. // for public metadata and `ServerService.GetMotd` for the authenticated MOTD.
  377. message RealtimeServerUpdatedEvent {
  378. // Server display name.
  379. string name = 1;
  380. // Server description.
  381. string description = 2;
  382. // Server logo URL, when configured.
  383. optional string logo_url = 3;
  384. // Server banner URL, when configured.
  385. optional string banner_url = 4;
  386. }
  387. // User-profile signal.
  388. //
  389. // Basic profile fields are inline. Use `UserService.GetUser` or
  390. // `UserService.BatchGetUsers` for complete user-profile hydration.
  391. message RealtimeUserProfileUpdatedEvent {
  392. // User whose profile changed.
  393. string user_id = 1;
  394. // Login name.
  395. string login = 2;
  396. // Display name.
  397. string display_name = 3;
  398. // Avatar URL, when the user has an avatar.
  399. optional string avatar_url = 4;
  400. }
  401. // User-custom-status set signal.
  402. //
  403. // The latest custom status is inline. Use `UserService.GetUser` when
  404. // clients need to refresh the complete user profile.
  405. message RealtimeUserCustomStatusSetEvent {
  406. // User whose status changed.
  407. string user_id = 1;
  408. // Emoji part of the status.
  409. string emoji = 2;
  410. // Text part of the status.
  411. string text = 3;
  412. // Expiration time, when the status expires automatically.
  413. google.protobuf.Timestamp expires_at = 4;
  414. }
  415. // User-custom-status cleared signal.
  416. //
  417. // Clear local custom status for `user_id`. Use `UserService.GetUser`
  418. // when clients need to refresh the complete user profile.
  419. message RealtimeUserCustomStatusClearedEvent {
  420. // User whose status was cleared.
  421. string user_id = 1;
  422. }
  423. // User display-preferences signal for the connected user.
  424. //
  425. // The updated display preferences are inline for the connected user.
  426. message RealtimeServerUserPreferencesUpdatedEvent {
  427. // IANA timezone name, when the user has selected a fixed timezone.
  428. optional string timezone = 1;
  429. // Preferred time display format.
  430. chatto.api.v1.TimeFormat time_format = 2;
  431. }
  432. // Room-group layout signal.
  433. //
  434. // Refetch room-group layout with `RoomDirectoryService.ListRoomGroups`. If a
  435. // client already knows specific group IDs, it can use
  436. // `RoomDirectoryService.GetRoomGroup` or
  437. // `RoomDirectoryService.BatchGetRoomGroups`.
  438. message RealtimeRoomGroupsUpdatedEvent {
  439. // Always true; present for event-bus compatibility.
  440. bool changed = 1;
  441. }
  442. // Server-member deleted signal.
  443. //
  444. // Remove or invalidate local member/user rows for `user_id`. Admin clients can
  445. // hydrate remaining member rows through `AdminUserService.GetMember` or
  446. // `AdminUserService.BatchGetMembers`.
  447. message RealtimeServerMemberDeletedEvent {
  448. // Deleted user ID.
  449. string user_id = 1;
  450. }
  451. // Asset-processing signal.
  452. //
  453. // Message attachments are message-owned subresources. When
  454. // `message_event_id` is known, refetch message state with
  455. // `MessageService.GetMessage` or `MessageService.BatchGetMessages`; otherwise
  456. // refetch the affected attachment list or timeline window.
  457. message RealtimeAssetProcessingEvent {
  458. // Room containing the message that owns the asset, when known.
  459. optional string room_id = 1;
  460. // Asset ID.
  461. string asset_id = 2;
  462. // Event ID of the message that owns the asset, when known.
  463. optional string message_event_id = 3;
  464. }
  465. // Asset-deleted signal.
  466. //
  467. // Remove local attachment state by `asset_id`. When the owning message is known,
  468. // clients can refetch that message through `MessageService.GetMessage` or
  469. // refetch the timeline window.
  470. message RealtimeAssetDeletedEvent {
  471. // Room containing the message that owned the asset, when known.
  472. optional string room_id = 1;
  473. // Asset ID.
  474. string asset_id = 2;
  475. }
  476. // Voice-call state signal.
  477. //
  478. // Hydrate current room-scoped call state with `VoiceCallService.GetActiveCall`
  479. // or `VoiceCallService.BatchGetActiveCalls`. Compare `call_id` to ignore stale
  480. // transitions from a previous call in the same room.
  481. message RealtimeCallEvent {
  482. // Room containing the call.
  483. string room_id = 1;
  484. // Call ID.
  485. string call_id = 2;
  486. // Source that produced the call transition.
  487. RealtimeCallEventSource source = 3;
  488. }
  489. // Source that produced a realtime voice-call transition.
  490. enum RealtimeCallEventSource {
  491. // No call event source was specified.
  492. REALTIME_CALL_EVENT_SOURCE_UNSPECIFIED = 0;
  493. // The user explicitly requested the transition through Chatto.
  494. REALTIME_CALL_EVENT_SOURCE_USER = 1;
  495. // LiveKit reported the transition through a webhook.
  496. REALTIME_CALL_EVENT_SOURCE_LIVEKIT = 2;
  497. // Chatto reconciled its local state against LiveKit's current room state.
  498. REALTIME_CALL_EVENT_SOURCE_RECONCILIATION = 3;
  499. }
  500. // Mention attention signal for the connected user.
  501. //
  502. // Inline names are display hints. Hydrate referenced rooms through
  503. // `RoomDirectoryService.BatchGetRooms` and users through
  504. // `UserService.BatchGetUsers` when local caches are missing or stale.
  505. message RealtimeMentionNotificationEvent {
  506. // Room where the mention occurred.
  507. string room_id = 1;
  508. // Author user ID.
  509. string actor_user_id = 2;
  510. // Display name of the room where the mention occurred, when hydrated for the caller.
  511. optional string room_name = 3;
  512. // Display name of the author who mentioned the connected user, when hydrated.
  513. optional string actor_display_name = 4;
  514. }
  515. // New-DM attention signal for the connected user.
  516. //
  517. // Inline names and avatar URLs are display hints. Hydrate the DM room through
  518. // `RoomDirectoryService.GetRoom` or `RoomDirectoryService.BatchGetRooms`, and
  519. // the sender through `UserService.GetUser` or `UserService.BatchGetUsers` when
  520. // local caches are missing or stale.
  521. message RealtimeNewDirectMessageNotificationEvent {
  522. // DM room ID.
  523. string room_id = 1;
  524. // Sender user ID.
  525. string sender_id = 2;
  526. // Display name of the sender, when hydrated.
  527. optional string sender_display_name = 3;
  528. // Avatar URL of the sender, when one is available.
  529. optional string sender_avatar_url = 4;
  530. // Display name for the DM conversation from the connected user's perspective, when hydrated.
  531. optional string conversation_name = 5;
  532. }
  533. // Session-terminated signal for the connected user's current session.
  534. message RealtimeSessionTerminatedEvent {
  535. // Termination reason.
  536. string reason = 1;
  537. }