# CLAUDE.md — Torrent Faker ## Project Purpose Torrent Faker is a fake BitTorrent seeder that announces upload stats to real trackers without transferring any actual data. It simulates realistic seeding behavior to satisfy tracker requirements. ## Tech Stack | Layer | Technology | |-----------|-------------------------------------| | Runtime | Bun | | Language | TypeScript (strict, ESNext) | | HTTP | Hono 4.x | | Config | YAML (`js-yaml`) + Zod validation | | Linting | Biome | | Frontend | Svelte 5 + Vite (in `ui/`) | ## Project Structure ``` src/ index.ts # Entry point: loads config, starts registry + HTTP server cli/index.ts # CLI client (talks to running API) config/Config.ts # Zod schema + config loader config/config.default.yml # Default config values api/ server.ts # Hono app, mounts routes, serves UI SeederRegistry.ts # Central in-memory store for all FakeSeeder instances response.ts # HTTP response helpers routes/ torrents.ts # GET/POST/DELETE /api/torrents status.ts # GET /api/status, GET /api/status/stream (SSE) config.ts # GET/PATCH /api/config core/ bencode/ # Bencode decoder + encoder client/ ClientProfile.ts # Profile interface + peer ID generation profiles/ # qbittorrent.ts, transmission.ts seeder/ FakeSeeder.ts # Announce loop, lifecycle (started → running → stopped) SpeedSimulator.ts # Gaussian noise + burst/stall events torrent/TorrentFile.ts # Parses .torrent, extracts info hash + tracker list tracker/ ITracker.ts # Tracker interface HttpTracker.ts # HTTP/HTTPS tracker (BEP 3) UdpTracker.ts # UDP tracker (BEP 15) TrackerResponse.ts # Response types config/config.yml # Runtime config (gitignored defaults) torrents/ # Drop .torrent files here ui/ # Svelte frontend (built to ui/dist/) ``` ## TypeScript Path Aliases ``` @core/* → src/core/* @api/* → src/api/* @config/* → src/config/* ``` ## Dev Commands ```bash bun run dev # Start server (hot-reload) bun run start # Start server (production) bun run cli # Run CLI against running server bun test # Run tests bun run lint # Biome check bun run format # Biome format --write bun run build:ui # Build Svelte UI to ui/dist/ bun run build # Full build (UI only for now) ``` ## Architecture ``` SeederRegistry └── FakeSeeder (one per torrent) ├── SpeedSimulator — random base rate, ±15% jitter, burst/stall └── Tracker[] ├── HttpTracker (BEP 3) └── UdpTracker (BEP 15) ``` - `SeederRegistry` is the single source of truth; routes call it, not FakeSeeder directly. - `FakeSeeder` extends `EventEmitter`; emits `announce`, `stopped`, `stateChange`. - `SeederRegistry` subscribes to seeder events and pushes SSE updates to connected clients. - Announce lifecycle: first call sends `event=started`, subsequent calls send `event=` (empty), final call on removal sends `event=stopped`. ## Configuration Runtime config file: `config/config.yml` (copied from `src/config/config.default.yml`). | Field | Default | Description | |---------------------------|-----------|------------------------------------------| | `port` | 3000 | HTTP server port (UI + API) | | `announcePort` | 6881 | Port reported to trackers | | `minUploadRateBytesPerSec`| 524288 | Min simulated speed (512 KB/s) | | `maxUploadRateBytesPerSec`| 2097152 | Max simulated speed (2 MB/s) | | `clientProfile` | qbittorrent | Client to impersonate | | `torrentsDir` | ./torrents | Directory for .torrent files | | `autoLoad` | true | Load all torrents in dir on startup | Config is Zod-validated at startup. `PATCH /api/config` persists changes back to `config.yml`. ## API Endpoints | Method | Path | Description | |--------|------------------------------|---------------------------------------| | GET | `/api/torrents` | List all active torrents | | POST | `/api/torrents` | Upload `.torrent` file (multipart) | | GET | `/api/torrents/:hash` | Stats + announce history for one | | DELETE | `/api/torrents/:hash` | Stop seeding and remove | | GET | `/api/status` | Global stats snapshot | | GET | `/api/status/stream` | SSE stream (stats, torrent, ping) | | GET | `/api/config` | Current config | | PATCH | `/api/config` | Update mutable config fields | | GET | `/*` | Serve Svelte SPA from `ui/dist/` | ### SSE Event Types - `stats` — global stats on every change - `torrent` — per-torrent state update on announce/change - `torrents` — full list on initial connection - `ping` — keep-alive every 30s ## CLI Requires a running server. Reads `TORRENT_FAKER_API` env var (default: `http://localhost:3000`). ```bash bun run cli add # Start fake-seeding bun run cli list # List active torrents bun run cli remove # Stop and remove (hash prefix OK) bun run cli status # Global stats ``` ## Client Profiles | Profile | Peer ID prefix | User-Agent | |---------------|----------------|-----------------------| | `qbittorrent` | `-qB4520-` | `qBittorrent/4.5.2` | | `transmission`| `-TR3000-` | `Transmission/3.00` | Each seeder generates a unique 20-byte peer ID per session. ## Code Conventions - Biome enforces formatting; run `bun run format` before committing. - Zod schemas for all external input (config file, API request bodies). - No ORM — binary protocols implemented manually (bencode, UDP packets). - EventEmitter pattern for seeder → registry communication. - SSE for live UI updates (no WebSockets, no polling).