| infra | ||
| packages | ||
| .dockerignore | ||
| .editorconfig | ||
| .env.example | ||
| .gitignore | ||
| .prettierrc.json | ||
| claude.md | ||
| docker-compose.override.yml | ||
| docker-compose.prod.yml | ||
| docker-compose.yml | ||
| eslint.config.mjs | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| README.md | ||
| specs.md | ||
| tsconfig.base.json | ||
| turbo.json | ||
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
# 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:
POST /auth/register(or/auth/login) and copytokens.accessTokenfrom the response.- Click Authorize (top right), paste the access token, and authorize.
- Protected endpoints (
/me,/auth/sessions,/auth/centrifugo-token, …) now work from "Try it out".
Dev notes:
- The backend runs in Docker with
node_modulesbaked 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
backendupstream 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)
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)
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.