# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Chad is a real-time voice/video chat server (think Discord-like) built with Fastify, Socket.IO, and mediasoup for WebRTC media handling. It uses SQLite via Prisma ORM and Lucia for session-based authentication. The client is a Tauri desktop app (separate repo).

## Commands

- **Start server:** `yarn start` (runs `ts-node --transpile-only server.ts`)
- **Deploy DB (migrate + seed + generate):** `yarn db:deploy`
- **Generate Prisma client after schema changes:** `npx prisma generate`
- **Create a migration:** `npx prisma migrate dev --name <name>`
- **Lint:** `npx eslint .`
- **Package manager:** Yarn 4 (corepack). Do not use npm.

## Architecture

### Entry Point & Plugin System

`server.ts` creates a Fastify instance and uses `@fastify/autoload` to auto-register everything in `plugins/` and `routes/` (prefixed under `/chad`). Plugins use `fastify-plugin` (`fp`) with named dependencies to control load order.

### Plugin Load Order (dependency chain)

1. `plugins/auth.ts` — Adds `req.user` / `req.session` via Lucia cookie validation on every request (preHandler hook)
2. `plugins/mediasoup-worker.ts` — Creates a mediasoup Worker, decorates `fastify.mediasoupWorker`
3. `plugins/mediasoup-router.ts` — Creates a mediasoup Router (depends on worker), decorates `fastify.mediasoupRouter`. Configures supported audio/video codecs (Opus, VP8, VP9, H.264, AV1)
4. `plugins/socket.ts` — Creates Socket.IO server at `/chad/ws` (depends on worker + router), decorates `fastify.io`. Registers socket handlers on `fastify.ready()`

### Socket Handlers (`socket/`)

- `socket/webrtc.ts` — Main WebRTC signaling: join/leave, transport creation, producer/consumer lifecycle, audio level observation, active speaker detection
- `socket/channel.ts` — Channel-based socket logic (in development on `channels` branch)

Both handlers authenticate by looking up `socket.handshake.auth.userId` against the DB.

### REST Routes (`routes/`)

All routes are prefixed with `/chad` via autoload config.

- `routes/auth.ts` — `/register`, `/login`, `/logout`, `/me`
- `routes/user.ts` — `/preferences` (GET/PATCH), `/profile` (PATCH). Profile changes broadcast to connected socket peers via `clientChanged` event.

### Type System (`types/socket.ts`)

Fully typed Socket.IO events: `ClientToServerEvents`, `ServerToClientEvents`, `SocketData`. The `SomeSocket` union type covers both live `Socket` and `RemoteSocket` (from `fetchSockets()`).

### Database

- SQLite with Prisma 7 + `@prisma/adapter-better-sqlite3`
- Schema at `prisma/schema.prisma`, generated client at `prisma/generated/client/`
- Models: `User`, `Session` (Lucia), `UserPreferences`, `Channel`
- Seed creates a persistent "Default" channel
- Config in `prisma.config.ts` using `dotenv` for `DATABASE_URL`

### Key Patterns

- Request validation uses Zod schemas defined inline in route handlers
- DTOs in `dto/` define Prisma select objects with `satisfies Prisma.*Select` for type-safe projections
- `utils/socket-to-client.ts` maps socket data to the `ChadClient` interface sent to clients
- ESLint uses `@antfu/eslint-config` (flat config) with `no-console` and `n/prefer-global/process` disabled

### Environment Variables

- `PORT` — Server port (default: 4000, Docker: 80)
- `DATABASE_URL` — SQLite path (e.g., `file:../data/database.db`)
- `ANNOUNCED_ADDRESS` — Public IP for WebRTC ICE candidates (default: `127.0.0.1`)
- `CORS_ORIGIN` — Socket.IO CORS origin (default: `*`)

### Docker

`Dockerfile` requires python3 and build-essential for mediasoup native compilation. Runs `yarn db:deploy && yarn start`.
