| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584 |
- syntax = "proto3";
- package chatto.realtime.v1;
- import "chatto/api/v1/notification_preferences.proto";
- import "chatto/api/v1/presence.proto";
- import "chatto/api/v1/viewer.proto";
- import "google/protobuf/timestamp.proto";
- // Client-to-server frame for Chatto's protobuf WebSocket realtime protocol.
- //
- // Clients send binary protobuf frames to `/api/realtime`. The first frame must
- // be `hello`; after the server replies with `hello`, clients send
- // `subscribe_events` to start the authenticated live event stream.
- message RealtimeClientFrame {
- reserved 4;
- reserved "ack";
- oneof frame {
- // Opens the protocol session and optionally carries a bearer token.
- RealtimeClientHello hello = 1;
- // Starts the caller's authorized live-event stream.
- RealtimeSubscribeEvents subscribe_events = 2;
- // Application-level ping. The server replies with `pong`.
- RealtimePing ping = 3;
- }
- }
- // Server-to-client frame for Chatto's protobuf WebSocket realtime protocol.
- message RealtimeServerFrame {
- oneof frame {
- // Confirms protocol version, server version, and capabilities.
- RealtimeServerHello hello = 1;
- // Confirms the live-event stream has started.
- RealtimeSubscribed subscribed = 2;
- // A live event authorized for the connected user.
- RealtimeEventEnvelope event = 3;
- // Application-level heartbeat used for liveness checks.
- RealtimeHeartbeat heartbeat = 4;
- // Protocol or authorization error.
- RealtimeError error = 5;
- // Server-requested close with reconnect guidance.
- RealtimeClose close = 6;
- // Reply to a client ping.
- RealtimePong pong = 7;
- }
- }
- // Initial client hello.
- message RealtimeClientHello {
- reserved 3;
- reserved "resume_cursor";
- // Protocol version requested by the client. The current version is 1.
- uint32 protocol_version = 1;
- // Optional bearer token. When present, it takes precedence over cookie auth.
- optional string bearer_token = 2;
- }
- // Initial server hello.
- message RealtimeServerHello {
- reserved 3;
- reserved "resume_supported";
- // Protocol version accepted by the server.
- uint32 protocol_version = 1;
- // Chatto server software version.
- string server_version = 2;
- // Approximate heartbeat interval clients should expect.
- uint32 heartbeat_interval_seconds = 4;
- // Stable protocol capability keys supported by this server. Current keys:
- // `chatto.realtime.events.live.v1`, `chatto.realtime.heartbeat.v1`, and
- // `chatto.realtime.ping.v1`.
- repeated string capabilities = 5;
- }
- // Request to start the caller's authorized live event stream.
- message RealtimeSubscribeEvents {}
- // Confirms event streaming has started.
- message RealtimeSubscribed {
- reserved 1;
- reserved "cursor";
- }
- // Application-level ping.
- message RealtimePing {
- // Client-chosen opaque value echoed in the pong.
- string nonce = 1;
- }
- // Application-level pong.
- message RealtimePong {
- // Echo of the ping nonce.
- string nonce = 1;
- }
- // Application-level heartbeat.
- message RealtimeHeartbeat {
- // Stable event ID for this heartbeat.
- string id = 1;
- // Time the heartbeat was emitted.
- google.protobuf.Timestamp created_at = 2;
- }
- // Protocol error.
- message RealtimeError {
- // Stable machine-readable error code.
- string code = 1;
- // Human-readable diagnostic message.
- string message = 2;
- // True when the server will close the socket after sending this error.
- bool fatal = 3;
- }
- // Close instruction sent before the socket is closed when possible.
- message RealtimeClose {
- // Stable machine-readable close code.
- string code = 1;
- // Human-readable diagnostic message.
- string message = 2;
- // True when the client should reconnect after a delay.
- bool reconnect = 3;
- // Suggested reconnect delay in milliseconds.
- uint32 retry_after_ms = 4;
- }
- // One authorized live event delivered over the realtime WebSocket.
- //
- // Realtime events are invalidation signals first: payloads carry stable IDs and
- // small inline hints, while durable resource state is hydrated through
- // chatto.api.v1 ConnectRPC services. Event-specific messages document the
- // intended hydration path when a referenced resource may need refreshing.
- message RealtimeEventEnvelope {
- reserved 4;
- reserved "cursor";
- // Stable event ID.
- string id = 1;
- // Time the event was created.
- google.protobuf.Timestamp created_at = 2;
- // User or system actor that caused the event, when known.
- optional string actor_id = 3;
- oneof event {
- // A message was posted in a room or thread.
- RealtimeMessagePostedEvent message_posted = 10;
- // A message body or renderable metadata changed.
- RealtimeMessageEditedEvent message_edited = 11;
- // A message was retracted.
- RealtimeMessageRetractedEvent message_retracted = 12;
- // A user added a reaction to a message.
- RealtimeReactionEvent reaction_added = 20;
- // A user removed a reaction from a message.
- RealtimeReactionEvent reaction_removed = 21;
- // A user is typing in a room or thread.
- RealtimeTypingEvent user_typing = 30;
- // A user's live presence changed.
- RealtimePresenceChangedEvent presence_changed = 31;
- // A room was created.
- RealtimeRoomEvent room_created = 40;
- // A room's profile or settings changed.
- RealtimeRoomEvent room_updated = 41;
- // A room was deleted.
- RealtimeRoomEvent room_deleted = 42;
- // A room was archived.
- RealtimeRoomEvent room_archived = 43;
- // A room was restored from the archive.
- RealtimeRoomEvent room_unarchived = 44;
- // A user joined a room visible to the current user.
- RealtimeRoomEvent user_joined_room = 45;
- // A user left a room visible to the current user.
- RealtimeRoomEvent user_left_room = 46;
- // A room's universal visibility flag changed.
- RealtimeRoomUniversalChangedEvent room_universal_changed = 47;
- // A notification was created for the current user.
- RealtimeNotificationCreatedEvent notification_created = 60;
- // A notification was dismissed for the current user.
- RealtimeNotificationDismissedEvent notification_dismissed = 61;
- // The current user's notification level changed.
- RealtimeNotificationLevelChangedEvent notification_level_changed = 62;
- // The current user's thread follow state changed.
- RealtimeThreadFollowChangedEvent thread_follow_changed = 63;
- // The current user's room read state changed.
- RealtimeRoomMarkedAsReadEvent room_marked_as_read = 64;
- // A thread was created in a room visible to the current user.
- RealtimeThreadCreatedEvent thread_created = 65;
- // The server profile/config changed.
- RealtimeServerUpdatedEvent server_updated = 70;
- // A user profile changed.
- RealtimeUserProfileUpdatedEvent user_profile_updated = 71;
- // A user's custom status was set or changed.
- RealtimeUserCustomStatusSetEvent user_custom_status_set = 72;
- // A user's custom status was cleared.
- RealtimeUserCustomStatusClearedEvent user_custom_status_cleared = 73;
- // The current user's display preferences changed.
- RealtimeServerUserPreferencesUpdatedEvent server_user_preferences_updated = 74;
- // Room-group layout changed.
- RealtimeRoomGroupsUpdatedEvent room_groups_updated = 75;
- // A server member was deleted.
- RealtimeServerMemberDeletedEvent server_member_deleted = 76;
- // Asset processing started for a message attachment.
- RealtimeAssetProcessingEvent asset_processing_started = 80;
- // Asset processing completed successfully for a message attachment.
- RealtimeAssetProcessingEvent asset_processing_succeeded = 81;
- // Asset processing failed for a message attachment.
- RealtimeAssetProcessingEvent asset_processing_failed = 82;
- // An asset attached to a message was deleted.
- RealtimeAssetDeletedEvent asset_deleted = 83;
- // A voice call started in a room.
- RealtimeCallEvent call_started = 84;
- // A participant joined a voice call.
- RealtimeCallEvent call_participant_joined = 85;
- // A participant left a voice call.
- RealtimeCallEvent call_participant_left = 86;
- // A voice call ended.
- RealtimeCallEvent call_ended = 87;
- // The current user was mentioned in a room.
- RealtimeMentionNotificationEvent mention_notification = 88;
- // The current user received a new direct message.
- RealtimeNewDirectMessageNotificationEvent new_direct_message_notification = 89;
- // The current user's session was terminated.
- RealtimeSessionTerminatedEvent session_terminated = 90;
- }
- }
- // Message-posted signal.
- //
- // Hydrate the affected timeline with `RoomService.GetRoomEventsAround`
- // for room messages, or `ThreadService.GetThreadEventsAround` when
- // `thread_root_event_id` is set.
- message RealtimeMessagePostedEvent {
- // Room containing the message.
- string room_id = 1;
- // Event ID of the posted message.
- string message_event_id = 2;
- // Root message event ID when the message was posted in a thread.
- optional string thread_root_event_id = 3;
- }
- // Message-edited signal.
- //
- // Refresh the affected timeline window with
- // `RoomService.GetRoomEventsAround` for room messages, or
- // `ThreadService.GetThreadEventsAround` when the local message belongs to
- // a thread.
- message RealtimeMessageEditedEvent {
- // Room containing the message.
- string room_id = 1;
- // Event ID of the edited message.
- string message_event_id = 2;
- }
- // Message-retracted signal.
- //
- // Refresh or patch the affected timeline window with
- // `RoomService.GetRoomEventsAround` for room messages, or
- // `ThreadService.GetThreadEventsAround` when the local message belongs to
- // a thread.
- message RealtimeMessageRetractedEvent {
- // Room containing the message.
- string room_id = 1;
- // Event ID of the retracted message.
- string message_event_id = 2;
- // Retraction reason, when one was supplied.
- optional string reason = 3;
- }
- // Reaction signal.
- //
- // Refresh or patch the affected message in its timeline window. Use
- // `RoomService.GetRoomEventsAround` for room messages, or
- // `ThreadService.GetThreadEventsAround` when the local message belongs to
- // a thread. `message_event_id` is canonical: reactions made through a channel
- // echo of a thread reply report the original reply event ID. Clients that show
- // channel echoes should also refresh or patch visible echo rows whose
- // `echo_of_event_id` matches this ID.
- message RealtimeReactionEvent {
- // Room containing the reacted-to message.
- string room_id = 1;
- // Canonical event ID of the reacted-to message.
- string message_event_id = 2;
- // Reaction emoji.
- string emoji = 3;
- }
- // Typing signal.
- //
- // This is an ephemeral signal. `room_id` and `thread_root_event_id` identify
- // where to display typing state; clients normally do not hydrate it.
- message RealtimeTypingEvent {
- // Room where the actor is typing.
- string room_id = 1;
- // Thread root event ID when the actor is typing in a thread.
- optional string thread_root_event_id = 2;
- }
- // Presence-changed signal.
- //
- // The latest presence status is inline. Use `UserService.GetUser` when
- // the surrounding user profile or custom status also needs refreshing.
- message RealtimePresenceChangedEvent {
- // User whose presence changed.
- string user_id = 1;
- // Latest presence status.
- chatto.api.v1.PresenceStatus status = 2;
- }
- // Room lifecycle or membership signal.
- //
- // Hydrate visible room state with `RoomDirectoryService.GetRoom` or
- // `RoomDirectoryService.BatchGetRooms`. Unknown/deleted rooms return
- // `NOT_FOUND` from singular reads, hidden rooms return `PERMISSION_DENIED`, and
- // batch reads omit unknown, deleted, hidden, and inaccessible rooms.
- message RealtimeRoomEvent {
- // Room affected by the event.
- string room_id = 1;
- }
- // Room universal-visibility signal.
- //
- // The latest universal flag is inline. Hydrate the rest of the visible room with
- // `RoomDirectoryService.GetRoom` or `RoomDirectoryService.BatchGetRooms`.
- message RealtimeRoomUniversalChangedEvent {
- // Room whose universal setting changed.
- string room_id = 1;
- // Latest universal setting.
- bool universal = 2;
- }
- // Notification-created signal for the connected user.
- //
- // Hydrate full notification rows with `NotificationService.GetNotification` or
- // `NotificationService.BatchGetNotifications`. Use `RoomService` or
- // `ThreadService` anchor reads when `event_id` or `in_reply_to_id` must be
- // opened in context.
- message RealtimeNotificationCreatedEvent {
- // Notification ID.
- string notification_id = 1;
- // Room associated with the notification, when the notification navigates to a room.
- optional string room_id = 2;
- // Event ID associated with the notification, when the notification navigates to an event.
- optional string event_id = 3;
- // Parent event ID for reply notifications, when known.
- optional string in_reply_to_id = 4;
- // True when the notification should not alert.
- bool silent = 5;
- }
- // Notification-dismissed signal for the connected user.
- //
- // Remove the local notification row by ID. A later singular notification read
- // returns `NOT_FOUND`; batch reads omit dismissed or missing notifications.
- message RealtimeNotificationDismissedEvent {
- // Notification ID.
- string notification_id = 1;
- }
- // Notification-level signal for the connected user.
- //
- // The updated level is inline. Use
- // `NotificationPreferencesService.GetRoomNotificationPreference` when clients
- // need the complete preference resource.
- message RealtimeNotificationLevelChangedEvent {
- // Room whose notification level changed.
- string room_id = 1;
- // Explicit room notification level.
- chatto.api.v1.NotificationLevel level = 2;
- // Effective notification level after defaults are applied.
- chatto.api.v1.NotificationLevel effective_level = 3;
- }
- // Thread-follow signal for the connected user.
- //
- // The updated follow state is inline. Use `ThreadService.GetThreadEvents`
- // or `ThreadService.GetThreadEventsAround` when the thread itself needs
- // refreshing.
- message RealtimeThreadFollowChangedEvent {
- // Room containing the thread.
- string room_id = 1;
- // Root message event ID of the thread.
- string thread_root_event_id = 2;
- // True when the user now follows the thread.
- bool following = 3;
- }
- // Thread-created signal.
- //
- // Hydrate the new thread with `ThreadService.GetThreadEventsAround` using
- // `thread_root_event_id` as the anchor.
- message RealtimeThreadCreatedEvent {
- // Room containing the thread.
- string room_id = 1;
- // Root message event ID that identifies the thread.
- string thread_root_event_id = 2;
- }
- // Room-read signal for the connected user.
- //
- // This is a current-user read-state invalidation signal. Clients can patch local
- // unread state for the room; no separate hydration is normally needed.
- message RealtimeRoomMarkedAsReadEvent {
- // Room marked read.
- string room_id = 1;
- }
- // Server-profile signal.
- //
- // Public server profile fields are inline. Use `ServerDiscoveryService.GetServer`
- // for public metadata and `ServerService.GetMotd` for the authenticated MOTD.
- message RealtimeServerUpdatedEvent {
- // Server display name.
- string name = 1;
- // Server description.
- string description = 2;
- // Server logo URL, when configured.
- optional string logo_url = 3;
- // Server banner URL, when configured.
- optional string banner_url = 4;
- }
- // User-profile signal.
- //
- // Basic profile fields are inline. Use `UserService.GetUser` or
- // `UserService.BatchGetUsers` for complete user-profile hydration.
- message RealtimeUserProfileUpdatedEvent {
- // User whose profile changed.
- string user_id = 1;
- // Login name.
- string login = 2;
- // Display name.
- string display_name = 3;
- // Avatar URL, when the user has an avatar.
- optional string avatar_url = 4;
- }
- // User-custom-status set signal.
- //
- // The latest custom status is inline. Use `UserService.GetUser` when
- // clients need to refresh the complete user profile.
- message RealtimeUserCustomStatusSetEvent {
- // User whose status changed.
- string user_id = 1;
- // Emoji part of the status.
- string emoji = 2;
- // Text part of the status.
- string text = 3;
- // Expiration time, when the status expires automatically.
- google.protobuf.Timestamp expires_at = 4;
- }
- // User-custom-status cleared signal.
- //
- // Clear local custom status for `user_id`. Use `UserService.GetUser`
- // when clients need to refresh the complete user profile.
- message RealtimeUserCustomStatusClearedEvent {
- // User whose status was cleared.
- string user_id = 1;
- }
- // User display-preferences signal for the connected user.
- //
- // The updated display preferences are inline for the connected user.
- message RealtimeServerUserPreferencesUpdatedEvent {
- // IANA timezone name, when the user has selected a fixed timezone.
- optional string timezone = 1;
- // Preferred time display format.
- chatto.api.v1.TimeFormat time_format = 2;
- }
- // Room-group layout signal.
- //
- // Refetch room-group layout with `RoomDirectoryService.ListRoomGroups`. If a
- // client already knows specific group IDs, it can use
- // `RoomDirectoryService.GetRoomGroup` or
- // `RoomDirectoryService.BatchGetRoomGroups`.
- message RealtimeRoomGroupsUpdatedEvent {
- // Always true; present for event-bus compatibility.
- bool changed = 1;
- }
- // Server-member deleted signal.
- //
- // Remove or invalidate local member/user rows for `user_id`. Admin clients can
- // hydrate remaining member rows through `AdminUserService.GetMember` or
- // `AdminUserService.BatchGetMembers`.
- message RealtimeServerMemberDeletedEvent {
- // Deleted user ID.
- string user_id = 1;
- }
- // Asset-processing signal.
- //
- // Message attachments are message-owned subresources. When
- // `message_event_id` is known, refetch message state with
- // `MessageService.GetMessage` or `MessageService.BatchGetMessages`; otherwise
- // refetch the affected attachment list or timeline window.
- message RealtimeAssetProcessingEvent {
- // Room containing the message that owns the asset, when known.
- optional string room_id = 1;
- // Asset ID.
- string asset_id = 2;
- // Event ID of the message that owns the asset, when known.
- optional string message_event_id = 3;
- }
- // Asset-deleted signal.
- //
- // Remove local attachment state by `asset_id`. When the owning message is known,
- // clients can refetch that message through `MessageService.GetMessage` or
- // refetch the timeline window.
- message RealtimeAssetDeletedEvent {
- // Room containing the message that owned the asset, when known.
- optional string room_id = 1;
- // Asset ID.
- string asset_id = 2;
- }
- // Voice-call state signal.
- //
- // Hydrate current room-scoped call state with `VoiceCallService.GetActiveCall`
- // or `VoiceCallService.BatchGetActiveCalls`. Compare `call_id` to ignore stale
- // transitions from a previous call in the same room.
- message RealtimeCallEvent {
- // Room containing the call.
- string room_id = 1;
- // Call ID.
- string call_id = 2;
- // Source that produced the call transition.
- RealtimeCallEventSource source = 3;
- }
- // Source that produced a realtime voice-call transition.
- enum RealtimeCallEventSource {
- // No call event source was specified.
- REALTIME_CALL_EVENT_SOURCE_UNSPECIFIED = 0;
- // The user explicitly requested the transition through Chatto.
- REALTIME_CALL_EVENT_SOURCE_USER = 1;
- // LiveKit reported the transition through a webhook.
- REALTIME_CALL_EVENT_SOURCE_LIVEKIT = 2;
- // Chatto reconciled its local state against LiveKit's current room state.
- REALTIME_CALL_EVENT_SOURCE_RECONCILIATION = 3;
- }
- // Mention attention signal for the connected user.
- //
- // Inline names are display hints. Hydrate referenced rooms through
- // `RoomDirectoryService.BatchGetRooms` and users through
- // `UserService.BatchGetUsers` when local caches are missing or stale.
- message RealtimeMentionNotificationEvent {
- // Room where the mention occurred.
- string room_id = 1;
- // Author user ID.
- string actor_user_id = 2;
- // Display name of the room where the mention occurred, when hydrated for the caller.
- optional string room_name = 3;
- // Display name of the author who mentioned the connected user, when hydrated.
- optional string actor_display_name = 4;
- }
- // New-DM attention signal for the connected user.
- //
- // Inline names and avatar URLs are display hints. Hydrate the DM room through
- // `RoomDirectoryService.GetRoom` or `RoomDirectoryService.BatchGetRooms`, and
- // the sender through `UserService.GetUser` or `UserService.BatchGetUsers` when
- // local caches are missing or stale.
- message RealtimeNewDirectMessageNotificationEvent {
- // DM room ID.
- string room_id = 1;
- // Sender user ID.
- string sender_id = 2;
- // Display name of the sender, when hydrated.
- optional string sender_display_name = 3;
- // Avatar URL of the sender, when one is available.
- optional string sender_avatar_url = 4;
- // Display name for the DM conversation from the connected user's perspective, when hydrated.
- optional string conversation_name = 5;
- }
- // Session-terminated signal for the connected user's current session.
- message RealtimeSessionTerminatedEvent {
- // Termination reason.
- string reason = 1;
- }
|