Distributed real-time chat and voice backend written in Go.
REST API · WebSocket delivery · File uploads · Full-text search · Link-preview embeds · WebRTC voice and streaming
GoChat is service-oriented — each binary has a single focused responsibility. Services communicate through NATS for async events and share YugabyteDB YSQL, ScyllaDB, and KeyDB for state.
→ Full diagram, data-store reference, and voice flow walkthrough
| Service | Path | Responsibility |
|---|---|---|
| API | cmd/api |
Public REST surface — guilds, channels, messages, search, uploads, voice control |
| Auth | cmd/auth |
Registration, login, token refresh, email flows, password reset |
| WebSocket Gateway | cmd/ws |
Real-time event delivery, presence updates, session management |
| Attachments | cmd/attachments |
Upload pipeline for files, avatars, and icons; S3 storage and metadata |
| Webhook | cmd/webhook |
Internal callbacks — SFU/stream heartbeats, stream lifecycle, and attachment finalization |
| SFU | cmd/sfu |
WebRTC media relay and WebSocket signaling for voice channels |
| Stream | cmd/stream |
WebRTC media relay and WebSocket signaling for voice-channel screen/app sharing |
| Indexer | cmd/indexer |
Consumes NATS message events, writes search documents to OpenSearch |
| Embedder | cmd/embedder |
Builds link-preview embeds from remote metadata |
| Telemetry Gateway | cmd/telemetrygateway |
OTEL proxy — collects signals from all services, forwards to observability backend |
| Tools | cmd/tools |
Operational helpers: observability bootstrap, webhook token generation |
- Account lifecycle with JWT-based authentication and email flows
- Guilds, channels, roles, permissions, invites, bans, and custom emoji
- Direct messages, threads, message history, mentions, and reactions
- File attachments, avatars, and icons via S3-compatible storage
- Link-preview embed generation from remote metadata
- Presence updates and real-time event fanout over WebSocket
- Full-text search indexing and query through OpenSearch
- Voice channels with region-aware SFU discovery, WebRTC relay, and separate screen/app streaming
- Structured observability — distributed traces, metrics, and logs via OTEL
| Area | Technology |
|---|---|
| Language | Go 1.25.8 |
| HTTP / WebSocket | Fiber v2, Fiber WebSocket |
| Voice / WebRTC | Pion WebRTC |
| Relational DB | YugabyteDB YSQL |
| Wide-column DB | ScyllaDB |
| Cache / sessions | Redis / KeyDB |
| Message bus | NATS |
| Search | OpenSearch |
| Object storage | S3-compatible |
| Service discovery | etcd |
| Reverse proxy | Traefik |
| Observability | OpenTelemetry + OpenObserve |
- Go
1.25.8or newer - Docker and Docker Compose
- GNU Make
migrateCLI for creating migration files locally (make toolsinstalls it)
make setupInstalls local tooling, starts the full Compose stack, initializes ScyllaDB, and applies all migrations.
# Start infrastructure
docker compose up -d
docker compose exec scylla bash ./init-scylladb.sh
# Apply all migrations
make migrateTo test the migration image used in CI:
make build_migration_image
make migrate_image \
YUGABYTE_ADDRESS="postgres://yugabyte:yugabyte@host.docker.internal:5433/gochat?sslmode=disable" \
CASSANDRA_ADDRESS="cassandra://host.docker.internal/gochat?x-multi-statement=true"CI publishes ghcr.io/<owner>/gochat-migrations:<tag> for releases and ghcr.io/<owner>/gochat-migrations:dev from the dev branch. Database bootstrap steps outside the migration files (ScyllaDB keyspace creation and YugabyteDB database creation) must be completed before running the container.
The container accepts YUGABYTE_ADDRESS, PG_ADDRESS as a backward-compatible YugabyteDB YSQL alias, CASSANDRA_ADDRESS, and MIGRATION_SCOPE=all|yugabyte|yb|ysql|cassandra|scylla.
For local Compose development, make migrate runs the official migrate/migrate container on the Compose network and mounts ./migration read-only. This uses service names like yugabyte:5433 and scylla, so host-port conflicts on 127.0.0.1:5433 do not affect development migrations. Override YUGABYTE_DB, YUGABYTE_USER, YUGABYTE_PASSWORD, or COMPOSE_PROJECT_NAME in .env when needed.
Local Compose creates the gochat YugabyteDB database with YUGABYTE_COLOCATION=false by default. This keeps core relational metadata sharded for high-load deployments; enable colocation only for small isolated test databases or tiny reference datasets.
Copy and edit the example config for each service:
api_config.example.yaml ws_config.example.yaml
auth_config.example.yaml sfu_config.example.yaml
stream_config.example.yaml telemetry_gateway_config.example.yaml
attachments_config.example.yaml indexer_config.example.yaml
webhook_config.example.yaml embedder_config.example.yaml
go run ./cmd/api
go run ./cmd/auth
go run ./cmd/ws
go run ./cmd/attachments
go run ./cmd/webhook
go run ./cmd/indexer
go run ./cmd/embedder
go run ./cmd/telemetrygateway
go run ./cmd/sfu # voice media; runs separately from Compose
go run ./cmd/stream # screen/app streaming media; runs separately from ComposeUseful Make targets:
| Target | What it does |
|---|---|
make up |
Start the Compose stack and initialize ScyllaDB |
make down |
Stop the stack |
make migrate |
Apply all database migrations |
make swag |
Rebuild docs/api/swagger.json |
make client |
Regenerate Go and TypeScript API clients |
make rebuild_all |
Rebuild API, Auth, Indexer, Embedder, and WS containers |
make build_migration_image |
Build the versioned migration container locally |
The local stack uses OpenObserve and the OpenTelemetry Collector. Bootstrap dashboards with the Tools CLI:
go run ./cmd/tools observability bootstrap \
--url http://localhost:5080 --org default \
--user root@example.com --password Complexpass#123
go run ./cmd/tools observability smoke \
--url http://localhost:5080 --org default \
--user root@example.com --password Complexpass#123| Endpoint | URL |
|---|---|
| OpenObserve | http://localhost:5080 |
| OTEL Collector health | http://localhost:13133/ |
| Traefik dashboard | http://localhost:8080 |
| OpenSearch Dashboards | http://localhost:5601 |
| Architecture | Full service diagram, data-store reference, voice flow |
| Services overview | Per-service responsibilities and config reference |
| Channels & messages | Channel types, message types, threads, embeds |
| Guilds, roles & permissions | Roles, permissions bitmask, moderation, custom emoji |
| Presence system | Presence state model and delivery |
| WebSocket protocol | Event types, subscription model, connection lifecycle |
| Voice & SFU | WebRTC signaling, SFU protocol, permissions |
| Voice-channel streaming | Screen/app streaming service, lifecycle, presence, and region migration |
| Direct-message calls | Private 1:1 voice calls, settings bootstrap, pair-scoped events, and DM call streaming |
| Observability | OTEL signals, dashboards, runbooks, external SFU |
| Auth security | Token design, expiry, refresh flow |
| Database schema | YugabyteDB YSQL and ScyllaDB schema diagrams |
| Tools CLI | Operational helper commands |
| OpenAPI schema | Machine-readable API spec |
| Go API client | Generated Go client |
| TypeScript API client | Generated TypeScript client |
| Frontend repo | React web client |
| Desktop client | Electron desktop app |
| Deployment repo | Production deployment manifests |
cmd/ runnable services and operational tools
internal/ shared packages (transport, storage, search, mail, presence, server wiring)
migration/ YugabyteDB YSQL and ScyllaDB migrations
docs/ project documentation and generated OpenAPI schema
clients/api/ generated Go and TypeScript API clients
compose.yaml local development stack
Makefile bootstrap, migration, client generation, and rebuild targets
MIT License · See LICENSE