4.6 KiB
Chitai
Self-hosted eBook library manager. Users organise eBook files into libraries, which contain books (one book = one metadata record + one or more files on disk). Books carry authors, publishers, tags, series and identifiers, can be grouped into per-user bookshelves, and are read in-browser through built-in EPUB and PDF readers with reading-progress tracking. The catalogue is also exposed as an OPDS feed for e-reader apps, and progress syncs with KOReader devices via a KOSync-compatible endpoint.
Layout
| Path | What |
|---|---|
backend/ |
Litestar REST API + PostgreSQL. See backend/AGENTS.md. |
frontend/ |
SvelteKit SSR web app. See frontend/AGENTS.md. |
docker-compose.yml |
Production stack: db (postgres:17), backend, frontend. |
docs/screenshots/ |
Images used by README.md. |
shell.nix |
Root dev shell; composes the two sub-shells. |
Development environment
nix-shell from the repo root is the intended entry point. It pulls in both
backend/shell.nix and frontend/shell.nix, whose shellHooks do real work as a side effect:
- backend —
uv venv+uv sync, theninitdb/pg_ctl startintobackend/.postgres/(socket dir, not TCP),createdb chitai, and finally applies migrations withalchemy --config chitai.database.config.config upgrade --no-prompt.exitHookstops PostgreSQL on shell exit. - frontend —
pnpm install.
So entering the shell gives you a running database with an up-to-date schema; you do not need to
start Postgres yourself. Both hooks cd around, which can be surprising in scripts.
Without Nix you need: Python 3.13 + uv, Node 24 + pnpm, and PostgreSQL 17 with the pg_trgm
extension available.
Configuration
All backend settings are read by backend/src/chitai/config.py (Settings, pydantic-settings) with
the CHITAI_ prefix from the repo-root .env. .env.prod-example is the template; copy it to
.env for a fresh deployment.
.env is gitignored and contains a real CHITAI_TOKEN_SECRET — do not print, copy or commit it.
The frontend reads exactly one variable, VITE_BACKEND_API_URL
(frontend/src/lib/server/config.ts, defaulting to http://localhost:8000).
How a request flows
browser
└─ SvelteKit SSR node server
├─ hooks.server.ts reads `authToken` cookie, validates via GET /access/me,
│ populates locals.user / locals.api, redirects to /login otherwise
├─ $lib/api/*.remote.ts remote functions (query/command/form) → locals.api (ApiClient)
└─ routes/api/[...path] catch-all proxy, for browser-direct fetches (reader file streams)
└─ Litestar backend
└─ controllers/ → services/ → SQLAlchemy models → PostgreSQL
The JWT the frontend holds in the authToken cookie is the same bearer token the backend issues
from POST /access/login. The frontend never stores credentials beyond that cookie.
Commands
Backend (from backend/):
uv run litestar --app-dir src/chitai/ run --reload # dev server on :8000
pytest tests/ # needs Docker (pytest-databases)
ruff format src/
alchemy --config chitai.database.config.config make-migrations
alchemy --config chitai.database.config.config upgrade
Frontend (from frontend/):
pnpm dev # vite dev server on :5173
pnpm build # adapter-node output in build/
pnpm check # svelte-check — run before finishing
pnpm lint # prettier --check + eslint
pnpm format # prettier --write
API docs are served by the running backend at http://localhost:8000/schema/ (Swagger) and
/schema/openapi.json.
Cross-cutting rules
- Keep the two schema layers in sync. Backend request/response shapes live in
backend/src/chitai/schemas/(Pydantic); the frontend mirrors them as Zod schemas infrontend/src/lib/schema/. Changing one without the other produces runtime validation failures, not type errors. - Regenerate the OpenAPI types after changing API shapes.
frontend/src/lib/schema/openapi/schema.d.tsis generated from the backend's OpenAPI document withopenapi-typescript(a devDependency; there is nopackage.jsonscript for it, so it is run manually against a live backend). - Migrations are mandatory. The app runs with
create_all=False, so a model change without a matching Alembic revision will not reach the database. - Commit messages follow
type: summary—feat:,fix:,refactor:,chore:. - The three
README.mdfiles are user-facing. Agent-facing knowledge belongs in theAGENTS.mdfiles.