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:
RPCResult : Response to a client request (matched by req_id)
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:
Client sends ClientMessage with a unique id
Server processes the request
Server responds with ServerMessage containing RPCResult
RPCResult.req_id matches the original ClientMessage.id
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:
Connect to server via WebSocket or TCP
Send core_initialize with client metadata
Receive Initialized with available entrypoints
Optionally authenticate with auth_authorize using a saved token
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