Appearance
Architecture
High-Level Overview
RustChat is composed of three runtime services and four external dependencies:
| Service | Technology | Purpose |
|---|---|---|
| Backend | Rust (Axum 0.8 + Tokio) | HTTP API, WebSocket hub, business logic |
| Frontend | Vue 3.5 + TypeScript + Pinia | Single-page web application |
| Push Proxy | Rust (Axum) | Mobile push notification gateway (FCM / APNS) |
External dependencies:
| Dependency | Purpose | Required |
|---|---|---|
| PostgreSQL 16+ | Primary data store | Yes |
| Redis 7+ | Pub/sub, rate limiting, sessions | Yes |
| S3-compatible storage | File uploads | Yes |
| FCM / APNS | Mobile push notifications | Optional |
| SMTP | Email, password reset | Optional |
| OAuth providers | SSO login | Optional |
| Meilisearch | Full-text search | Optional (via search profile) |
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Web Client │────▶│ RustChat API │◀────│ Push Proxy │
│ (Vue.js SPA) │ │ (Rust / Axum) │ │ (Mobile Push) │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│
┌───────────────────┼───────────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────────┐
│PostgreSQL│ │ Redis │ │S3-compatible │
│(Primary) │ │(Pub/Sub) │ │(File Store) │
└──────────┘ └──────────┘ └──────────────┘Request Flow
A typical HTTP request flows through these layers:
Client → Reverse Proxy (optional) → Axum Router → Middleware → Handler → Service → Database/S3/Redis- Client request → Reverse proxy (optional, for TLS)
- Axum router → Matches path to handler
- Middleware → Auth (JWT validation), rate limiting, CORS, tracing, request ID
- Handler → Thin HTTP layer, delegates to services
- Service → Business logic, database queries, external calls
- Response → Serialized via
Result<T, AppError>with consistent JSON error format
Authentication Flow
┌─────────┐ POST /api/v1/auth/login ┌──────────┐
│ Client │ ─────────────────────────────▶│ Backend │
│ │ │ │
│ │◀──────────────────────────────│ │
│ │ { access_token, refresh } │ │
│ │ │ │
│ │ WS /api/v1/ws │ │
│ │ Authorization: Bearer ... │ │
│ │ ─────────────────────────────▶│ │
│ │ │ │
│ │◀──────────────────────────────│ │
│ │ Real-time events │ │
└─────────┘ └──────────┘- Client POSTs credentials to
/api/v1/auth/login - Backend validates against PostgreSQL, issues JWT access + refresh tokens
- Client includes
Authorization: Bearer <token>header on all API requests - WebSocket connection uses the same token via header (or query param in development)
- Refresh token endpoint (
/api/v1/auth/refresh) silently extends sessions
API Surface
The backend exposes two API versions on the same HTTP server:
| API | Path | Purpose |
|---|---|---|
| Native | /api/v1/* | Web client, modern features |
| Compatible | /api/v4/* | Mattermost-compatible for mobile/desktop clients |
Both share the same service layer and database. The v4 layer translates request/response formats to match Mattermost's API contract.
Real-Time Communication
Two WebSocket endpoints share a common event core:
| Endpoint | Format | Clients |
|---|---|---|
/api/v1/ws | Internal envelope | Web app |
/api/v4/websocket | Mattermost framing | Mobile apps, desktop clients |
WebSocket Event Flow
┌─────────┐ WS connection ┌──────────┐
│ Client A│◀──────────────────────▶│ Backend 1│
│ │ │ │
│ │ │ Redis │
│ │ │ pub/sub │
│ │ │ │
│ Client B│◀──────────────────────▶│ Backend 2│
└─────────┘ └──────────┘- Client A sends a message via WebSocket to Backend 1
- Backend 1 persists to PostgreSQL
- Backend 1 publishes event to Redis pub/sub channel
- Backend 2 (and any other instances) receives the event from Redis
- Backend 2 forwards the event to connected clients (including Client B)
This design allows horizontal scaling: add more backend instances behind a load balancer, and Redis coordinates real-time delivery across all of them.
File Upload Flow
┌─────────┐ POST /api/v1/files ┌──────────┐ PUT ┌─────────┐
│ Client │ ───────────────────────▶│ Backend │ ──────────▶│ S3 │
│ │ multipart/form-data │ │ internal │ (RustFS)│
│ │ │ │ client │ │
│ │◀─────────────────────── │ │◀───────────│ │
│ │ { file_id, api url } │ │ │ │
│ │ │ │ │ │
│ │ GET /api/v1/files/...│ │ │ │
│ │ ───────────────────────▶│ │ │ │
│ │ │ │ proxy │ │
│ │◀─────────────────────── │ │◀───────────│ │
│ │ file bytes (auth'd) │ │ │ │
└─────────┘ └──────────┘ └─────────┘- Client uploads file via multipart POST to backend
- Backend validates channel membership before reading the upload body
- Backend uploads to S3-compatible storage through the server-side storage client
- Backend returns file metadata and an authenticated RustChat API URL to client
- Client requests file download through backend (authenticated)
- Backend proxies the file from S3 with auth check — no presigned URL leaks to end users
The native presign endpoint is intentionally unsupported for end-user clients. Uploads should use multipart POST /api/v1/files, and reads should use the returned RustChat API URL. Mattermost-compatible file link responses and custom emoji image responses also return or stream through RustChat API URLs rather than direct S3 links.
Deployment Topology
Single-Node (Evaluation / Small Team)
┌─────────────────────────────────────────────┐
│ Single Host │
│ ┌─────────┐ ┌─────────┐ ┌─────────────┐ │
│ │ Nginx │ │Backend │ │Frontend │ │
│ │(TLS) │ │(Port │ │(Port 8080) │ │
│ │(443) │ │ 3000) │ │ │ │
│ └────┬────┘ └────┬────┘ └─────────────┘ │
│ │ │ │
│ └────────────┘ │
│ ┌─────────┐ ┌─────────┐ ┌─────────────┐ │
│ │PostgreSQL│ │ Redis │ │S3 (RustFS) │ │
│ │(Port │ │(Port │ │(Port 9000) │ │
│ │ 5432) │ │ 6379) │ │ │ │
│ └─────────┘ └─────────┘ └─────────────┘ │
└─────────────────────────────────────────────┘Multi-Node (Production)
┌─────────────┐
│ Load Balancer│
│ (TLS) │
└──────┬──────┘
│
┌────────────────┼────────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Backend 1│ │ Backend 2│ │ Backend N│
│ │◄───►│ │◄───►│ │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
└────────────────┼────────────────┘
│
┌─────────┴─────────┐
▼ ▼
┌──────────┐ ┌──────────┐
│ PostgreSQL│ │ Redis │
│ (HA) │ │ (Cluster)│
└──────────┘ └──────────┘
│
▼
┌──────────┐
│S3 (Cloud) │
└──────────┘Key differences:
- Load balancer distributes HTTP traffic (sticky sessions recommended for WebSocket)
- Redis pub/sub synchronizes real-time events across backend instances
- PostgreSQL and S3 are shared external services
- Frontend is a static SPA that can be served by CDN or any static file server