Your private Chess.com. Self-hosted, AI-coached, family-friendly.
Stockfish + your own LLM, in one Docker container.
- Your games stay home. Single Docker container on a Pi / NAS / old laptop. No cloud, no telemetry, no upsell.
- Bring your own LLM. The AI coach runs against your own Ollama host. The coach is render-only — chess facts are computed server-side by Stockfish + chess.js, so a small local model can't hallucinate moves or pieces.
- Made for a household, not a stadium. Multi-user with admin console, per-profile language, kid-mode blunder warnings, "horsey" piece names for the youngest profiles.
Patzer is a tiny, self-hosted take on the Chess.com / Lichess workflow you actually use:
- Game Review — pull your public Chess.com games, analyze with bundled Stockfish, get Lichess-style classifications (Best / Excellent / Good / Inaccuracy / Mistake / Blunder / Brilliant / Miss), accuracy %, eval graph, mistake markers.
- Play vs Bot — full games against Stockfish at seven named tiers (Kid → Stockfish max), all standard time controls, premoves enabled, kid-mode blunder warnings.
- Play vs Friend — real-time PvP between profiles on the same server over WebSocket.
- AI Coach (your LLM) — point at any Ollama host. Audience-tuned voices for Kid / Beginner / Intermediate / Advanced. Anti-hallucination by design — chess facts are computed server-side; the LLM only renders them.
- Family-ready — multi-user with admin console, per-profile language, kid-mode blunder warnings, "horsey" piece names for the youngest profiles.
- Multilingual — EN + BG out of the box, UI and coach prompts. PRs for more languages welcome.
- Self-hosted, single container — runs on a Pi, a NAS, an old laptop. Your games never leave home.
Pick one of these. The Docker options open http://localhost:8800; the Codespaces option opens in your browser. Either way, the first visit walks you through a setup wizard.
Try it in your browser
Spins up a temporary Codespace with Patzer + Stockfish pre-installed. Wait ~60 seconds for npm install + dev server to start, then click the forwarded port labelled "Patzer (Vite dev — open this)". You get the full app except the AI Coach narration (which needs an Ollama host on your network — see below).
docker run
docker run -d \
-p 8800:8800 \
-v patzer-data:/app/data \
--name patzer \
ghcr.io/SikamikanikoBG/patzer:latestdocker compose
services:
patzer:
image: ghcr.io/SikamikanikoBG/patzer:latest
container_name: patzer
restart: unless-stopped
ports:
- "8800:8800"
volumes:
- patzer-data:/app/data
volumes:
patzer-data:What works without any extras: play vs Stockfish, play vs friend on the same server, move classification, accuracy %, eval graph, opening detection. The setup wizard takes you straight to a working board.
What needs Ollama: the AI Coach commentary voice. Until you point Patzer at an Ollama host, the Coach panel just shows the engine facts in plain text.
What needs a Chess.com username: importing your public games for review. Without it you can still load PGNs by paste or play live and review from the move list.
You'll want, optionally:
- For the AI Coach: an Ollama server reachable from the Patzer container. The wizard validates the URL and lists available models for you. Patzer accepts loopback / RFC1918 /
*.localOllama hosts only — public-Internet model proxies aren't supported here. - For Game Review on your own games: a Chess.com username (entered later in Settings).
To use a different host port, run with -p 9000:8800 (or set HOST_PORT=9000 if you're using docker compose).
If you're terminating TLS at a reverse proxy, set COOKIE_SECURE=true in the container's environment so session cookies aren't shipped over plaintext HTTP.
| Patzer | Lichess Studio | Chess.com Review | Aimchess | |
|---|---|---|---|---|
| Self-hosted | ✅ | ❌ | ❌ | ❌ |
| LLM coach (BYO model) | ✅ | ❌ | ❌ | ❌ |
| Imports Chess.com | ✅ | ❌ | ✅ | ✅ |
| Multi-user / family | ✅ | ❌ | ❌ | ❌ |
| Multilingual coach | ✅ | partial UI | ❌ | ❌ |
| Free | ✅ | ✅ | 💳 | 💳 |
┌────────────────────────── one Docker container ───────────────────────────┐
│ │
│ ┌──────────────┐ HTTP / WS ┌────────────────────────────────────┐ │
│ │ Browser │ ◀───────────▶ │ Hono server (Node, TypeScript) │ │
│ │ React + Vite│ │ ├─ /api/auth, /games, /analyze… │ │
│ │ Tailwind │ │ ├─ /ws/play (live games) │ │
│ │ chessground │ │ └─ /ws/lobby (challenges) │ │
│ └──────────────┘ └────────┬───────────────┬───────────┘ │
│ │ │ │
│ UCI ▼ ▼ HTTP │
│ ┌──────────┐ ┌────────────────┐ │
│ │ Stockfish│ │ Ollama (your │ │
│ │ (native) │ │ machine, LAN) │ │
│ └──────────┘ └────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────┐ │
│ │ SQLite │ (volume: /app/data) │
│ └──────────────┘ │
└────────────────────────────────────────────────────────────────────────────┘
The coach is render-only: every prompt is built from a pre-computed fact list (piece inventory, captured pieces, recent moves in plain English, in-check flag, engine PV) and the LLM is forbidden from inventing moves or pieces. See server/src/coach/prompts.ts and the CHANGELOG 2.1.0 entry for the why.
Requirements: Node.js ≥ 20.11.
git clone https://github.com/SikamikanikoBG/patzer.git
cd patzer
npm install
# Windows: download Stockfish into ./bin/
npm run setup
# Linux/macOS: install Stockfish via your package manager
# apt install stockfish / brew install stockfish
npm run dev- Server: http://localhost:8800
- Vite dev server (HMR): http://localhost:5173 — proxies
/apiand/wsto the server.
Useful scripts:
npm run typecheck # tsc -b across server + web (no emit)
npm run build # production build of both workspacesTwo equivalent scripts are bundled — deploy.ps1 for Windows hosts,
deploy.sh for Linux / macOS hosts. Both tar the source, scp it to the
target, then run docker compose build && up -d over SSH.
Create .env.deploy (gitignored) on the workstation you're deploying from:
HOST=user@192.168.x.x
REMOTE_DIR=/home/user/patzer
SUDO_PASS=... # only if your user is not in the docker group on the target
HOST_PORT=8800
Then:
# Windows
.\deploy.ps1 # tar source → ssh, build & start
.\deploy.ps1 -NoBuild # restart without rebuilding
.\deploy.ps1 -Logs # tail logs after deploy# Linux / macOS
./deploy.sh # tar source → ssh, build & start
./deploy.sh --no-build # restart without rebuilding
./deploy.sh --logs # tail logs after deployAll user-facing configuration is done through the UI and persisted in SQLite. The only environment variables are operational:
| Var | Default | What it does |
|---|---|---|
PORT |
8800 |
HTTP listen port |
HOST |
0.0.0.0 |
Bind address |
DB_PATH |
./data/chess.db |
SQLite database file |
STOCKFISH_PATH |
(auto) | Override Stockfish binary path |
SESSION_SECRET |
(auto-generated) | Cookie signing secret. Persisted on first run. |
COOKIE_SECURE |
false |
Set to true when terminating TLS at a reverse proxy so session cookies are flagged Secure. |
System settings (Ollama URL, default coach model, Stockfish path override) live in Admin → System. Per-profile settings (language, audience, coach behavior, TTS voice, Chess.com username) live in Settings.
Each played move is compared against the engine's best move at the same position. We compute the win-percentage drop using the Lichess sigmoid (100 / (1 + exp(-0.00368208 · cp))) and combine it with real centipawn loss to classify:
| Classification | Win-% drop | Centipawn loss |
|---|---|---|
| Best ★ | < 0.5 | < 8 (or engine's #1) |
| Excellent ✓ | < 2 | < 25 |
| Good · | < 5 | < 60 |
| Inaccuracy ?! | < 10 | — |
| Mistake ? | < 20 | — |
| Blunder ?? | ≥ 20 | — |
Brilliant (!!) fires on calculated sacrifices that keep a winning eval; Miss flags blunders that gave away a winning advantage. Per-game accuracy is the harmonic-friendly Lichess formula 103.1668 · exp(-0.04354 · Δwin%) - 3.1669, clamped to [0, 100].
Estimated Elo is calibrated against published Lichess/Chess.com ACPL-vs-rating data — ACPL ~5 → 2700+, ACPL 30 → ~1900, ACPL 80 → ~1200. Accuracy nudges this ±200 Elo at most.
- Server: Node 20 · TypeScript · Hono · better-sqlite3 · chess.js ·
ws· native Stockfish - Web: React 18 · Vite · Tailwind CSS · chessground · framer-motion · react-i18next · TanStack Query
- Coach: Ollama (default model:
gemma3:1b; recommend something larger likeqwen2.5:7bfor nicer voice) - TTS: browser Web Speech API (uses installed OS voices)
- Persistence: single SQLite file in
./data/
- "Stockfish binary not found" — Patzer no longer falls back to a bare
stockfishPATH lookup (defense-in-depth: a malicious binary earlier in$PATHwould otherwise run as the server user). Either install Stockfish into/usr/games/stockfish,/usr/local/bin/stockfish,/opt/homebrew/bin/stockfish, orbin/stockfishin the project, or setSTOCKFISH_PATH(env) / Admin → System → Stockfish path. - "Ollama unreachable" during setup — Patzer's setup-time test endpoint only allows loopback / RFC1918 /
*.localURLs. Usehttp://host.docker.internal:11434from inside Docker on Mac/Windows, or your LAN IP on Linux. After setup, change it any time in Admin → System. - Port 8800 already in use —
-p 9000:8800(docker run) orHOST_PORT=9000 docker compose up -d. - Lost your admin password — there is no in-app reset yet. Until one ships, edit
chess.dbdirectly: opendata/chess.dbwithsqlite3and replace the row'spassword_hashwith abcryptjshash (cost ≥ 12). - Cookies dropped behind a reverse proxy — see
COOKIE_SECURE=trueabove. The cookie also requires the same hostname for both the page and the API. - PvP refresh ate my clock — fixed in 3.1.0 (clocks + last-move timestamp now persist on every move). Earlier versions reset the time control on rehydration.
- FAQ — what Patzer is and isn't, Chess.com API legality, NAT/proxy notes, backup, "I lost my admin password", language additions.
- Roadmap — what's queued and what's deliberately out of scope.
- Changelog — every release, with why-not-just-what entries.
PRs welcome — please read CONTRIBUTING.md first. Translations especially encouraged.
See SECURITY.md. For vulnerabilities, don't open a public issue — use GitHub's private vulnerability reporting.
MIT — see LICENSE. Note the GPL-3.0 components (chessground, Stockfish) — see THIRD_PARTY_NOTICES.md.