Skip to content

Instantly share code, notes, and snippets.

@ar-nelson
Created September 3, 2025 21:14
Show Gist options
  • Select an option

  • Save ar-nelson/75cb64f1f0986ef36d4754fd3115f5c1 to your computer and use it in GitHub Desktop.

Select an option

Save ar-nelson/75cb64f1f0986ef36d4754fd3115f5c1 to your computer and use it in GitHub Desktop.
Chatterbox API definition as of September 2025
syntax = "proto3";
package chatterbox.api.v1;
import "google/protobuf/timestamp.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/any.proto";
import "google/protobuf/descriptor.proto";
/*
* The Chatterbox API is a WebSocket protocol that performs RPC by sending `ChatterboxRequest`s and receiving `ChatterboxResponse`s.
*
* This protocol is designed with simplicity and ease of implementation as the highest priority.
* Consequently, it does not use Protobuf's service definition syntax (which would require a custom compilation step).
* Instead, it uses simple custom RPC: each `ChatterboxRequest` is replied to with at least one `ChatterboxResponse` with the same `id`.
*
* === STAGE 1: WELCOME ===
*
* After a client connects to a host's WebSocket endpoint, the host will send a `Welcome` message.
* The client should not send anything until it receives this message.
*/
message Welcome {
uint32 version = 1; // Major version of Chatterbox protocol
string host = 2;
optional string notice = 3;
bool federated = 4;
bool pubkey_registration = 5;
bool password_registration = 6;
bool email_required = 7;
repeated string registration_questions = 8;
}
/*
* === STAGE 2: AUTHENTICATION ===
*
* After receiving a `Welcome`, the client must send an `AuthRequest`.
* The server will reply with an `AuthResponse`, which may require more `AuthRequest`s.
* Once the server has sent an `AuthResponse` whose `payload` is `authenticated`,
* the connection progresses to stage 3, and the client may begin sending `ChatterboxRequest`s.
*
* Pubkey authentication flow:
* request `pubkey` -> response `pubkey_challenge` -> request `challenge_solution` -> response `authenticated`
*/
message AuthRequest {
uint64 id = 1;
oneof payload {
bytes token = 2;
Pubkey pubkey = 3;
Password password = 4;
ChallengeSolution challenge_solution = 5;
Register register = 6;
}
message Pubkey {
string user = 1;
optional string host = 2;
bytes pubkey = 3;
}
message Password {
string username = 1;
string password = 2;
}
message ChallengeSolution {
bytes nonce = 1;
optional bytes pow_suffix = 2;
}
message Register {
string name = 1;
optional string email = 2;
repeated string answers = 3;
oneof auth {
bytes pubkey = 4;
string password = 5;
}
}
}
message AuthResponse {
uint64 id = 1;
oneof payload {
google.protobuf.Empty authenticated = 2;
string error = 3;
PubkeyChallenge pubkey_challenge = 4;
}
message PubkeyChallenge {
bytes challenge = 1;
optional uint32 pow_difficulty = 2;
optional string pow_message = 3;
}
}
/*
* === STAGE 3: CHATTERBOX API ===
*
* Once authenticated, the client communicates by sending `ChatterboxRequest`s and receiving `ChatterboxResponse`s.
* The host MUST respond to each request with at least one response with the same `id`.
* Some requests can reply with streams, consisting of multiple responses with the same `id`.
* (Empty streams are represented by a single response of type `unit`.)
* Responses have a `state` field describing the state of the stream: DONE, ACTIVE, or WAITING.
* Single responses or ends of streams have a state of DONE.
* Active streams, which continue on their own unless closed, have a state of ACTIVE.
* Passive streams, which must be continued by the client, have a state of WAITING.
*/
message ChatterboxRequest {
uint64 id = 1;
oneof payload {
/// === 2-9: Stream Control ===
// When a `ChatterboxResponse` has a `state` of `STREAM_WAITING`, the client must send this request to continue the stream.
// Value is the `id` of the `ChatterboxResponse` stream to continue. Response is `unit`.
uint64 continue_stream = 2;
// When a `ChatterboxResponse` is an open stream (`state` is not `STREAM_DONE`), the client may send this request to close the stream early.
// A stream closed this way will send one more response, of type `error`.
// Value is the `id` of the `ChatterboxResponse` stream to close. Response is `unit`.
uint64 close_stream = 3;
/// === 10-19: Common Host Functions ===
// Get generic information about the host. Response is `host_info`.
google.protobuf.Empty host_get_info = 10;
// List all signed statements about a user, optionally filtered by type.
// Response is a stream of `statement`.
HostGetStatements host_get_statements = 11;
// Publish a signed statement to this host.
// This call is used by users to publish their own statements, as well as by other servers to propagate statements.
// Users with custodial private keys must use `current_user_sign_and_publish_statement` instead.
// The host MUST act upon a valid statement upon receiving it, updating users' keys, names, etc. accordingly.
// If the statement is invalid (bad signature, signed for the wrong user, etc.), the call MUST fail with an error.
// Response is `unit`.
SignedStatement host_publish_statement = 12;
// Invite a local user to a DM conversation.
// DMs and group chats occur in the "zero server", a server with an all-0 UUID that always exists on each host.
// The user will receive a notification in the zero server and have the option to accept or reject the invite.
// Response is `unit`.
Identifier host_dm_invite = 13;
// Respond to a DM invite, with the UUID of an already-created DM room.
// This should be called on BOTH the invite sender's host and the invite recipient's host.
// DM rooms can be created with `room_get_dm_room`.
// If `room_uuid` is present, the invite was accepted; if the room does not already exist, it will be created.
// If `room_uuid` is absent, the invite was rejected.
// Cross-host DMs create rooms with the same UUID on each host, and sync them via their room event streams.
// The host SHOULD enforce that the newly created room's UUIDv7 timestamp is recent, to avoid collisions.
// Response is `unit`.
HostDMResponse host_dm_respond_to_invite = 14;
// Invite a local user to a group chat.
// Group chats occur in the "zero server", a server with an all-0 UUID that always exists on each host.
// The invite will create an empty local copy of the group room if it does not already exist.
// The user will receive a notification in the zero server and have the option to accept or reject the invite.
// Response is `unit`.
HostGroupInvite host_group_invite = 15;
// Respond to a group chat invite; the `accept` field denotes whether the invite was accepted.
// This should be called on BOTH the invite sender's host and the invite recipient's host.
// If the invite was accepted, the host should begin syncing the group room's events with other members' hosts.
// Response is `unit`.
HostGroupResponse host_group_respond_to_invite = 16;
/// === 20-29: Admin Functions ===
// Set the `HostRole` of a user. Used to promote or moderate users for the entire host.
// Setting a user to `HOST_ROLE_BANNED` will automatically kick them from the host.
// The `until` field sets a timeout to return to `HOST_ROLE_USER`, but only for certain roles:
// `HOST_ROLE_BANNED`, `HOST_ROLE_QUARANTINED`
// Response is `unit`.
AdminUserSetRole admin_user_set_role = 20;
// Purge a user from the host
// This deletes all data associated with the user, including all messages and uploaded media.
// Response is `unit`.
Identifier admin_user_purge = 21;
// List all known users on the host, optionally filtered by name, host, role, and join time.
// This includes any user that has ever connected, even if the user has not joined a server.
// Response is a stream of `user`.
AdminUserList admin_user_list = 22;
/// === 30-39: Host List Management ===
// A host list is simply a list of strings (domain names) representing hosts.
// All local users have host lists for joined servers and blocked servers.
// Additionally, admins can set host lists for federation allow and block lists.
// Get all hosts in a given host list.
// Response is `host_list`. Note that, despite being a list, this is a single non-stream response.
HostListType host_list_get = 30;
// Add a host to a host list. If it already exists, this does nothing.
// Hosts MUST check that `host` is a valid domain name, and fail with an error if it is not.
// Hosts MUST normalize `host` to lowercase and convert Unicode domains to Punycode.
// Response is `unit`.
HostListEntry host_list_add = 31;
// Remove a host from a host list. If it does not exist, this does nothing.
// Hosts MUST normalize `host` to lowercase and convert Unicode domains to Punycode.
// Response is `unit`.
HostListEntry host_list_remove = 32;
/// === 40-49: File Management ===
/// Users may upload files to the host. Files have a few uses: icons, emoji packs, and attachments.
/// Files are identified by a `FileType` and a BLAKE3 hash of their contents.
/// Hosts SHOULD limit image formats to PNG and JPEG, audio formats to Opus, and video formats to WebM+VP9+Opus.
/// Conversion to these formats should happen in the client.
/// Hosts MAY limit file sizes arbitrarily and delete unused files after a period of time.
// Start a new file upload. ONLY ONE upload may be in progress at a time per WebSocket.
// Once this call returns a `unit` response, the client may send `file_upload_chunk` requests.
// When the upload is complete, this call will return the file hash as a second response (type `binary`).
// Attempting to start an upload while another is in progress will cause both to fail with an error.
// Response is a stream of two entries (`unit`, then `binary`).
FileBeginUpload file_begin_upload = 40;
// Upload a chunk of a file, if an upload is currently in progress (see `file_begin_upload`).
// Response is `unit`.
FileChunk file_upload_chunk = 41;
// Download a file by hash and type. Response is a stream of `file_chunk`.
FileDownload file_download = 42;
// Delete a file by hash and type. Users can only delete their own files; host admins can delete any file.
// Response is `unit`.
FileDownload file_delete = 43;
/// === 100-199: Current User Functions ===
// Open a stream of `UserEvent`s for the current user.
// If `since` is present, the stream will start with all past events after `since` before continuing with current events.
// The host MAY delete old events, but, if any events after `since` have been deleted, this call MUST fail with an error.
// Response is an active stream of `UserEvent`s.
CurrentUserEventStream current_user_event_stream = 100;
// When the user has a custodial private key, this request is used to sign auth challenges from other hosts.
// This MUST fail with an error if the user does not have a custodial private key.
// Response is `binary`.
bytes current_user_sign_challenge = 101;
// Get miscellaneous information about the current user. Response is `current_user_state`.
google.protobuf.Empty current_user_get_state = 102;
// Get the current user's profile for a given server. Response is `user`.
bytes current_user_get_server_member = 103;
// Set the current user's status message, with an optional timeout.
// If `server_uuids` is absent, the status is set for all servers, and becomes the default for new servers.
// Response is `unit`.
CurrentUserSetStatus current_user_set_status = 104;
// Updates some fields of the current user's profile.
// If `server_uuids` is absent, the fields are set for all servers, and become the default for new servers.
// Response is `unit`.
CurrentUserUpdateServerMember current_user_update_server_member = 105;
// Delete the current user.
// This will kick the current user from the host, ending the WebSocket connection.
// Unlike `current_user_purge`, this does not delete messages or uploaded media.
// No response, because this closes the connection.
google.protobuf.Empty current_user_delete = 106;
// Delete the current user and all past messages and uploaded media.
// This will kick the current user from the host, ending the WebSocket connection.
// No response, because this closes the connection.
google.protobuf.Empty current_user_purge = 107;
// Publish a statement about the current user to the host.
// If the statement is not about the current user, the call MUST fail with an error.
// The host should sign the statement with the custodial private key.
// Afterward, it should publish the statement in the same manner as `host_publish_statement`.
// Response is `statement`. (the signed statement)
Statement current_user_sign_and_publish_statement = 108;
// Set which events cause notifications for a given server or room. Response is `unit`.
CurrentUserUpdateNotifyMode current_user_update_notify_mode = 109;
/// === 200-299: Server Functions ===
/// In Chatterbox terminology, a "server" does not refer to a specific instance of the backend software.
/// Instead, a server is a collection of users and rooms. A host (instance) may contain multiple servers.
/// This is similar to what Discord calls a "server", or what Matrix calls a "space".
/// Servers are identified by a UUID, and may be replicated across multiple hosts.
// Open a stream of `ServerEvent`s for a given server.
// If `since` is present, the stream will start with all past events after `since` before continuing with current events.
// The host MAY delete old events, but, if any events after `since` have been deleted, this call MUST fail with an error.
// Response is an active stream of `ServerEvent`s.
ServerEventStream server_event_stream = 200;
// Create a new server on this host. Response is `binary` (the server UUID).
ServerCreate server_create = 201;
// Get a server by UUID, including its list of visible rooms. Response is `server`.
bytes server_get = 202;
// List all known/public servers on this host. Response is a stream of `server`.
ServerList server_list = 203;
// Update some metadata fields of a server. Response is `unit`.
ServerUpdate server_update = 204;
// Delete a server. Note that this is a destructive operation and cannot be undone.
// Response is `unit`.
bytes server_delete = 205;
// Join a server. If the current user is already a member, this does nothing.
// If the current user has role `SERVER_ROLE_VISITOR`, this will upgrade it to `SERVER_ROLE_MEMBER`.
// Response is `unit`.
bytes server_join = 206;
// Leave a server. If the current user is not a member, this does nothing.
// Can also be used to remove oneself from invited servers where the user has role `SERVER_ROLE_VISITOR`.
// Response is `unit`.
bytes server_leave = 207;
// Invite a user to a server.
// The invitation appears as a notification to the user, and the user is added to the server with role `SERVER_ROLE_VISITOR`.
// Response is `unit`.
ServerInvite server_invite = 208;
// Get a specific server member's profile, identified by `name@host`.
// Response is `user`.
ServerMemberGet server_member_get = 209;
// List all members of a server, optionally filtered by name, host, role, and join time.
// Users with roles below `SERVER_ROLE_MODERATOR` may not be able to see invited visitors.
// Response is a stream of `user`.
ServerMemberList server_member_list = 210;
// Set the `ServerRole` of a user. Used to promote or moderate users for a given server.
// Only usable by host role >= `HOST_ROLE_ADMIN` or server role >= `SERVER_ROLE_MODERATOR`.
// Setting a user to `SERVER_ROLE_BANNED` will automatically kick them from the server.
// The `until` field sets a timeout to return to `SERVER_ROLE_MEMBER`, but only for certain roles:
// `SERVER_ROLE_BANNED`, `SERVER_ROLE_MUTED`.
// Response is `unit`.
ServerMemberSetRole server_member_set_role = 211;
// List past notifications for a server. Response is a stream of `notification`.
ServerNotificationList server_notification_list = 212;
// Mark a notification as read. Response is `unit`.
ServerNotificationMarkRead server_notification_mark_read = 213;
// List all emoji packs for a server (value is server UUID).
// Response is a stream of `emoji_pack`.
bytes server_emoji_pack_list = 214;
// Import an emoji pack from a file and add it to this server.
// Only usable by host role >= `HOST_ROLE_ADMIN` or server role >= `SERVER_ROLE_MODERATOR`.
// The file must be a ZIP file uploaded with type `FILE_TYPE_EMOJI_PACK`.
// The ZIP file must contain only PNG files; each PNG file's name (minus extension) is its shortcode.
// Clients SHOULD support animated PNGs and SHOULD convert GIFs in uploaded emoji pack ZIPs to PNGs.
// Response is `unit`.
ServerEmojiPackImport server_emoji_pack_import = 215;
// Delete an emoji pack by name. If the pack does not exist, this does nothing.
// Only usable by host role >= `HOST_ROLE_ADMIN` or server role >= `SERVER_ROLE_MODERATOR`.
// Does not delete the file the pack is based on, though the server may delete it after a period of time.
// Note that this may break custom emojis in existing messages.
// Response is `unit`.
ServerEmojiPackDelete server_emoji_pack_delete = 216;
/// === 300-399: Room Functions ===
// Open a stream of `RoomEvent`s for a given room.
// If `since` is present, the stream will start with all past events after `since` before continuing with current events.
// The host MAY delete old events, but, if any events after `since` have been deleted, this call MUST fail with an error.
// Response is an active stream of `RoomEvent`s.
RoomEventStream room_event_stream = 300;
// Create a new room in a given server.
// Only usable by host role >= `HOST_ROLE_ADMIN` or server role >= `SERVER_ROLE_MODERATOR`.
// If the new room is public, all users in the server will be automatically added to the room.
// Response is `binary` (the room UUID).
RoomCreate room_create = 301;
// Get a room by UUID. The current user must have permission to view the room. Response is `room`.
bytes room_get = 302;
// Update some metadata fields of a room.
// Only usable by host role >= `HOST_ROLE_ADMIN` or server role >= `SERVER_ROLE_MODERATOR`.
// Response is `unit`.
RoomUpdate room_update = 303;
// Update the topic of a room.
// Only usable by host role >= `HOST_ROLE_ADMIN` or server role >= `SERVER_ROLE_MODERATOR`.
// This sends an ephemeral event to the room that includes the current user's name.
// Response is `unit`.
StringUpdate room_update_topic = 304;
// Delete a room. Note that this is a destructive operation and cannot be undone.
// Only usable by host role >= `HOST_ROLE_ADMIN` or server role >= `SERVER_ROLE_MODERATOR`.
// Response is `unit`.
bytes room_delete = 305;
// Join a private room. If the current user is already a member, this does nothing.
// The current user must have been invited to the room, or must be a moderator.
// Response is `unit`.
bytes room_join = 306;
// Leave a private room. If the current user is not a member, this does nothing.
// The current user retains the ability to view and rejoin the room, unless a moderator calls `room_kick`.
// Response is `unit`.
bytes room_leave = 307;
// Invite a user to a private room.
// The current user must already be a member of the room.
// The invitation appears as a notification to the user, and the user gains permission to view the room.
// Permission to view the room remains until a moderator calls `room_kick`.
// Response is `unit`.
RoomInvite room_invite = 308;
// Kick a user from a private room.
// Only usable by host role >= `HOST_ROLE_ADMIN` or server role >= `SERVER_ROLE_MODERATOR`.
// The user loses permission to view the room, and must be re-invited to join.
// Response is `unit`.
RoomInvite room_kick = 309;
// Get a specific room member's profile, identified by `name@host`.
// Response is `user`.
RoomMemberGet room_member_get = 310;
// List all members of a private room, optionally filtered by name, host, role, and join time.
// Users with roles below `SERVER_ROLE_MODERATOR` may not be able to see invited visitors.
// Response is a stream of `user`.
RoomMemberList room_member_list = 311;
// Get the direct message room for a given user, creating it if it does not exist.
// Only works if the current user is local. The DM room is always in the zero server.
// Response is `binary` (room UUID).
Identifier room_get_dm_room = 312;
// Sends a "User is typing" event for the current user to the given room.
// Typing notifications last for 5 seconds or until a message is sent.
// Clients should debounce key events to roughly once per second when sending repeated requests.
// Response is `unit`.
bytes room_notify_typing = 313;
// Get the last-read markers for up to the given number of users in the given room and thread.
// Should try to intellegently choose users who are recently active.
// Response is `last_read`.
RoomLastReadGet room_last_read_get = 314;
// Set the last-read marker for the current user in the given room and thread.
// Last read is cumulative; reading a message implies that you have read all previous messages in the thread.
// Response is `unit`.
RoomLastReadSet room_last_read_set = 315;
/// === 400-499: Message Functions ===
// Send a message to a room.
// Response is `binary` (the message UUID).
MessageSend message_create = 400;
// Send a command in a room.
// Commands are messages starting with /. They are only visible to the sender and the app that receives them.
// The command is handled by the app's owner, which is usually a bot.
// The command's prefix must be defined in one of the apps in the room's server.
// Response is `binary` (the event UUID).
MessageCommandSend message_command_send = 401;
// Send an action to an app.
// Actions are defined as buttons or other controls on annotations.
// Interacting with these controls sends actions, which are handled by the user that owns the app.
// Response is `binary` (the event UUID).
MessageActionSend message_action_send = 402;
// Get a message by UUID. The current user must have permission to view the message.
// Response is `message`.
bytes message_get = 403;
// List a subset of the message history of a room or thread.
// Used to reconstruct history when joining a room or when `room_event_stream` does not go back far enough.
// Includes only messages in their most recent form, not additional events like join/leave, edit, delete, etc.
// The response may be different between users: annotations hidden from the current user will not be included.
// Response is a stream of `message`.
MessageListHistory message_list_history = 404;
// Update specific fields of a message.
// The current user must be the message's sender or a moderator.
// Response is `unit`.
MessageUpdate message_update = 405;
// Delete a message.
// The current user must be the message's sender or a moderator.
// Response is `unit`.
bytes message_delete = 406;
// React to a message with an emoji. If a matching reaction already exists, does nothing.
// Response is `unit`.
MessageReact message_react = 407;
// Remove an emoji reaction from a message. If no such reaction exists, does nothing.
// The current user must be the reaction's sender or a moderator.
// Response is `unit`.
MessageReact message_unreact = 408;
/// === 500-599: App and Annotation Functions ===
/// Chatterbox supports plugin-like functionality via apps and bots.
/// A user with host role >= `HOST_ROLE_BOT` can own one or more apps.
/// The owner of an app can attach annotations to messages, and can receive commands and actions from users.
/// Servers can activate an app by inviting the app's owning bot user.
/// Bots can use this functionality to add features like link previews, automoderation, and more.
// Create a new app, assigned to a specific bot user.
// The app should have a unique name. App names are case-insensitive and should be in reverse-URL format, e.g. "com.example.app".
// The current user must have host role >= `HOST_ROLE_ADMIN`.
// Response is `unit`.
AppDescription app_create = 500;
// Look up an app by name.
// Response is `app`.
string app_get = 501;
// List all apps available on this host.
// Response is a stream of `app`.
AppList app_list = 502;
// Update some fields of an app's metadata.
// The current user must have host role >= `HOST_ROLE_ADMIN`.
// Response is `unit`.
AppUpdate app_update = 503;
// Delete an app by (case-insensitive) name.
// The current user must have host role >= `HOST_ROLE_ADMIN`.
// Response is `unit`.
string app_delete = 504;
// Create an annotation associated with an app and attach it to a message.
// Only the owner of an app can create annotations for that app.
// Response is `binary` (the annotation UUID).
AppAnnotationCreate app_annotation_create = 510;
// Send an annotation message to a room.
// An annotation message is a message containing only an annotation, no content.
// The message is only visible to the users who can see the annotation.
// Only the owner of an app can send annotation messages for that app.
// Response is `binary` (the message and annotation UUIDs. which are the same).
AppAnnotationMessageSend app_annotation_message_send = 511;
// Get an annotation by UUID. The current user must have permission to view the annotation.
// Response is `annotation`.
bytes app_annotation_get = 512;
// Update some fields of an annotation.
// Only the owner of an app can update annotations for that app.
// Response is `unit`.
AppAnnotationUpdate app_annotation_update = 513;
// Delete an annotation by UUID.
// Annotations can be deleted by the owner of the annotation's app, the owner of the annotated message, and moderators.
// Response is `unit`.
bytes app_annotation_delete = 514;
};
message HostGetStatements {
Identifier user = 1;
repeated StatementType types = 2;
}
message HostDMResponse {
Identifier inviter = 1;
optional bytes room_uuid = 2;
}
message HostGroupInvite {
Identifier recipient = 1;
bytes room_uuid = 2;
repeated string group_hosts = 3;
string display_name = 4;
optional string topic = 5;
}
message HostGroupResponse {
Identifier inviter = 1;
bytes room_uuid = 2;
bool accept = 3;
}
message AdminUserSetRole {
Identifier user = 1;
HostRole role = 2;
optional google.protobuf.Timestamp until = 3;
optional string reason = 4;
}
message AdminUserList {
optional string name_match = 1;
optional string email_match = 2;
optional string host_match = 3;
optional bool local = 4;
optional google.protobuf.Timestamp joined_before = 5;
optional google.protobuf.Timestamp joined_after = 6;
repeated HostRole roles = 7;
}
message CurrentUserEventStream {
optional google.protobuf.Timestamp since = 1;
}
message CurrentUserSetStatus {
repeated bytes server_uuids = 1;
UserStatus status = 2;
optional string message = 3;
optional EmojiReference emoji = 4;
optional google.protobuf.Timestamp until = 5;
}
message CurrentUserUpdateServerMember {
repeated bytes server_uuids = 1;
optional StringUpdate display_name = 2;
optional FileReferenceUpdate icon = 3;
optional StringUpdate bio = 4;
}
message CurrentUserUpdateNotifyMode {
oneof subject {
bytes server_uuid = 1;
bytes room_uuid = 2;
};
NotifyMode notify_mode = 3;
}
message HostListEntry {
HostListType list = 1;
string host = 2;
}
message FileBeginUpload {
FileType type = 1;
optional string mime_type = 2;
uint64 size_in_bytes = 3;
}
message FileDownload {
bytes hash = 1;
optional FileType type = 2;
}
message ServerEventStream {
bytes server_uuid = 1;
optional google.protobuf.Timestamp since = 2;
}
message ServerCreate {
string display_name = 1;
optional string description = 2;
optional string rules = 3;
optional FileReference icon = 4;
bool private = 5;
bool anyone_can_invite = 6;
repeated string languages = 7;
}
message ServerList {
Sort sort = 1;
bool ascending = 2;
optional string filter = 3;
enum Sort {
SERVER_SORT_NAME = 0;
SERVER_SORT_MEMBERS = 1;
SERVER_SORT_CREATED_AT = 2;
SERVER_SORT_LAST_ACTIVE = 3;
}
}
message ServerUpdate {
bytes server_uuid = 1;
optional StringUpdate display_name = 2;
optional StringUpdate description = 3;
optional StringUpdate rules = 4;
optional FileReferenceUpdate icon = 5;
optional bool private = 6;
optional bool anyone_can_invite = 7;
optional StringList languages = 8;
}
message ServerInvite {
bytes server_uuid = 1;
Identifier user = 2;
optional string message = 3;
}
message ServerMemberGet {
bytes server_uuid = 1;
Identifier user = 2;
}
message ServerMemberList {
bytes server_uuid = 1;
optional string name_match = 2;
optional string host_match = 3;
optional google.protobuf.Timestamp joined_before = 4;
optional google.protobuf.Timestamp joined_after = 5;
repeated ServerRole roles = 6;
}
message ServerNotificationList {
bytes server_uuid = 1;
optional google.protobuf.Timestamp since = 2;
bool unread_only = 3;
repeated NotificationType types = 4;
}
message ServerNotificationMarkRead {
bytes server_uuid = 1;
bytes notification_uuid = 2;
}
message ServerMemberSetRole {
bytes server_uuid = 1;
Identifier user = 2;
ServerRole role = 3;
optional google.protobuf.Timestamp until = 4;
optional string reason = 5;
}
message ServerEmojiPackImport {
bytes server_uuid = 1;
string name = 2;
bytes file_hash = 3;
}
message ServerEmojiPackDelete {
bytes server_uuid = 1;
string name = 2;
}
message RoomEventStream {
bytes room_uuid = 1;
optional google.protobuf.Timestamp since = 2;
}
message RoomCreate {
bytes server_uuid = 1;
string display_name = 2;
RoomType type = 3;
bool private = 4;
optional string topic = 5;
optional string category = 6;
optional google.protobuf.FileDescriptorSet custom_fields_descriptor = 7;
optional FileReference icon = 8;
optional int32 sort_order = 9;
repeated Identifier group_members = 10;
}
message RoomUpdate {
bytes room_uuid = 1;
optional string display_name = 2;
optional bool private = 3;
optional StringUpdate category = 4;
optional int32 sort_order = 5;
optional FileReferenceUpdate icon = 6;
}
message RoomInvite {
bytes room_uuid = 1;
Identifier user = 2;
optional string message = 3;
}
message RoomMemberGet {
bytes room_uuid = 1;
Identifier user = 2;
}
message RoomMemberList {
bytes room_uuid = 1;
optional string name_match = 2;
optional string host_match = 3;
optional google.protobuf.Timestamp joined_before = 4;
optional google.protobuf.Timestamp joined_after = 5;
repeated ServerRole roles = 6;
}
message RoomLastReadSet {
bytes room_uuid = 1;
bytes message_uuid = 2;
}
message RoomLastReadGet {
bytes room_uuid = 1;
optional bytes thread_uuid = 2;
optional google.protobuf.Timestamp since = 3;
optional uint32 limit = 4;
}
message MessageSend {
bytes room_uuid = 1;
optional bytes thread_uuid = 2;
optional bytes in_reply_to_message_uuid = 3;
bool top_level = 4;
string content = 5;
optional string spoiler = 6;
repeated google.protobuf.Any custom_fields = 7;
repeated Attachment attachments = 8;
}
message MessageCommandSend {
bytes room_uuid = 1;
optional bytes thread_uuid = 2;
string prefix = 3;
string suffix = 4;
}
message MessageActionSend {
bytes annotation_uuid = 1;
string action = 2;
repeated string arguments = 3;
}
message MessageListHistory {
bytes room_uuid = 1;
optional bytes thread_uuid = 2;
optional bytes start = 3;
bool inclusive = 4;
bool ascending = 5;
}
message MessageUpdate {
bytes message_uuid = 1;
optional bool top_level = 2;
optional string content = 3;
optional StringUpdate spoiler = 4;
optional AttachementsUpdate attachments = 5;
message AttachementsUpdate {
repeated Attachment attachments = 1;
}
}
message MessageReact {
bytes message_uuid = 1;
oneof emoji {
string unicode = 2;
string custom = 3;
}
}
message AppList {
optional Identifier owner = 1;
optional string name_match = 2;
optional string metadata_match = 3;
}
message AppUpdate {
string name = 1;
optional string display_name = 2;
optional Identifier owner = 3;
optional string description = 4;
optional StringUpdate link = 5;
optional FileReferenceUpdate icon = 6;
optional CommandsUpdate commands = 7;
message CommandsUpdate {
repeated CommandDescription commands = 1;
}
}
message AppAnnotationCreate {
string app = 1;
bytes message_uuid = 2;
optional string click_anywhere_link = 3;
optional string text_content = 4;
optional string html_embed = 5;
repeated google.protobuf.Any custom_fields = 6;
repeated Attachment attachments = 7;
repeated Action actions = 8;
repeated Identifier visible_to = 9;
}
message AppAnnotationMessageSend {
string app = 1;
bytes room_uuid = 2;
optional bytes thread_uuid = 3;
optional bytes in_reply_to_message_uuid = 4;
optional string click_anywhere_link = 5;
optional string text_content = 6;
optional string html_embed = 7;
repeated google.protobuf.Any custom_fields = 8;
repeated Attachment attachments = 9;
repeated Action actions = 10;
repeated Identifier visible_to = 11;
}
message AppAnnotationUpdate {
bytes annotation_uuid = 1;
optional string click_anywhere_link = 2;
optional string text_content = 3;
optional string html_embed = 4;
repeated google.protobuf.Any custom_fields = 5;
repeated Attachment attachments = 6;
repeated Action actions = 7;
repeated Identifier visible_to = 8;
}
}
message ChatterboxResponse {
uint64 id = 1;
StreamState state = 2;
oneof payload {
google.protobuf.Empty unit = 3;
Error error = 4;
bytes binary = 5;
UserEvent user_event = 6;
ServerEvent server_event = 7;
RoomEvent room_event = 8;
HostInfo host_info = 10;
CurrentUserState current_user_state = 11;
Statement statement = 12;
StringList host_list = 13;
FileChunk file_chunk = 14;
User user = 15;
ServerDetail server = 16;
RoomDetail room = 17;
EmojiPack emoji_pack = 18;
Notification notification = 19;
Message message = 20;
LastRead last_read = 21;
AppDescription app = 22;
}
enum StreamState {
STREAM_DONE = 0;
STREAM_ACTIVE = 1;
STREAM_WAITING = 2;
}
enum ErrorType {
// 0: Covers all errors that don't fit other types
ERROR_UNKNOWN = 0;
/// 1X errors: protocol errors
// 10: ID of a ChatterboxRequest is already used or out of range
ERROR_BAD_ID = 10;
// 11: continue_stream or close_stream refers to closed or invalid stream
ERROR_BAD_STREAM = 11;
// 12: streams send this when closed abnormally or by close_stream
ERROR_STREAM_CLOSED = 12;
// 13: streams send this when left open long enough to auto-timeout
ERROR_STREAM_TIMEOUT = 13;
/// 2X errors: non-retryable request errors
// 20: request is malformed or invalid
ERROR_BAD_REQUEST = 20;
// 21: feature is not implemented by this host
ERROR_NOT_IMPLEMENTED = 21;
// 22: user is not authorized to perform this action
ERROR_FORBIDDEN = 22;
// 23: some resource (user, server, room, message, etc.) is not found
ERROR_NOT_FOUND = 23;
/// 3X errors: retryable request errors
// 30: unspecified spurious host error (use ERROR_UNKNOWN for true unanticipated errors)
ERROR_HOST_FAILURE = 30;
// 31: rate limited, try again later
ERROR_RATE_LIMITED = 31;
}
message Error {
ErrorType type = 1;
optional string message = 2;
}
message HostInfo {
uint32 version = 1;
string host = 2;
optional string name = 3;
optional string rules = 4;
optional bytes default_server_uuid = 5;
optional string voice_platform = 6;
optional string voice_host = 7;
bool open_registration = 8;
bool open_federation = 9;
bool anyone_can_create_servers = 10;
bool anyone_can_create_public_servers = 11;
bool can_send_email = 12;
uint32 user_count = 13;
uint32 server_count = 14;
uint32 event_expiration_days = 15;
repeated string registration_questions = 16;
repeated string languages = 17;
repeated Identifier admins = 18;
}
message CurrentUserState {
Identifier user = 1;
bytes pubkey = 2;
bool local_account = 3;
bool custodial_private_key = 4;
repeated bytes joined_local_servers = 5;
}
message ServerDetail {
Server server = 1;
bool joined = 2;
bool has_unread = 3;
int32 notification_count = 4;
NotifyMode notify_mode = 5;
int32 members = 6;
repeated string room_categories = 7;
repeated bytes room_uuids = 8;
repeated string apps = 9;
}
message RoomDetail {
Room room = 1;
bool joined = 2;
bool has_unread = 3;
int32 notification_count = 4;
NotifyMode notify_mode = 5;
int32 sort_order = 6;
optional string category = 7;
google.protobuf.Timestamp last_active = 8;
int32 members = 9;
}
message LastReadEntry {
repeated LastRead entries = 1;
}
message LastRead {
Identifier user = 1;
bytes message_uuid = 2;
}
message EmojiPack {
string name = 1;
repeated CustomEmojiReference emoji = 2;
}
}
/*
* === EVENTS ===
*/
message UserEvent {
bytes uuid = 1;
oneof event {
StatementEvent statement_published = 2;
ServerReferenceEvent server_invite = 3;
ServerReferenceEvent server_joined = 4;
ServerReferenceEvent server_left = 5;
ServerConfigEvent server_config = 6;
RoomReferenceEvent room_invite = 7;
RoomReferenceEvent room_joined = 8;
RoomReferenceEvent room_left = 9;
RoomConfigEvent room_config = 10;
NotificationEvent notification = 11;
HostRoleUpdatedEvent host_role_updated = 12;
}
}
message ServerEvent {
bytes uuid = 1;
oneof event {
RoomAddedEvent room_added = 2;
RoomAddedEvent room_updated = 3;
RoomDeletedEvent room_deleted = 4;
Server server_updated = 5;
UserJoinedEvent user_joined = 6;
UserLeftEvent user_left = 7;
UserUpdatedEvent user_updated = 8;
UserStatusUpdatedEvent user_status_updated = 9;
MemberRoleUpdatedEvent member_role_updated = 10;
StatementEvent user_statement_published = 11;
}
}
message RoomEvent {
bytes uuid = 1;
oneof event {
Message message_created = 2;
MessageUpdated message_updated = 3;
MessageDeleted message_deleted = 4;
ReactionReference reaction_created = 5;
ReactionReference reaction_deleted = 6;
MessageAnnotated message_annotated = 7;
Annotation annotation_updated = 8;
bytes annotation_deleted = 9;
CommandEvent command = 10;
ActionEvent action = 11;
UserJoinedEvent user_joined = 12;
UserLeftEvent user_left = 13;
Identifier user_typing = 14;
LastReadUpdated last_read_updated = 15;
}
message MessageUpdated {
bytes message_uuid = 1;
Identifier updated_by = 2;
optional string reason = 3;
optional string content = 4; // only absent for annotation messages
optional string spoiler = 5;
repeated google.protobuf.Any custom_fields = 6;
repeated Attachment attachments = 7;
repeated Identifier mentions = 8;
}
message MessageDeleted {
bytes message_uuid = 1;
Identifier deleted_by = 2;
optional string reason = 3;
}
message ReactionReference {
bytes message_uuid = 1;
Reaction reaction = 2;
}
message MessageAnnotated {
bytes message_uuid = 1;
Annotation annotation = 2;
}
message CommandEvent {
Identifier sender = 1;
optional bytes thread_uuid = 2;
string prefix = 3;
string suffix = 4;
}
message ActionEvent {
Identifier sender = 1;
bytes annotation_uuid = 2;
string action = 3;
repeated string arguments = 4;
}
message LastReadUpdated {
Identifier user = 1;
optional bytes thread_uuid = 2;
bytes read_message_uuid = 3;
}
}
message NotificationEvent {
bytes server_uuid = 1;
Notification notification = 2;
}
message StatementEvent {
Identifier user = 1;
SignedStatement statement = 2;
google.protobuf.Timestamp published_at = 3;
}
message RoomAddedEvent {
Room room = 1;
int32 sort_order = 2;
optional string category = 3;
}
message RoomUpdatedEvent {
Room room = 1;
optional Identifier updated_by = 2;
}
message RoomDeletedEvent { bytes uuid = 1; }
message UserJoinedEvent {
Identifier id = 1;
User user = 2;
}
message UserLeftEvent { Identifier id = 1; }
message UserUpdatedEvent {
Identifier id = 1;
optional string display_name = 2;
optional FileReference icon = 3;
optional string bio = 4;
}
message UserStatusUpdatedEvent {
Identifier id = 1;
UserStatus status = 2;
optional string status_message = 3;
}
message HostRoleUpdatedEvent {
HostRole role = 1;
optional string reason = 2;
optional google.protobuf.Timestamp until = 3;
}
message MemberRoleUpdatedEvent {
Identifier id = 1;
oneof role {
ServerRole server_role = 2;
HostRole host_role = 3;
}
optional Identifier updated_by = 4;
optional string reason = 5;
optional google.protobuf.Timestamp until = 6;
}
message ServerReferenceEvent {
bytes uuid = 1;
repeated string replica_hosts = 2;
uint32 sort_order = 3;
}
message RoomReferenceEvent {
bytes server_uuid = 1;
repeated string server_replica_hosts = 2;
bytes room_uuid = 3;
optional string category = 4;
}
message ServerConfigEvent { repeated ServerConfigUpdate updates = 1; }
message ServerConfigUpdate {
bytes server_uuid = 1;
oneof action {
NotifyMode notify_mode = 2;
int32 sort_order = 3;
StringList hosts = 4;
}
}
message RoomConfigEvent { repeated RoomConfigUpdate updates = 1; }
message RoomConfigUpdate {
bytes room_uuid = 1;
oneof action { NotifyMode notify_mode = 2; }
}
/*
* === COMMON TYPES ===
*/
enum RoomType {
// Basic room for text chat.
ROOM_TYPE_TEXT = 0;
// Like ROOM_TYPE_TEXT but only SERVER_ROLE_BOT and higher can post.
ROOM_TYPE_ANNOUNCEMENTS = 1;
// Autogenerated direct message room in zero server. Like ROOM_TYPE_TEXT.
ROOM_TYPE_DM = 2;
// Group chat room in zero server. Like ROOM_TYPE_TEXT.
ROOM_TYPE_GROUP = 3;
// Self chat room in zero server. Like ROOM_TYPE_TEXT.
ROOM_TYPE_SELF = 4;
// Voice chat room with no text component.
// This is the only type that is significantly functionally different: it cannot contain messages at all.
ROOM_TYPE_VOICE = 5;
// Text room where all top-level posts must be threads.
ROOM_TYPE_FORUM = 6;
// Client-specific custom room type determined by custom metadata.
ROOM_TYPE_CUSTOM = 7;
}
enum UserStatus {
// User is offline on all devices.
USER_STATUS_OFFLINE = 0;
// User is online but should appear offline to other users.
// Methods that fetch server members or room members should display `USER_STATUS_OFFLINE` instead.
USER_STATUS_INVISIBLE = 1;
// User is online but does not want to be disturbed, may not receive notifications.
USER_STATUS_DO_NOT_DISTURB = 2;
// User is online but has not been active on any device recently.
USER_STATUS_IDLE = 3;
// User is online and recently active on at least one device.
USER_STATUS_ONLINE = 4;
}
enum HostRole {
// User cannot connect to the host.
HOST_ROLE_BANNED = 0;
// User can connect to the host but cannot interact or join servers.
HOST_ROLE_QUARANTINED = 1;
// Like HOST_ROLE_USER but the user is a proxy for a user on another platform or host.
HOST_ROLE_PROXY = 2;
// User is an ordinary user.
HOST_ROLE_USER = 3;
// User is a bot, can own apps.
HOST_ROLE_BOT = 4;
// User is an admin, can moderate users at the host level and change host settings.
HOST_ROLE_ADMIN = 5;
// User is the system user (there can only be one), can perform federation operations.
HOST_ROLE_SYSTEM = 6;
}
enum ServerRole {
// User cannot join the server.
SERVER_ROLE_BANNED = 0;
// User can view posts but cannot post or react. (due to moderation)
SERVER_ROLE_MUTED = 1;
// User can view posts but cannot post or react. (invited but not joined)
SERVER_ROLE_VISITOR = 2;
// User is an ordinary user.
SERVER_ROLE_MEMBER = 3;
// User is a bot, can annotate and receive events for owned apps assigned to the server.
SERVER_ROLE_BOT = 4;
// User is a moderator, can moderate users, create and delete rooms.
SERVER_ROLE_MODERATOR = 5;
// User is an admin, has moderator permissions and can edit server settings.
SERVER_ROLE_ADMIN = 6;
}
enum FileType {
// Downloadable files of any MIME type.
FILE_TYPE_DOWNLOAD = 0;
// Icons for users, servers, rooms, etc. Must be square PNG or JPEG.
FILE_TYPE_ICON = 1;
// Images attached to messages; must be PNG or JPEG.
FILE_TYPE_IMAGE = 2;
// Audio files attached to messages; must be Opus.
FILE_TYPE_AUDIO = 3;
// Video files attached to messages; must be WebM container with VP9 video and Opus audio.
FILE_TYPE_VIDEO = 4;
// Thumbnails for images and videos. This is a special category that cannot be uploaded by users.
// Whenever an image or video is uploaded, the host generates a PNG or JPEG thumbnail and stores it.
// Thumbnails always have the same hash as the file they represent.
// Thumbnails cannot be deleted directly, but are deleted when their associated file is deleted.
FILE_TYPE_THUMBNAIL = 5;
// ZIP files containing emoji packs. An emoji pack is a ZIP containing only PNG files, which may be animated.
// `server_emoji_pack_import` can be called to import an emoji pack.
// Each file in the ZIP will become a custom emoji, with its extensionless filename as the shortcode.
FILE_TYPE_EMOJI_PACK = 6;
// Custom emoji. This is a special category that cannot be uploaded by users.
// Custom emoji files are generated by the host when an emoji pack is imported.
// Emojis are always PNG files, and may be animated.
// Emojis cannot be deleted directly, but are deleted when their associated emoji pack is deleted.
FILE_TYPE_EMOJI = 7;
}
enum NotificationType {
NOTIFICATION_TYPE_UNKNOWN = 0;
NOTIFICATION_TYPE_MENTION = 1;
NOTIFICATION_TYPE_EVERYONE = 2;
NOTIFICATION_TYPE_THREAD = 3;
NOTIFICATION_TYPE_DM = 4;
NOTIFICATION_TYPE_DM_INVITE = 5;
NOTIFICATION_TYPE_GROUP_INVITE = 6;
NOTIFICATION_TYPE_SERVER_INVITE = 7;
NOTIFICATION_TYPE_ROOM_INVITE = 8;
}
enum NotifyMode {
// Inherit from parent notification settings, default to `NOTIFY_MODE_MENTIONS_AND_THREADS`.
NOTIFY_MODE_UNSET = 0;
// Notify when anyone posts.
NOTIFY_MODE_ALL = 1;
// Notify when current user is mentioned or someone replies to a thread current user has participated in.
NOTIFY_MODE_MENTIONS_AND_THREADS = 2;
// Do not notify about anything.
NOTIFY_MODE_MUTE = 3;
}
enum AttachmentType {
ATTACHMENT_TYPE_DOWNLOAD = 0;
ATTACHMENT_TYPE_IMAGE = 1;
ATTACHMENT_TYPE_AUDIO = 2;
ATTACHMENT_TYPE_VIDEO = 3;
}
enum HostListType {
// List of hosts that a local user has joined servers on.
// Used to restart federation when a user reconnects to their main host.
// Users who manage their own private keys may store this locally instead.
HOST_LIST_TYPE_USER_JOINED = 0;
// List of hosts that a local user has blocked.
// The user will not receive DM requests from these hosts.
HOST_LIST_TYPE_USER_BLOCK = 1;
// Admin-controlled list of hosts allowed to federate with this host.
// Only local users and users from these hosts can connect to this host.
// Only used when the host is in limited federation mode.
HOST_LIST_TYPE_FEDERATION_ALLOW = 2;
// Admin-controlled list of hosts blocked from federating with this host.
// Users from these hosts cannot connect to this host.
// Only used when the host is in open federation mode.
HOST_LIST_TYPE_FEDERATION_BLOCK = 3;
}
message Identifier {
string name = 1;
string host = 2;
}
message User {
string name = 1;
optional string display_name = 2;
bytes pubkey = 3;
HostRole host_role = 4;
ServerRole server_role = 5;
optional FileReference icon = 6;
optional string bio = 7;
UserStatus status = 8;
optional string status_message = 9;
optional EmojiReference status_emoji = 10;
optional google.protobuf.Timestamp status_until = 11;
optional string voice_id = 12;
google.protobuf.Timestamp created_at = 13;
google.protobuf.Timestamp last_seen_at = 14;
google.protobuf.Timestamp joined_at = 15;
bool custodial_private_key = 16;
optional bytes last_update_event_uuid = 17;
optional bytes last_status_update_event_uuid = 18;
}
message Server {
bytes uuid = 1;
string host = 2;
string display_name = 3;
string description = 4;
optional string rules = 5;
optional FileReference icon = 6;
google.protobuf.Timestamp created_at = 7;
bool private = 8;
bool federated = 9;
bool anyone_can_invite = 10;
repeated string languages = 11;
repeated string replica_hosts = 12;
optional bytes last_update_event_uuid = 13;
}
message Room {
bytes uuid = 1;
bytes server_uuid = 2;
string display_name = 3;
optional string topic = 4;
RoomType type = 5;
optional string voice_id = 6;
optional google.protobuf.FileDescriptorSet custom_fields_descriptor = 7;
optional FileReference icon = 8;
google.protobuf.Timestamp created_at = 9;
bool private = 10;
optional bytes last_update_event_uuid = 11;
}
message ThreadSummary {
uint32 reply_count = 1;
google.protobuf.Timestamp last_reply_at = 2;
repeated Identifier some_reply_authors = 3;
}
message Message {
bytes uuid = 1;
oneof thread {
bytes parent = 2;
ThreadSummary replies = 3;
}
bool top_level = 4;
Identifier author = 5;
optional string content = 6; // only absent for annotation messages
optional string spoiler = 7;
google.protobuf.Timestamp created_at = 8;
optional bytes last_update_event_uuid = 9;
repeated google.protobuf.Any custom_fields = 10;
repeated Attachment attachments = 11;
repeated Annotation annotations = 12;
repeated Identifier mentions = 13;
repeated ReactionSummary reactions = 14;
}
message Attachment {
FileReference file = 1;
AttachmentType attachment_type = 2;
optional string name = 3;
uint64 size_in_bytes = 4;
optional uint32 width = 5;
optional uint32 height = 6;
optional uint64 length_in_seconds = 7;
}
message Action {
string name = 1;
string caption = 2;
bool primary = 3;
}
message Annotation {
bytes uuid = 1;
Identifier author = 2;
optional string app_name = 3;
optional FileReference app_icon = 4;
google.protobuf.Timestamp created_at = 5;
optional bytes last_update_event_uuid = 6;
optional string click_anywhere_link = 7;
optional string text_content = 8;
optional string html_embed = 9;
repeated google.protobuf.Any custom_fields = 10;
repeated Attachment attachments = 11;
repeated Action actions = 12;
repeated Identifier visible_to = 13;
}
message Reaction {
Identifier author = 1;
EmojiReference emoji = 2;
google.protobuf.Timestamp created_at = 3;
}
message ReactionSummary {
EmojiReference emoji = 1;
uint32 count = 2;
optional string yours = 3;
repeated Identifier some_authors = 4;
}
message Notification {
bytes uuid = 1;
optional bytes room_uuid = 2;
bool read = 3;
NotificationType notification_type = 4;
oneof referent {
Message message = 5;
Identifier user = 6;
bytes ref_uuid = 7;
}
}
message CommandDescription {
string prefix = 1;
string name = 2;
string usage = 3;
string description = 4;
}
message AppDescription {
string name = 1;
string display_name = 2;
Identifier owner = 3;
string description = 4;
optional string link = 5;
optional FileReference icon = 6;
repeated CommandDescription commands = 7;
}
message FileReference {
bytes file_hash = 1;
repeated string hosts = 2;
optional bytes thumbhash = 3;
}
message CustomEmojiReference {
bytes file_hash = 1;
repeated string hosts = 2;
string shortcode = 3;
}
message EmojiReference {
oneof reference {
string unicode = 1;
string grouped_unicode = 2;
CustomEmojiReference custom = 3;
}
}
message FileChunk {
bytes chunk = 1;
bool done = 2;
}
message StringList {
repeated string strings = 1;
}
message StringUpdate {
optional string value = 1;
}
message FileReferenceUpdate {
optional FileReference value = 1;
}
/*
* === SIGNED STATEMENTS ===
*
* Some actions, such as key rotation and account migration, require a statement signed with the user's private key.
* This provides proof that the user approved the action, even if the user's original host is no longer available.
* These statements can be propagated among federated servers with a gossip protocol.
*/
enum StatementType {
STATEMENT_TYPE_UNSPECIFIED = 0;
STATEMENT_TYPE_KEY_ROTATION = 1;
STATEMENT_TYPE_KEY_REVOCATION = 2;
STATEMENT_TYPE_MIGRATION = 3;
}
message SignedStatement {
StatementType statement_type = 1;
bytes statement = 2;
bytes signature = 3;
}
message Statement {
oneof statement {
KeyRotationStatement key_rotation = 1;
KeyRevocationStatement key_revocation = 2;
MigrationStatement migration = 3;
}
}
message KeyRotationStatement {
Identifier user = 1;
bytes old_pubkey = 2;
bytes new_pubkey = 3;
google.protobuf.Timestamp effective_at = 4;
bool custodial_private_key = 5;
optional string reason = 6;
}
message KeyRevocationStatement {
Identifier user = 1;
bytes pubkey = 2;
google.protobuf.Timestamp effective_at = 3;
optional string reason = 4;
}
message MigrationStatement {
Identifier old_user = 1;
Identifier new_user = 2;
bytes pubkey = 3;
optional bytes new_pubkey = 4;
google.protobuf.Timestamp effective_at = 5;
bool custodial_private_key = 6;
optional string reason = 7;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment