Messenger
Self-hosted messenger for family and friends. Built from scratch: Go backend, React PWA frontend, PostgreSQL, WebRTC calls. Deployed on a home-lab mini PC.
Status: Phase 1 — auth (JWT + refresh in httpOnly cookies, argon2id), contacts, direct-message chats, real-time delivery over WebSocket (messages, typing, presence, read receipts). Backend is complete and tested; the frontend still shows the Phase 0 status screen.
Repository layout
server/ Go API
cmd/api/ entrypoint (wiring, migrations, graceful shutdown)
internal/api/ HTTP handlers: auth, users, contacts, conversations, messages
internal/auth/ argon2id hashing, JWT access tokens, refresh tokens
internal/realtime/ WebSocket hub: events, presence, typing, read receipts
internal/store/ PostgreSQL access (pgx), embedded migrations
internal/platform/ config, structured logging
web/ React + TypeScript + Vite frontend (PWA-ready)
deploy/ docker-compose and operational config
docs/ architecture, ADR, OpenAPI
.github/ CI workflows
API (Phase 1)
All endpoints under /api/v1. Auth via httpOnly cookies (messenger_access
= JWT 15 min, messenger_refresh = opaque token 30 days, rotated on refresh).
| Method | Path | Description |
|---|---|---|
| POST | /api/v1/auth/register |
Create account (sets cookies) |
| POST | /api/v1/auth/login |
Sign in (sets cookies) |
| POST | /api/v1/auth/refresh |
Rotate refresh token |
| POST | /api/v1/auth/logout |
Revoke session, clear cookies |
| GET | /api/v1/users/me |
Current user |
| GET | /api/v1/users?q= |
Search users by username/name |
| GET | /api/v1/contacts |
Contact list |
| POST | /api/v1/contacts |
{user_id} — add contact |
| DELETE | /api/v1/contacts/{user_id} |
Remove contact |
| GET | /api/v1/conversations |
List chats + unread counts |
| POST | /api/v1/conversations |
{user_id} — create/find DM |
| GET | /api/v1/conversations/{id}/messages |
History, keyset ?before= |
| POST | /api/v1/conversations/{id}/messages |
{body, temp_id} — send |
| GET | /ws |
WebSocket (auth via cookie) |
WebSocket event types: message.created, typing, presence, read.
Prerequisites
- Go 1.27+
- Node.js 22+
- Docker + Docker Compose (for deployment)
Local development
Requires a local PostgreSQL (e.g. sudo -u postgres psql -c "CREATE ROLE messenger LOGIN PASSWORD 'messenger';" and
sudo -u postgres psql -c "CREATE DATABASE messenger OWNER messenger;").
# Backend (migrations run automatically on start)
cd server
go run ./cmd/api # listen on :8080
# Frontend (separate terminal)
cd web
npm install
npm run dev # Vite on :5173, proxies /api -> :8080
The frontend calls /api/v1/ping on load and renders the server response.
Useful endpoints
| Endpoint | Purpose |
|---|---|
/healthz |
Liveness probe |
/readyz |
Readiness probe |
/api/v1/ping |
Round-trip check (JSON) |
Deployment (Docker Compose)
cp deploy/.env.example deploy/.env # adjust secrets
cd deploy
docker compose up -d --build
The API container is a single static binary that also serves the built
frontend (/app/web). PostgreSQL runs alongside with a healthcheck.
Roadmap
- Phase 0 — skeleton, CI, Docker ✅
- Phase 1 — auth (JWT + refresh in httpOnly cookie, argon2id), contacts, DMs, WebSocket delivery, receipts, typing ✅ (backend)
- Phase 1.5 — frontend: auth screens, chat UI, realtime client (PWA)
- Phase 2 — groups, reactions, media upload + thumbnails (S3-like storage interface)
- Phase 3 — search (pg_trgm), Web Push (VAPID), PWA offline
- Phase 4 — P2P WebRTC calls via our WebSocket signalling + coturn TURN fallback
- Phase 5 — metrics (Prometheus + VictoriaMetrics + Grafana), docs, hardening
License
Proprietary for now; open-sourcing at v1.0.