Chat Service
Port: 8085 (HTTP + WebSocket)
Database: omninet_chat (PostgreSQL)
Language: Go 1.22
Framework: go-chi/chi v5
The chat service is OmniNet's real-time messaging backbone, built in Go for maximum concurrency and low-latency WebSocket handling.
Features
- Real-Time Messaging — Bidirectional WebSocket communication via
gorilla/websocket. - DM & Group Conversations — Create 1-to-1 direct messages or multi-member group chats.
- Multi-Tab Support — A single user can connect from multiple browser tabs simultaneously; all receive messages.
- Redis Pub/Sub Fan-Out — Messages delivered to one instance are published to Redis and consumed by all other instances — enabling horizontal scaling with zero shared in-process state.
- Typing Indicators — Broadcast real-time typing events to all conversation members (excluding the sender). Stored temporarily in Redis.
- Delivery & Read Receipts —
DELIVEREDstatus set on message delivery;READstatus pushed when a member callsmark_read. - Emoji Reactions — Add/remove reactions on any message; updated
reactionsmap broadcast to all members. - User Presence — Online/offline status tracked in Redis + PostgreSQL. New connections receive the current online list immediately.
- Message Edit & Delete — REST API for editing message content or soft-deleting (unsend); changes broadcast via WebSocket.
- Member Management — Add/remove members from group conversations via REST API.
- Graceful Shutdown — 10-second drain using
signal.NotifyContext+http.Server.Shutdown. - Structured Logging —
go.uber.org/zapwith production-mode JSON logging.
Stack
| Component | Library |
|---|---|
| HTTP Router | go-chi/chi v5 |
| WebSocket | gorilla/websocket v1.5 |
| PostgreSQL | jackc/pgx v5 (connection pool) |
| Redis | redis/go-redis v9 |
| JWT | golang-jwt/jwt v5 |
| Logger | go.uber.org/zap |
WebSocket Events
Client → Server
| Event | Description |
|---|---|
send_message | Send a text message to a conversation |
typing | Notify members that user is typing |
mark_read | Mark all messages up to last_message_id as read |
add_reaction | Add emoji reaction to a message |
remove_reaction | Remove emoji reaction |
ping | Keepalive ping |
Server → Client
| Event | Description |
|---|---|
connected | Sent immediately after WS handshake with user info |
new_message | New message received in a conversation |
message_edited | A message's content was updated |
message_deleted | A message was unsent/deleted |
message_status | Delivery or read receipt update (DELIVERED / READ) |
typing | Another user is typing |
presence | User came online or went offline |
reaction_update | Emoji reactions on a message changed |
pong | Response to ping |
error | Operation failed with code + message |
WebSocket Connection
Connect with a JWT as a query parameter (since browsers can't set Authorization headers on WebSocket):
ws://localhost:8085/ws?token=<jwt>On connection the server immediately sends:
{ "type": "connected", "payload": { "user_id": "...", "email": "..." } }REST API
Conversations
| Method | Path | Description |
|---|---|---|
GET | /api/chat/conversations | List user's conversations |
POST | /api/chat/conversations | Create DM or group conversation |
GET | /api/chat/conversations/{id} | Get conversation details |
PUT | /api/chat/conversations/{id} | Update name/avatar |
POST | /api/chat/conversations/{id}/members | Add member to group |
DELETE | /api/chat/conversations/{id}/members/{userId} | Leave / remove from group |
Messages
| Method | Path | Description |
|---|---|---|
GET | /api/chat/conversations/{id}/messages | Paginated message history |
POST | /api/chat/conversations/{id}/read | Mark conversation as read |
PUT | /messages/{id} | Edit a message |
DELETE | /messages/{id} | Delete (unsend) a message |
POST | /messages/{id}/reactions | Add emoji reaction |
DELETE | /messages/{id}/reactions/{emoji} | Remove emoji reaction |
Presence
| Method | Path | Description |
|---|---|---|
GET | /api/chat/presence?user_ids=a,b,c | Batch query online status |
Hub Architecture
Message Flow
Horizontal Scaling
Each hub instance registers a Redis Pub/Sub subscriber on chat:presence and per-conversation channels (chat:conv:{id}). When a message target is not connected locally, deliverToUser publishes to Redis, and the instance where the target user is connected picks it up and delivers it.
NOTE
The chat service runs independently on port 8085. It does not go through the Java API Gateway — it validates JWTs directly using the same JWT_SECRET.