# Server Architecture ## Folder Structure ``` server/ room/ P2P room domain friends/ Friends and DM domain calls/ Voice/video call domain profile/ Local user profile domain identity/ Cryptographic identity domain messages/ Chat message domain ``` Each domain folder follows the same pattern: | File | Role | |---|---| | `store.ts` | Read and write data to disk | | `utils.ts` | Pure utility functions | | `handler.ts` | WebSocket message handlers | | `events.ts` | P2P event handlers | `watcher.ts` | Live P2P subscriptions (room only) | | `tracker.ts` | In-memory runtime state (calls only) | ## Domain Responsibilities ### `room/` Everything about P2P rooms - persisting joined rooms, watching for live updates, querying room history for ownership and moderation state, and handling all room-related WebSocket messages (create, join, leave, admin operations). ### `friends/` The friends system - persisting the friends list, pure lookup utilities, handling friend request/accept/remove WebSocket messages, and handling incoming P2P friend events (joining DM rooms on accept). ### `calls/` Voice/video calls - tracking which calls are currently active in memory, and relaying call signaling WebSocket messages (start, join, signal relay, end) through the room log. PeerJS runs in the browser; the server only forwards the opaque `call-signal` payloads, so it needs no PeerServer broker. ### `profile/` The local user's display data - persisting name, avatar, and presence status to disk, utility functions for normalising presence and generating anonymous usernames, and handling profile update WebSocket messages. ### `identity/` Cryptographic identity operations - handling WebSocket messages for seed phrase backup, seed phrase import, and local database reset. The core identity logic lives in `lib/identity.ts`; ### `messages/` Chat message operations - handling all message-related WebSocket messages (send, edit, react, pin, file upload/download, channel management, custom emoji). ## Server-Level Files | File | Role | |---|---| | `server.ts` | Entry point | | `ws-dispatch.ts` | Routes incoming WebSocket messages to the correct handler | | `context.ts` | Shared types: `SessionContext` and `PipelineContext` | | `broadcast.ts` | Pushes messages to all connected WebSocket clients | | `http-handler.ts` | Serves static client files and handles the reset endpoint | | `quibble-init.ts` | Initialises the P2P node with retry logic for the storage lock | | `rtc.ts` | Resolves the ICE server list pushed to clients (delegates to `lib/rtc-config.ts`) | | `net.ts` | Finds an open TCP port on startup | ## ICE servers On connect the server sends each client an `rtc-config` message with the ICE server list. Defaults come from `lib/rtc-config.ts` (Cloudflare STUN + shared Metered TURN). Override with environment variables: - `QUIBBLE_ICE_SERVERS_JSON` - a full JSON array of `{ urls, username?, credential? }`, used verbatim. - `TURN_URL`, `TURN_USERNAME`, `TURN_CREDENTIAL` - point every client at your own TURN server. Users can also set their own STUN/TURN server in Settings, which overrides the server defaults locally. See [stun-turn.md](stun-turn.md).