Real-time chat in Go — WebSockets for live text, Kafka as the durable event log, Redis for fan-out and presence, Postgres/pgvector for history and semantic search, and local-LLM (Ollama) RAG features. Web UI via HTMX (no SPA). Sibling project to news-feed-go: "one repo, many roles."
- Hot path (real-time, <100ms): client
-> WebSocket -> gateway -> Redis pub/sub -> gateway -> WebSocket ->other clients. No broker on this path. - Durable path: every message is also an event -> Kafka log (
chat.messages, keyed byroom_idfor per-room ordering), consumed independently by persistence, search-indexing, and AI.
Delivery avoids the dual-write problem: the gateway produces to Kafka (source of truth), a fan-out consumer republishes to Redis, and every gateway with a subscriber in that room pushes down the socket.
| Process | Role |
|---|---|
gateway |
HTTP + WebSocket server: JWT auth, HTMX UI, WS connections, WebRTC signaling |
persister |
Kafka -> Postgres message history |
indexer |
Kafka -> Ollama embed -> pgvector |
ai-worker |
RAG assistant + "catch me up" summaries |
notifier |
offline push |
Backing services: Postgres + pgvector, Redis (pub/sub + presence), Kafka (durable log).
- Scaffold — repo, schema, docker-compose (Postgres + Redis). <- you are here
- Text chat MVP — WS gateway + auth + send/receive via Redis pub/sub + HTMX UI (no Kafka yet).
- Durability + Kafka — produce to Kafka,
persister-> Postgres, load history. - Semantic search —
indexer(embed -> pgvector) + search. - RAG assistant —
@botretrieve + LLM + stream. - "Catch me up" summarization.
- Presence + multi-gateway scaling (Redis TTL heartbeats).
- WebRTC voice/video (signaling + STUN/TURN).
- Deploy on k3s (Helm) + tests + CI.
cp .env.example .env
docker compose up -d # Postgres :5433, Redis :6381
# apply the schema:
docker compose exec -T postgres psql -U chat -d chat < internal/repository/postgres/schema.sql
# generate type-safe Go from SQL:
sqlc generate # -> internal/repository/postgres/sqlc