# Altricade Messenger A self-hosted, cross-platform realtime messenger (chat + later calls). TypeScript everywhere, pnpm monorepo. See `specs.md` for the full architecture brief. > **Status:** Phase 0 — skeleton + Docker. The full backend stack boots with one > command; no product features yet. Phases are built one at a time (see `specs.md` §8). ## Architecture in one paragraph Centrifugo is a **dumb, fast pipe**; the **backend is the brain**. Clients **send** over REST and **receive** over the Centrifugo WebSocket. The backend authorizes, persists to Postgres (source of truth), then publishes to Centrifugo which fans out to subscribers. Media lives in MinIO; only references travel through channels. Redis backs Centrifugo history/recovery and clustering. ## Monorepo layout ``` packages/ core/ # THE KEYSTONE — pure TS, imported by all (types, events, channels, ...) backend/ # Fastify service (Docker) — imports core web/ # React + Vite — imports core (Feature-Sliced Design) mobile/ # React Native + Expo (Phase 7) — placeholder desktop/ # Tauri shell over web build (Phase 7) — placeholder infra/ # centrifugo, nginx, postgres init docker-compose*.yml ``` `core` is consumed **as source** across packages, so a type change surfaces as an immediate compile error everywhere. Builds go through bundlers (Vite for web, tsup for backend); `tsc` is used only for type-checking. ## Prerequisites - Node.js ≥ 22 and pnpm (via `corepack enable`) - Docker + Docker Compose ## Local development ```bash # 1. Install the workspace pnpm install # 2. Configure env (copy the template, adjust if you like — dev defaults work) cp .env.example .env # 3. Bring up the whole backend stack (Postgres, Redis, MinIO, Centrifugo, # backend, nginx) with hot reload. The dev override is auto-loaded. docker compose up --build # 4. Run the web client (separate terminal) pnpm --filter @altricade/web dev ``` Endpoints (via the dev override): - Gateway (nginx): http://localhost:8080 - **API docs (Swagger UI): http://localhost:8080/docs** (or http://localhost:4000/docs) - API health: http://localhost:8080/api/health → `{ "status": "ok" }` - API readiness: http://localhost:8080/api/ready → 200 only when Postgres + Redis + MinIO are reachable - Web client: http://localhost:5173 - MinIO console: http://localhost:9001 ### Testing the API with Swagger Open the Swagger UI, then: 1. `POST /auth/register` (or `/auth/login`) and copy `tokens.accessToken` from the response. 2. Click **Authorize** (top right), paste the access token, and authorize. 3. Protected endpoints (`/me`, `/auth/sessions`, `/auth/centrifugo-token`, …) now work from "Try it out". > Dev notes: > - The backend runs in Docker with `node_modules` baked into the image. After > changing backend dependencies, recreate the container so it picks them up: > `docker compose up -d --build --force-recreate backend`. > - nginx resolves the `backend` upstream once at startup, so after recreating the > backend container, restart nginx too: `docker compose restart nginx` > (otherwise you'll get 502s from a stale cached IP). ## Quality gates (enforced mechanically — a violation fails the build) ```bash pnpm typecheck # strict TypeScript across all packages pnpm lint # ESLint: no any, no type assertions, no non-null !, FSD import boundaries pnpm build # bundle backend + build web pnpm format # prettier ``` Standards: strict TS, no `any`, no `as` type assertions, no `!`, functional iteration, and strict **Feature-Sliced Design** import boundaries (frontend) plus layered backend modules. Dependencies are always added via `pnpm add` (never hand-pinned) at their latest trusted versions. ## Deploy (production) ```bash docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build ``` Only nginx is published to the host; all other services stay on the private network. Secrets come from the environment — never commit a real `.env`.