# Architecture
Messenger is a self-hosted monolith: a single Go binary serves the REST API,
WebSocket hub, media files and the built frontend. PostgreSQL is the only
stateful dependency; attachments live on local disk behind an S3-like
interface.
## Component diagram
```mermaid
flowchart LR
subgraph Browser[Browser / PWA]
UI[React + Vite + Tailwind]
SW[Service Worker
offline cache + Web Push]
end
subgraph Server[Go binary - single process]
HTTP[REST API /api/v1]
WS[WebSocket hub
in-memory]
MEDIA[Media service
thumbnails]
PUSH[Push notifier
VAPID]
end
subgraph Data
PG[(PostgreSQL 17)]
DISK[(local disk
S3-like Storage)]
end
UI -->|REST + WS| HTTP
UI --> WS
SW -->|Web Push| PUSH
HTTP --> PG
WS --> PG
PUSH --> PG
MEDIA --> DISK
HTTP --> MEDIA
WS -.->|presence check| PUSH
```
## Request flow for a message
```mermaid
sequenceDiagram
participant A as Alice (PWA)
participant API as API
participant PG as PostgreSQL
participant WS as WS hub
participant B as Bob (PWA)
A->>API: POST /conversations/{id}/messages
API->>PG: INSERT message
API->>WS: publish message.created
WS-->>A: message.created (echo)
WS-->>B: message.created
API->>PUSH: notify offline members
PUSH-->>B: Web Push (if offline)
API-->>A: 201 message
```
## Key decisions (see docs/adr)
| Area | Decision | Rationale |
|---|---|---|
| Shape | Monolith, one container | Family scale; trivial deploy; no fake microservices |
| Realtime | In-memory WS hub | Presence and delivery in one process; pg LISTEN/NOTIFY is the scaling path |
| Search | ILIKE substring | No extension dependency; instant at family scale; pg_trgm noted as upgrade |
| Media | S3-like Storage interface | Swap local disk for MinIO without touching handlers |
| Auth | JWT access + rotated refresh in httpOnly cookies | No token in JS; rotation limits replay |
| Crypto | TLS in transit; E2EE via libsignal on roadmap | Never hand-roll crypto |
## Deployment
docker-compose runs: api, postgres, victoriametrics, grafana.
See `deploy/` for compose + Grafana dashboard provisioning.
- **Metrics**: `GET /metrics` (Prometheus format), scraped by VictoriaMetrics
- **Grafana**: http://localhost:3000, dashboard «Messenger» auto-provisioned
- **Push**: optional; requires VAPID keys (`go run ./cmd/keygen`)