Files
vantablackTea 6fc6d977bf
CI / Go (build + vet + test) (push) Canceled after 0s
CI / Web (build + lint) (push) Canceled after 0s
added media fixes, search, push updates, pwa, metrics. preparing release 1.0
2026-09-27 23:26:15 +03:00

2.4 KiB

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

flowchart LR
    subgraph Browser[Browser / PWA]
        UI[React + Vite + Tailwind]
        SW[Service Worker<br/>offline cache + Web Push]
    end

    subgraph Server[Go binary - single process]
        HTTP[REST API /api/v1]
        WS[WebSocket hub<br/>in-memory]
        MEDIA[Media service<br/>thumbnails]
        PUSH[Push notifier<br/>VAPID]
    end

    subgraph Data
        PG[(PostgreSQL 17)]
        DISK[(local disk<br/>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

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)