Architecture Overview¶
Semaphore Chat is a self-hosted communication platform with real-time messaging, voice/video calls, and community management.
System Architecture¶
graph LR
Browser["Browser / Electron"]
Frontend["Frontend<br/>(React + Vite)"]
Backend["Backend<br/>(NestJS)"]
PostgreSQL[(PostgreSQL)]
Redis[(Redis)]
LiveKit["LiveKit Server"]
Browser -->|HTTP/WS| Frontend
Browser -->|REST + Socket.IO| Backend
Browser -->|WebRTC| LiveKit
Backend --> PostgreSQL
Backend --> Redis
Backend --> LiveKit
Frontend -.->|Static assets| Browser Communication Patterns¶
| Protocol | Technology | Purpose |
|---|---|---|
| REST | OpenAPI-generated client | CRUD operations, auth, file uploads |
| WebSocket | Socket.IO + Redis adapter | Real-time events (messages, presence, notifications) |
| WebRTC | LiveKit | Voice, video, screen sharing |
REST API¶
The backend exposes an OpenAPI spec. The frontend uses a generated TypeScript client (frontend/src/api-client/) -- never raw fetch or axios. When backend controllers change, regenerate the client:
docker compose run --rm backend pnpm run generate:openapi
docker compose run --rm frontend sh -c 'OPENAPI_SPEC_PATH=/spec/openapi.json pnpm exec openapi-ts'
WebSocket Events¶
Socket.IO connects the browser to the backend for real-time updates. In production, the Redis adapter coordinates messages across multiple backend pods.
Events flow through a handler registry on the frontend (socket-hub/handlers/). Each handler either updates the TanStack Query cache directly or invalidates it to trigger a refetch. See WebSocket Patterns for details.
Voice & Video¶
LiveKit handles all media transport. The backend generates LiveKit tokens and manages room state. Channel IDs are used as LiveKit room IDs.
Shared Package¶
@semaphore-chat/shared (shared/src/) provides type-safe contracts between frontend and backend:
- Events --
ServerEventsenum with all WebSocket event names - Payloads -- TypeScript interfaces for each event's data shape
- Type maps -- Maps event names to payload types for type-safe handlers
Key Design Decisions¶
| Decision | Choice | Rationale |
|---|---|---|
| Database | PostgreSQL + Prisma | Relational model with strong consistency and referential integrity; Prisma provides type safety |
| State management | TanStack Query v5 | Server state only -- no client-side store needed; cache invalidation via WebSocket |
| Real-time | Socket.IO + Redis adapter | Multi-pod scaling, automatic reconnection, room-based broadcasting |
| Voice/Video | LiveKit | Open-source SFU with WebRTC, supports screen sharing and recording |
| Auth | JWT + Passport | Stateless tokens, refresh token rotation, works with WebSocket |
| Schema management | Prisma Migrate | SQL migration files for reproducible schema changes |