Messenger

Self-hosted messenger for family and friends. Built from scratch: Go backend, React PWA frontend, PostgreSQL. Deployed on a home-lab mini PC.

Status: v1.0.0 — production-ready. Auth, direct chats and groups, real-time delivery, media, reactions, search, Web Push, PWA offline, metrics and hardening are all implemented and tested.

Repository layout

server/   Go API
  cmd/api/                 entrypoint (wiring, migrations, graceful shutdown)
  cmd/keygen/              generate VAPID keys for Web Push
  cmd/seed/                create demo users and a family group
  internal/api/            HTTP handlers: auth, users, contacts, conversations, messages, media, search, push
  internal/auth/           argon2id hashing, JWT access tokens, refresh tokens
  internal/realtime/       WebSocket hub: events, presence, typing, read receipts
  internal/media/          S3-like storage + image thumbnails
  internal/store/          PostgreSQL access (pgx), embedded migrations
  internal/platform/       config, logging, metrics, rate limiting, version
web/      React + TypeScript + Vite PWA (offline, Web Push)
deploy/   docker-compose (api, postgres, victoriametrics, grafana)
docs/     architecture (mermaid), ADR
.github/  CI workflows

API

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} DM or {type:"group", name, member_ids}
GET /api/v1/conversations/{id}/members List participants + roles
POST /api/v1/conversations/{id}/members {user_id} — add to group
DELETE /api/v1/conversations/{id}/members/{user_id} Remove/leave (owner or self)
GET /api/v1/conversations/{id}/messages History, keyset ?before=
POST /api/v1/conversations/{id}/messages {body, temp_id, media_id?} — send
PUT /api/v1/conversations/{id}/messages/{msg_id}/reactions {emoji} — react
DELETE /api/v1/conversations/{id}/messages/{msg_id}/reactions/{emoji} Remove reaction
POST /api/v1/media Upload file (multipart file)
GET /api/v1/media/{id} Download file
GET /api/v1/media/{id}/thumb Image thumbnail
GET /api/v1/search/messages?q= Search user's messages
POST /api/v1/push/subscribe Register Web Push subscription
DELETE /api/v1/push/subscribe Unregister Web Push subscription
GET /ws WebSocket (auth via cookie)

WebSocket event types: message.created, typing, presence, read, reaction.added/removed, member.added/removed, conversation.added/removed.

Prerequisites

  • Go 1.27+, Node.js 22+, PostgreSQL
  • Docker + Docker Compose (for deployment)

Local development

# create the role once (as postgres superuser)
sudo -u postgres psql -c "CREATE ROLE messenger LOGIN PASSWORD 'messenger';"
sudo -u postgres psql -c "CREATE DATABASE messenger OWNER messenger;"

# backend (migrations run automatically on start)
cd server
go run ./cmd/api                    # :8080

# frontend (separate terminal)
cd web
npm install
npm run dev                         # :5173, proxies /api and /ws -> :8080

Optional: demo data → cd server && go run ./cmd/seed (users papa/mama/syn/doch, password password123, group «Семья»).

Deployment (Docker Compose)

cp deploy/.env.example deploy/.env   # adjust secrets
cd deploy
docker compose up -d --build

Services: api (single static binary serving API + frontend), postgres, victoriametrics (metrics storage), grafana (dashboard «Messenger» auto-provisioned at http://localhost:3000).

Metrics

GET /metrics exposes Prometheus metrics: HTTP requests by status/path, request latency, WebSocket connections, Go runtime. Scraped by VictoriaMetrics.

Web Push (optional)

cd server && go run ./cmd/keygen     # prints the three VAPID variables

Put them in deploy/.env (MESSENGER_VAPID_*) and in web/.env (VITE_VAPID_PUBLIC_KEY). Without keys everything works except notifications.

Architecture & decisions

See docs/architecture.md (component and sequence diagrams) and docs/adr/ for the rationale behind: monolith, in-memory WS hub, ILIKE search, S3-like media storage, E2EE roadmap.

Roadmap

  • Phase 0-3 — skeleton → auth/chats → frontend → groups/reactions/media → search/push/PWA ✅
  • Phase 4 — P2P WebRTC calls via our WebSocket signalling + coturn TURN fallback (planned, major feature)
  • Phase 5 — metrics, docs, hardening ✅

License

Proprietary for now; open-sourcing at a future major version.

S
Description
Small selfhosted messenger for friends and family. Currently WIP
Readme
492 KiB
2026-09-28 13:05:45 +03:00
Languages
TypeScript 48.6%
Go 47.6%
CSS 3.3%
Dockerfile 0.3%
HTML 0.2%