Skip to main content

Protocol Overview

The Osmium Chat Protocol is built on Protocol Buffers (proto3) and uses a bidirectional communication model combining request-response RPC with server-pushed updates.

Core Architecture

All communication flows through two primary message types defined in core.proto:

ClientMessage

Every request from the client is wrapped in a ClientMessage:
message ClientMessage {
  uint32 id = 1;
  oneof message {
    auth.SignIn auth_sign_in = 2;
    auth.SignUp auth_sign_up = 3;
    messages.SendMessage messages_send_message = 4;
    messages.GetHistory messages_get_history = 5;
    communities.CreateCommunity communities_create_community = 6;
    // ... 80+ RPC methods
  }
}
Key Points:
  • id: A unique identifier for this request, used to match responses
  • message: A oneof field containing exactly one RPC request type
  • The naming convention follows the pattern: package_method_name
The client generates unique IDs for each request. The server uses this ID in the response’s req_id field, allowing clients to match responses to their requests.

ServerMessage

Every message from the server is wrapped in a ServerMessage:
message ServerMessage {
  uint32 id = 1;
  oneof message {
    tangle.client.updates.Update update = 2;
    RPCResult result = 3;
  }
}
Two Message Types:
  1. RPCResult: Response to a client request (matched by req_id)
  2. Update: Real-time update pushed by the server

RPC Pattern

The protocol uses a request-response pattern with type-safe results.

RPCResult Structure

message RPCResult {
  uint32 req_id = 1;
  oneof result {
    RPCError error = 2;
    messages.SentMessage sent_message = 3;
    auth.Authorization authorization = 4;
    communities.Communities communities = 5;
    messages.Messages messages = 7;
    chats.Chats chats = 9;
    // ... 30+ result types
  }
}

message RPCError {
  uint32 error_code = 1;
  string error_message = 2;
}
Flow:
  1. Client sends ClientMessage with a unique id
  2. Server processes the request
  3. Server responds with ServerMessage containing RPCResult
  4. RPCResult.req_id matches the original ClientMessage.id
  5. Result contains either the expected data type or an RPCError

Example: Authentication Flow

// 1. Client Request
ClientMessage {
  id: 1,
  message: {
    auth_sign_in: SignIn {
      email: "[email protected]",
      password: "secure_password"
    }
  }
}

// 2. Server Response (Success)
ServerMessage {
  id: 100,
  message: {
    result: RPCResult {
      req_id: 1,
      result: {
        authorization: Authorization {
          token: "eyJhbGciOiJIUzI1NiIs...",
          user: User {
            id: 123456789,
            name: "Alice",
            username: "alice"
          },
          session_id: 987654321
        }
      }
    }
  }
}

// 2. Server Response (Error)
ServerMessage {
  id: 100,
  message: {
    result: RPCResult {
      req_id: 1,
      result: {
        error: RPCError {
          error_code: 401,
          error_message: "Invalid credentials"
        }
      }
    }
  }
}

Real-Time Updates

The server pushes updates to clients for real-time synchronization. Updates are defined in updates.proto.

Update Types

message Update {
  oneof update {
    UpdateMessageCreated          message_created                  = 1;
    UpdateChannel                 channel                          = 2;
    UpdateMessageDeleted          message_deleted                  = 3;
    UpdateUserStatus              user_status                      = 4;
    UpdateUser                    user                             = 5;
    UpdateCommunity               community                        = 6;
    UpdateChatTyping              chat_typing                      = 9;
    UpdateCommunityMember         community_member                 = 10;
    UpdateUserRelationship        update_user_relationship         = 18;
    UpdateMessageReactions        message_reactions                = 23;
    UpdateConversationLastRead    conversation_last_read           = 24;
    // ... 25+ update types
  }
}

Common Update Patterns

New Message Update

UpdateMessageCreated {
  message: Message {
    chat_ref: ChatRef { ... },
    message_id: 555555555,
    author_id: 111111111,
    message: "Hello!",
    edited_at: null,
    reply_to: null
  },
  channel_unread_count: 5,
  author: User {
    id: 111111111,
    name: "Bob",
    username: "bob"
  }
}
The author field in UpdateMessageCreated is optional but commonly included in community contexts to avoid additional lookups.

Typing Indicator

