Skip to content

Protocol basics ​

OmniChat speaks Protobuf over WebSocket binary frames. One WebSocket message = one Envelope.

Full schema: proto/omnichat/v1/omnichat.proto
Generated Go: pkg/pb/

Framing ​

  • Use binary WebSocket frames only (not text/JSON).
  • Max size defaults to 1 MiB (OMNICHAT_MAX_FRAME_BYTES).
  • Server sends WebSocket pings; clients should reply with pong.

Envelope ​

Every frame is an Envelope with optional request_id (echoed on replies) and one payload.

Common payloads:

DirectionTypePurpose
Client → serverAuthJWT on first frame
Server → clientAuthOKAuthenticated; includes user_id
Client → serverJoinRoom / LeaveRoomSubscribe to a room
Client → serverSendMessageroom_id, client_msg_id, body
Client → serverEditMessageroom_id, server_msg_id, body (author only)
Client → serverDeleteMessageroom_id, server_msg_id (author only; soft-delete)
Server → clientMessageAckConfirms send/edit/delete (server_msg_id, seq)
Server → clientMessageEventMessage upsert; includes update_seq, edited_at_unix_ms, deleted
Client → serverTypingEphemeral typing flag
Client → serverReadReceiptlast_read_seq
Client → serverSyncRoomCatch-up (since_seq = last update_seq)
Server → clientErrorcode + message

Happy path ​

  1. Connect → send Auth → receive AuthOK
  2. JoinRoom → JoinOK (includes latest_seq)
  3. SendMessage → MessageAck (+ MessageEvent to the room)
  4. Optional: ReadReceipt, Typing, SyncRoom

Errors ​

CodeMeaning
unauthorizedBad/missing JWT or action before auth
not_foundUnknown room or message
invalidBad payload
forbiddenNot joined, or not message author
goneMessage already deleted
internalServer failure

REST (bootstrap only) ​

POST /v1/rooms
Authorization: Bearer <JWT>
{"id":"lobby","name":"Lobby"}

Requires the same JWT secret/keys as WebSocket auth.

Dig deeper ​

Wire details and reserved edit/delete types live in the .proto file. Client offline queues are described briefly in Sync model.

Apache License 2.0