GoChat is composed of focused services connected through a shared data and messaging layer. The diagram below shows the primary data-flow relationships between all components.
---
config:
theme: dark
---
flowchart TD
Client(["Client Applications"])
Traefik["Traefik"]
subgraph services["Application Services"]
direction LR
API["API"]
Auth["Auth"]
WS["WebSocket Gateway"]
Attachments["Attachments"]
Webhook["Webhook"]
end
subgraph messaging["Messaging"]
direction LR
NATS[("NATS\n(app events)")]
NATS_S[("NATS\n(search events)")]
end
subgraph workers["Background Workers"]
direction LR
Indexer["Indexer"]
Embedder["Embedder"]
end
subgraph storage["Storage & State"]
direction LR
PG[("PostgreSQL / Citus")]
Scylla[("ScyllaDB")]
KeyDB[("Redis / KeyDB")]
etcd[("etcd")]
OS[("OpenSearch")]
S3[("S3")]
OO[("OpenObserve")]
end
subgraph voice["Voice"]
SFU["SFU"]
TG["Telemetry Gateway"]
end
Collector["OTEL Collector"]
Client <--> Traefik
Traefik --> API & Auth & WS & Attachments & Webhook
API --> NATS
WS --> NATS
Attachments --> NATS
Webhook --> NATS
NATS --> WS & Embedder
API --> NATS_S
NATS_S --> Indexer
API --> PG & Scylla & KeyDB & OS
API -- "discover" --> etcd
Auth --> PG & KeyDB
WS --> PG & Scylla & KeyDB
Attachments --> PG & Scylla & S3
Webhook --> Scylla
Webhook -- "register" --> etcd
Indexer --> OS
Embedder --> Scylla
Client <-- "WebRTC + WS" --> SFU
SFU -- "heartbeat" --> Webhook
services & workers -. "OTEL" .-> Collector
SFU -. "OTEL" .-> TG
TG -.-> Collector
Collector -.-> OO
| Service | Path | Role |
|---|---|---|
| API | cmd/api |
Public REST surface — guilds, channels, messages, search, voice control |
| Auth | cmd/auth |
Registration, login, token refresh, email flows, password reset |
| WebSocket Gateway | cmd/ws |
Real-time subscriptions, event delivery, presence, session handling |
| Attachments | cmd/attachments |
Upload pipeline for files, avatars, icons; S3 storage and metadata |
| Webhook | cmd/webhook |
Internal callbacks — SFU heartbeats and attachment finalization |
| SFU | cmd/sfu |
WebRTC media relay and WebSocket signaling for voice channels |
| Indexer | cmd/indexer |
Consumes NATS message events, writes to OpenSearch |
| Embedder | cmd/embedder |
Builds link-preview embeds from remote metadata |
| Telemetry Gateway | cmd/telemetrygateway |
OTEL proxy — collects signals from all services and forwards to the observability backend |
| Tools | cmd/tools |
Operational helpers (observability bootstrap, webhook token generation) |
| Store | Technology | Primary use |
|---|---|---|
| Relational | PostgreSQL / Citus | Users, guilds, channels, roles, invites, bans |
| Time-series / wide-column | ScyllaDB | Message timelines, attachment metadata, presence data |
| Cache / session | Redis / KeyDB | Auth tokens, voice route bindings, presence state |
| Messaging | NATS | Async event delivery between services |
| Search index | OpenSearch | Full-text message search |
| Object storage | S3-compatible | Uploaded files, avatars, icons |
| Service discovery | etcd | Voice SFU instance registry |
- Client calls
POST /api/v1/channel/{id}/voice/join— API picks the best SFU via etcd. - API issues a short-lived SFU JWT (2-minute expiry, HS256) and returns the SFU endpoint.
- Client connects directly to SFU over WebSocket and establishes a WebRTC peer connection.
- SFU sends periodic heartbeats to
Webhook, which refreshes the instance in etcd. - On region change, the API publishes
VoiceRegionChangingover NATS, waits 3 seconds (jitter), updates the cache binding, then sends aVoiceRebindevent and closes the old SFU channel via admin endpoint.
See voice/SystemArchitecture.md and voice/ConnectionProtocol.md for the full protocol.