UpdateChatTyping {
  chat_ref: ChatRef {
    channel: ChannelRef {
      community_id: 123456789,
      channel_id: 987654321
    }
  },
  user_id: 111111111,
  typing: true
}

User Status Change

UpdateUserStatus {
  user_id: 111111111,
  status: UserStatus {
    status: ONLINE
  }
}

Message References

The protocol uses reference types (refs.proto) to address different chat contexts:
message ChatRef {
  oneof ref {
    UserRef user = 1;          // Direct message with a user
    ChannelRef channel = 2;    // Community channel
    GroupRef group = 3;        // Group chat
    RefSelf self = 4;          // Saved messages / self chat
  }
}

message ChannelRef {
  fixed64 community_id = 1;
  fixed64 channel_id = 2;
}

message UserRef {
  fixed64 user_id = 1;
}

message GroupRef {
  fixed64 group_id = 1;
}
Usage: Most messaging operations (send, edit, delete, get history) use ChatRef to specify the target conversation.
// Send to a community channel
SendMessage {
  chat_ref: ChatRef {
    channel: ChannelRef {
      community_id: 123456789,
      channel_id: 987654321
    }
  },
  message: "Hello community!"
}

// Send a direct message
SendMessage {
  chat_ref: ChatRef {
    user: UserRef {
      user_id: 111111111
    }
  },
  message: "Hi there!"
}

Initialization

Clients must initialize before making other requests:
message Initialize {
  uint32 client_id = 1;
  string device_type = 2;
  string device_version = 3;
  string app_version = 4;
  bool no_subscribe = 5;
}

message Initialized {
  Entrypoints entrypoints = 1;
  optional string vapid_public_key = 2;
}
Flow:
  1. Connect to server via WebSocket or TCP
  2. Send core_initialize with client metadata
  3. Receive Initialized with available entrypoints
  4. Optionally authenticate with auth_authorize using a saved token
  5. Begin sending requests and receiving updates
Set no_subscribe = true if you don’t want to receive real-time updates (useful for bots or read-only clients).

Data Types

The protocol defines rich data types in types.proto.

Core Types

message User {
  fixed64 id = 1;
  string name = 2;
  optional string username = 3;
  optional UserStatus status = 4;
  optional ChatPhoto photo = 5;
  bool bot = 6;
}

message Message {
  refs.ChatRef chat_ref = 1;
  fixed64 message_id = 2;
  fixed64 author_id = 3;
  string message = 4;
  optional fixed64 reply_to = 5;
  repeated media.MessageMedia media = 6;
  repeated MessageEntity entities = 7;
  optional uint64 edited_at = 8;
}

message Community {
  fixed64 id = 1;
  bool owner = 2;
  string name = 3;
  optional ChatPhoto photo = 4;
  fixed64 permissions = 5;
  bool muted = 6;
}

message Channel {
  fixed64 id = 1;
  fixed64 community_id = 2;
  string name = 3;
  ChannelType type = 4;  // TEXT, VOICE, or CATEGORY
  uint32 position = 5;
  optional fixed64 parent_id = 6;
}

Snowflake IDs

All entities use snowflake IDs (fixed64):
  • Distributed ID generation
  • Timestamp-ordered (IDs increase over time)
  • Unique across all entity types
  • Annotated with comments like // @snowflake<User>

Permission System

Communities use a bitfield permission system:
enum CommunityPermission {
  NO_PERMISSION = 0;
  ADMINISTRATOR = 1;      // 1<<0
  VIEW_CHANNEL = 2;       // 1<<1
  SEND_MESSAGES = 4;      // 1<<2
  CONNECT_VOICE = 8;      // 1<<3
  MODIFY_CHANNEL = 16;    // 1<<4
  SEND_MEDIA = 32;        // 1<<5
  DELETE_MESSAGES = 64;   // 1<<6
  // ...
}

message PermissionOverrides {
  fixed64 pos = 1;  // Positive permissions (allow)
  fixed64 neg = 2;  // Negative permissions (deny)
}
Permissions can be set at:
  • Community default level
  • Role level
  • Channel override level (per role)

What’s Next?

Quick Start

Get hands-on with practical examples and make your first RPC call

Messages API

Learn about sending, editing, and managing messages

Build docs developers (and LLMs) love