No-account chess puzzle trainer built as a pnpm monorepo. It serves PGN-based tactical puzzles with variation trees, validates moves on the server, keeps recent-session history, and adds optional browser-side Stockfish evaluation for replay and study.
- PGN puzzle import with full variation-tree support
- Server-side move validation and session orchestration
- Explore and mainline solving modes
- Puzzle history with replay, previous/next transport, tree review, and direct loading by public ID
- Optional autoplay, auto-next, one-try mode, restart-in-place, hints, reveal, skip variation, and auto-queen
- Lichess-style PGN explorer review with back/forward/live navigation
- Browser Stockfish eval bar, prepared instant hints, and local UI preferences
- Adjustable animation speed alongside theme and presentation settings
- Multilingual UI with native-language labels and RTL handling
- Local Postgres support with
pg-memfallback for lightweight development
- Frontend: React, Vite, TypeScript, Chessground
- Backend: Fastify, TypeScript, Postgres
- Shared domain logic:
packages/chess-core - Database layer:
packages/db - Shared config/helpers:
packages/config - Workspace tooling:
pnpm
apps/web: React clientapps/api: Fastify APIpackages/chess-core: PGN parsing and puzzle/session engine logicpackages/db: migrations, repositories, DB clientpackages/config: shared env/config helpersdocs: architecture and flow documentationops: deployment and operations notesassets: screenshots and app icon
- Copy the example env file:
cp .env.example .env- For zero-setup local development, set this in
.env:
DATABASE_URL=pgmem://local- Install dependencies:
npx pnpm@10.5.2 install- Start both apps:
./start.shDefault local URLs:
- Web:
http://localhost:5173 - API:
http://localhost:3001 - Health check:
http://localhost:3001/health
start.sh waits for both services to become reachable and stops them together on Ctrl+C.
The example env file includes the main runtime settings:
API_PORT,API_HOSTVITE_API_BASE_URLDATABASE_URLCOOKIE_SECRETALLOWED_ORIGINSIMPORT_TOKENSEED_PGN_FILESEED_MAX_PUZZLES
In non-production mode, if the configured Postgres database is unavailable, the API falls back to in-memory pg-mem.
The API can run against either:
- Postgres via
DATABASE_URL - in-memory
pgmem://localfor development
Run migrations manually:
npx pnpm@10.5.2 --filter @chess-web/api migrateImport a PGN file from the CLI:
npx pnpm@10.5.2 --filter @chess-web/api import:pgn -- --file /path/to/puzzles.pgn --token "$IMPORT_TOKEN"Bundled puzzle data also exists at puzzle_exports/stack_min_2plies_256k.pgn.
If SEED_PGN_FILE is set and the puzzle table is empty, the API can seed puzzles automatically on startup.
From the repo root:
npx pnpm@10.5.2 -r typecheck
npx pnpm@10.5.2 -r lint
npx pnpm@10.5.2 -r test
npx pnpm@10.5.2 -r buildOr run the helper build script:
./build.shRun apps separately:
npx pnpm@10.5.2 --filter @chess-web/api dev
npx pnpm@10.5.2 --filter @chess-web/web dev- Restarting a solved puzzle resets the current session in place instead of creating a duplicate history entry.
- Hint previews are prepared when a puzzle loads so the first hint action can appear immediately.
- The PGN explorer supports in-line review plus back, forward, and live-position controls.
- Animation speed is configurable from the settings panel and affects both board motion and puzzle feedback timing.
Session routes:
POST /api/v1/session/startPOST /api/v1/session/loadPOST /api/v1/session/restartPOST /api/v1/session/movePOST /api/v1/session/hintPOST /api/v1/session/revealPOST /api/v1/session/skip-variationPOST /api/v1/session/nextPOST /api/v1/session/prefetch-nextPOST /api/v1/session/historyPOST /api/v1/session/history/clearPOST /api/v1/session/tree
Puzzle and admin routes:
GET /api/v1/puzzles/countGET /api/v1/admin/import-statusPOST /api/v1/admin/import-bundledGET /api/v1/puzzles/:publicId/treein non-production debug modeGET /health
The admin import routes require x-import-token to match IMPORT_TOKEN.
- docs/README.md
- docs/architecture.md
- docs/api-and-session-flow.md
- docs/frontend-flow.md
- docs/database-model.md
- ARCHITECTURE.md
- Repository licenses: LICENSE, LICENSE.txt
- Piece asset notice:
apps/web/public/pieces/cburnett/NOTICE.txt - Sound pack notices:
apps/web/public/sounds/lichess-standard/LICENSE.txtapps/web/public/sounds/lichess-sfx/LICENSE.txt
- Curated runtime dependency notices: THIRD_PARTY_LICENSES.json
Regenerate the curated runtime notice file with:
node scripts/generateThirdPartyLicenses.mjs- Browser-side anti-download prevention is only best-effort.
- The app uses rate limiting and avoids bulk export-style puzzle endpoints.
- The default runtime audio pack is
lichess-standard. - Deployment notes live in ops/oracle-cloudflare.md.


