# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Environment / Secrets All project environment variables are stored in **Doppler**. When you need to access any secret or env var (e.g. API tokens, database URLs, credentials), fetch them via: ```bash doppler run -- # inject all secrets into a command doppler secrets get SECRET_NAME # inspect a specific secret ``` Never use `.env` files. Do not ask the user to provide secrets manually — they are available via Doppler. ## Commands ### Docker (via `just` — the primary way to run services) All services use Doppler for secrets injection. The `just` commands handle this automatically. ```bash just up # Start all services in background just up-fg # Start all services, stream logs just down # Stop all services just down-volumes # Full reset (destructive — removes all volumes) just build # Rebuild all Docker images just build-svc backend # Rebuild a specific service just restart # Stop + rebuild + start just logs # Tail all logs just log backend # Tail a specific service just shell backend # Open shell in running container just init # One-shot init: MinIO buckets, PocketBase collections, Postgres ``` ### Backend (Go) ```bash cd backend go vet ./... go test -short -race -count=1 -timeout=60s ./... go test -short -race -count=1 -run TestFoo ./internal/somepackage/ go build ./cmd/backend go build ./cmd/runner ``` ### Frontend (SvelteKit) ```bash cd ui npm run dev # Dev server at localhost:5173 npm run build # Production build npm run check # svelte-check (type-check) npm run paraglide # Regenerate i18n messages (run after editing messages/*.json) ``` ## Architecture Three services communicate via PocketBase records and a Redis/Valkey task queue: **Backend** (`backend/cmd/backend`) — HTTP REST API. Handles reads, enqueues tasks to Redis via Asynq, returns presigned MinIO URLs. Minimal processing; delegates heavy work to the runner. **Runner** (`backend/cmd/runner`) — Asynq task worker. Processes scraping, TTS audio generation, AI text/image generation. Reads/writes PocketBase and MinIO directly. **UI** (`ui/`) — SvelteKit 2 + Svelte 5 SSR app. Consumes the backend API. Uses Paraglide JS for i18n (5 locales). ### Data layer | Service | Role | |---------|------| | **PocketBase** (SQLite) | Auth, structured records (books, chapters, tasks, subscriptions) | | **MinIO** (S3-compatible) | Object storage — chapter text, audio files, images | | **Meilisearch** | Full-text search (runner indexes, backend reads) | | **Redis/Valkey** | Asynq task queue + presigned URL cache | ### Key backend packages - `internal/backend/` — HTTP handlers and server setup - `internal/runner/` — Task processor implementations - `internal/storage/` — Unified MinIO + PocketBase interface (all data access goes through here) - `internal/orchestrator/` — Task orchestration across services - `internal/taskqueue/` — Enqueue helpers (backend side) - `internal/asynqqueue/` — Asynq queue setup (runner side) - `internal/config/` — Environment variable loading (Doppler-injected at runtime, no .env files) - `internal/presigncache/` — Redis cache for MinIO presigned URLs ### UI routing conventions (SvelteKit) - `+page.svelte` / `+page.server.ts` — Page + server-side load - `+layout.svelte` / `+layout.server.ts` — Layouts - `routes/api/` — API routes (`+server.ts`) - `lib/audio.svelte.ts` — Client-side audio playback store (Svelte 5 runes) ## Key Conventions - **Svelte 5 runes only** — use `$state`, `$derived`, `$effect`; do not use Svelte 4 stores or reactive statements. - **Modern Go idioms** — structured logging via `log/slog`, OpenTelemetry tracing throughout. - **No direct MinIO/PocketBase client calls** outside the `internal/storage/` package. - **Secrets via Doppler** — never use `.env` files. All secrets are injected by Doppler CLI. - **CI/CD is Gitea Actions** (`.gitea/workflows/`), not GitHub Actions. Use `gitea.ref_name`/`gitea.sha` variables. - **Git hooks** in `.githooks/` — enable with `just setup`. - **i18n**: translation files live in `ui/messages/{en,es,fr,de,pt}.json`; run `npm run paraglide` after editing them. - **Error tracking**: GlitchTip with per-service DSNs (backend id/2, runner id/3, UI id/1) stored in Doppler.