Skip to content

Repository files navigation

CheckAI

Chess Server for AI Agents

A Rust-powered chess server and CLI with REST, WebSocket, and deep analysis APIs — following FIDE 2023 rules.

CI License: MIT Rust GitHub All Releases

Documentation | Changelog | Releases


Features

Chess Engine

  • Full FIDE 2023 Rules — Move generation and validation with castling, en passant, promotion, check/checkmate/stalemate, and all draw conditions (50-move rule, threefold repetition, insufficient material)
  • Search — Iterative deepening with per-line aspiration windows; Principal Variation Search (PVS / Negascout); singular extensions; adaptive null-move pruning with a high-depth verification search; table-driven Late Move Reductions; Late Move Pruning; reverse-futility, futility and razoring at frontier nodes; history-based pruning; Internal Iterative Reduction; check extensions; mate-distance pruning; full Static Exchange Evaluation (SEE) for capture ordering and pruning; killer-move, counter-move, butterfly-history and one-ply continuation-history ordering; in-tree repetition detection and 50-move awareness; and a quiescence search with stand-pat, delta pruning, SEE pruning and TT cutoffs
  • Lazy SMP — Up to 64 search threads sharing one lock-free transposition table (--threads, UCI Threads). Single-threaded search stays fully deterministic
  • MultiPV — Up to 16 principal variations from every surface: CLI, UCI, REST and both UIs
  • Two-tier time management — A hard in-tree limit plus a soft limit that stretches while the best move is unstable and shortens once it settles
  • Strength limiting — A 0–20 skill scale (or a target UCI_Elo) that makes weakened play look human rather than merely short-sighted
  • PeSTO Evaluation — Tapered midgame/endgame piece-square tables interpolated by game phase, plus pawn structure (passed, doubled, isolated, backward, connected pawns), bishop-pair bonus, rook open/semi-open file bonuses, king safety (open-file and pawn-shield penalties), per-piece mobility, and a tempo bonus. The evaluation is always relative to the side to move
  • Async Game Analysis — A separate job-based analysis service performs deep (30+ ply) game review on top of the same engine
  • Live Position AnalysisPOST /api/analysis/position runs one bounded search and answers in the same request: evaluation, best move, MultiPV lines, book and tablebase information
  • Opening Book — Polyglot .bin format, consulted by the search itself, with weight-proportional move sampling
  • Endgame Tablebases — Syzygy tablebase detection with analytical evaluation for common endgames, probed at the search root
  • PGN with real SAN — Reading and writing complete PGN files, including disambiguated algebraic notation, FEN/SetUp start positions, comments, NAGs and variations

APIs & Interfaces

  • REST API — JSON-based endpoints for game management, moves, draw claims, resignation, FEN/PGN import/export (Agent Protocol)
  • Analysis API — Separate /api/analysis/* endpoints for asynchronous game review with job progress, completed summaries, and per-move annotations
  • WebSocket API — Full real-time API at /ws mirroring REST endpoints with push notifications and game subscriptions
  • Swagger/OpenAPI — Auto-generated interactive API docs at /swagger-ui/
  • Animated Terminal CLI — Play against the engine (10-level ladder) or a second human, watch engine-vs-engine games, analyze positions, move lists and PGN files, inspect the evaluation, benchmark the engine, run perft, and speak UCI for chess GUIs. Boards repaint in place with coloured squares and animated piece movement, next to a live search panel with a colour-graded evaluation bar. Every effect is TTY-gated and degrades to clean plain text when piped; honors --no-color / NO_COLOR
  • Chess clocks--time 5+3 gives play and watch a real two-sided clock that the engine paces itself from

Web & Deployment

  • Modern Web UI — TypeScript SPA with @bquery/bquery, Tailwind CSS v4, Vite — interactive SVG board, analysis panel, FEN/PGN tools, promotion dialog, WebSocket auto-reconnect. Compiled into the binary via rust-embed
  • Desktop UI — Electron app built with Svelte — dedicated desktop shell with persistent sessions, local backend launch controls, dashboard/game/analysis/archive views, inline log inspection, and desktop-focused settings
  • Docker Support — Multi-stage Dockerfile and docker-compose.yml with volume mounts for game data, opening books, and tablebases
  • Internationalization — 8 languages (EN, DE, FR, ES, ZH, JA, PT, RU) with auto-detection and per-request API selection
  • Self-Update — Automatic version checks and checkai update for in-place binary updates
  • JavaScript Package@josunlp/checkai on GitHub Packages — the full chess engine compiled to WebAssembly, usable as a Bun or Node.js CLI/library package

Quick Start

Install

Recommended: pin the release you want and verify the downloaded binary against the published SHA-256 checksums before installing it. The examples below use v1.0.1; check the Releases page and replace it with the current or desired release tag.

# Linux / macOS
CHECKAI_VERSION=v1.0.1
OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
[ "$OS" = "darwin" ] || OS="linux"
ARCH="$(uname -m)"
case "$ARCH" in x86_64|amd64) ARCH=x86_64 ;; arm64|aarch64) ARCH=aarch64 ;; esac
ASSET="checkai-${OS}-${ARCH}"
BASE_URL="https://github.com/JosunLP/checkai/releases/download/${CHECKAI_VERSION}"

curl -fSLO "${BASE_URL}/${ASSET}"
curl -fSLO "${BASE_URL}/checksums-sha256.txt"
CHECKSUM_LINE="$(grep "  ${ASSET}$" checksums-sha256.txt)" || {
  echo "Error: Asset ${ASSET} not found in checksums-sha256.txt" >&2
  exit 1
}
if command -v sha256sum >/dev/null 2>&1; then
  echo "${CHECKSUM_LINE}" | sha256sum -c -
elif command -v shasum >/dev/null 2>&1; then
  echo "${CHECKSUM_LINE}" | shasum -a 256 -c -
else
  echo "Error: Neither sha256sum nor shasum found. On Linux, install coreutils; on macOS, shasum should be pre-installed." >&2
  exit 1
fi
chmod +x "${ASSET}"
sudo install -m 0755 "${ASSET}" /usr/local/bin/checkai
# Windows (PowerShell)
$Version = "v1.0.1"
$Asset = "checkai-windows-x86_64.exe"
$BaseUrl = "https://github.com/JosunLP/checkai/releases/download/$Version"
Invoke-WebRequest "$BaseUrl/$Asset" -OutFile $Asset
Invoke-WebRequest "$BaseUrl/checksums-sha256.txt" -OutFile checksums-sha256.txt
$Expected = ((Select-String .\checksums-sha256.txt -Pattern "  $([regex]::Escape($Asset))$").Line -split "\s+")[0].ToLowerInvariant()
$Actual = (Get-FileHash ".\$Asset" -Algorithm SHA256).Hash.ToLowerInvariant()
if ($Actual -ne $Expected) { throw "Checksum verification failed for $Asset" }
New-Item -ItemType Directory "$env:LOCALAPPDATA\checkai" -Force | Out-Null
Move-Item -Force ".\$Asset" "$env:LOCALAPPDATA\checkai\checkai.exe"

For the shortest install path, you can pipe the installer script directly to your shell. This executes the current main branch script immediately, so only use it if you accept that trade-off:

curl -fsSL https://raw.githubusercontent.com/JosunLP/checkai/main/scripts/install.sh | sh
irm https://raw.githubusercontent.com/JosunLP/checkai/main/scripts/install.sh | iex

Uninstall

# Linux / macOS
curl -fsSL https://raw.githubusercontent.com/JosunLP/checkai/main/scripts/uninstall.sh | sh
# Windows (PowerShell)
irm https://raw.githubusercontent.com/JosunLP/checkai/main/scripts/uninstall.sh | iex

Tip: The installer script automatically detects the operating system, architecture, and latest release, while the uninstaller script detects the operating system — no manual changes required. The direct installer shortcut is quick, but it executes a remote script before you can verify the release asset yourself. Prefer the pinned release commands above when you want release integrity checks before installation. See the Getting Started guide for details.

Build from Source

git clone https://github.com/JosunLP/checkai.git
cd checkai

# Build web UI (requires Bun)
cd web && bun install && bun run build && cd ..

# Build the Rust binary
cargo build --release

Desktop App

The repository now also includes a dedicated Electron desktop shell in desktop/.

cd desktop
bun install --frozen-lockfile
bun run build
bun run start

By default the desktop app targets http://127.0.0.1:8080, can persist backend launch settings between sessions, and can start a local checkai serve process for you. The embedded live workspace is intentionally limited to loopback URLs for safety; non-local targets can still be opened in your browser. Packaged desktop releases can also check GitHub Releases for updates, download them, and prompt for restart-based installation from inside the app. Release automation now publishes updater-compatible artifacts (AppImage/zip/NSIS) together with native installer packages per platform (.deb, .dmg, .msi). To keep Windows desktop updates working, release builds continue to ship the updater-compatible NSIS package alongside the MSI installer.

Start the Server

checkai serve                    # Default: http://0.0.0.0:8080
checkai serve --port 3000        # Custom port
checkai serve \
  --book-path books/book.bin \
  --tablebase-path tablebase/ \
  --analysis-depth 30            # With opening book + tablebases

Open http://localhost:8080/ for the Web UI or /swagger-ui/ for interactive API docs.

Docker

docker compose up -d             # Build and start
docker compose logs -f           # Follow logs
docker compose down              # Stop

JavaScript Package (WebAssembly)

The chess engine is also available as a Bun/Node.js package via GitHub Packages:

# Configure GitHub Packages registry (Bun reads .npmrc)
echo "@josunlp:registry=https://npm.pkg.github.com" >> ~/.npmrc

# Install as CLI
bun add --global @josunlp/checkai
checkai fen
checkai search "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1" --depth 15

# Or use as library
bun add @josunlp/checkai
import { engine } from "@josunlp/checkai";
const moves = engine.legalMoves(engine.startingFen());
const result = engine.bestMove(engine.startingFen(), 10);

See the package README for the full API reference.

Terminal Mode

checkai play

Command-Line Interface

checkai is a single binary with the subcommands below. Two global options apply to every command: -l, --lang <LANG> (override the locale, e.g. de, fr, zh-CN) and --no-color (disable colored output; the NO_COLOR env var is honored too). Run checkai <command> --help for the full flag list.

Command Description Example
serve Start the REST + WebSocket API server with Swagger UI checkai serve --port 3000
play Play in the terminal — defaults to playing vs the built-in engine checkai play --level 9 --color black
watch Watch the engine play itself (engine-vs-engine showcase) checkai watch --level-white 9 --level-black 3
analyze Analyze a position, a move list or a PGN file checkai analyze --pgn game.pgn
eval Inspect the evaluation, ranked moves, book and tablebase checkai eval --fen "<FEN>" --top 20
bench Run the fixed engine benchmark suite (nodes, time, NPS) checkai bench --depth 12
perft Verify move generation with perft node counts checkai perft 5 --divide
uci Run as a UCI engine on stdin/stdout (for chess GUIs / match runners) checkai uci
export Export archived games as text, PGN, or JSON checkai export --all --format pgn
update Update CheckAI to the latest version from GitHub checkai update
version Print the current version checkai version

Every command that runs a search — play, watch, analyze, eval, bench — shares one engine option group:

Flag Description Default
--threads <N> Lazy SMP search threads; 0 = one per CPU core 1
--hash <MB> Transposition table size per command
--nodes <N> Node budget per search unlimited
--multipv <N> Report the best N lines (1–16) 1
--book <FILE> Polyglot opening book (.bin) none
--tablebase <D> Syzygy tablebase directory none

Highlights:

  • play plays vs the engine by default — pick a strength on the 1–10 level ladder (--level, default 5) and a side (--color white|black|random). Use --vs human for a local two-player game. Other flags: --time, --movetime, --depth, --fen, --pgn, --board, --ascii, --flip.
  • watch runs an engine-vs-engine showcase — set both sides with --level, or asymmetrically with --level-white / --level-black; play it on a clock with --time, stop hopeless games with --adjudicate, and save the result with --pgn-out.
  • Moves in either notation — coordinate (e2e4, e7e8q) or standard algebraic (e4, Nf3, exd5, O-O) anywhere a move is accepted.
  • Animated, TTY-aware UI — boards repaint in place, pieces slide across the board, and the search panel updates live. Piped output stays plain and parseable.
  • Color & locale--no-color / the NO_COLOR env var disable ANSI styling, and --lang selects one of 8 bundled languages (English is the source of truth and fallback).
checkai play                              # White vs the engine at level 5
checkai play --time 5+3 --board ice       # Five minutes each, blue board
checkai play --threads 4 --book book.bin  # Give the engine cores and a book
checkai watch --movetime 200 --delay 0    # Fast engine-vs-engine game
checkai analyze --pgn game.pgn            # Annotate a whole game
checkai eval --fen "<FEN>" --multipv 5    # What does the engine see here?
checkai bench --depth 8                   # Faster, shallower benchmark
checkai perft 6 --threads 0               # Exact node counts on every core

UCI Mode

checkai uci speaks the UCI protocol on stdin/stdout so you can drop CheckAI into any UCI-compatible GUI (Arena, Cute Chess, BanksiaGUI, …) or match runner. The UCI output is a machine protocol and is intentionally not localized.

$ checkai uci
uci
position startpos moves e2e4
go movetime 1000
quit

Want to know how strong it really is? Measure it yourself. With cutechess-cli you can run CheckAI against any other UCI engine — for example Stockfish as a convenient, widely-available opponent — and read the result off the scoreboard:

cutechess-cli \
  -engine name=CheckAI cmd=checkai arg=uci \
  -engine name=Opponent cmd=stockfish \
  -each proto=uci tc=10+0.1 -games 100 -repeat -recover \
  -pgnout match.pgn

Adjust the opponent, time control, and game count to taste; the resulting score and Elo estimate are the honest measure of CheckAI's playing strength.

API Reference

Game Endpoints

Method Path Description
POST /api/games Create a new game
GET /api/games List all games
GET /api/games/{id} Get full game state
DELETE /api/games/{id} Delete a game
POST /api/games/{id}/move Submit a move
POST /api/games/{id}/action Special action (resign, draw claim)
GET /api/games/{id}/moves List legal moves
GET /api/games/{id}/board ASCII board display
GET /api/games/{id}/fen Export FEN notation
POST /api/games/fen Import game from FEN
GET /api/games/{id}/pgn Export PGN notation

Analysis Endpoints

Method Path Description
POST /api/analysis/position Analyse one position, answered synchronously
POST /api/analysis/game/{id} Submit game for asynchronous analysis
GET /api/analysis/jobs List all analysis jobs
GET /api/analysis/jobs/{id} Get job status & results
DELETE /api/analysis/jobs/{id} Cancel or delete a job

WebSocket

Connect to ws://localhost:8080/ws for real-time bidirectional communication.

Action Fields
create_game
list_games
get_game game_id
delete_game game_id
submit_move game_id, from, to, promotion?
submit_action game_id, action_type, reason?
get_legal_moves game_id
subscribe / unsubscribe game_id
list_archived / get_archived game_id
replay_archived game_id, move_number?

Full API documentation with request/response schemas: REST | WebSocket | Analysis

Usage Examples

REST API

# Create a game
curl -X POST http://localhost:8080/api/games
# → { "game_id": "550e8400-...", "message": "New chess game created. White to move." }

# Submit a move (1. e4)
curl -X POST http://localhost:8080/api/games/{game_id}/move \
  -H "Content-Type: application/json" \
  -d '{"from": "e2", "to": "e4"}'

# Get legal moves
curl http://localhost:8080/api/games/{game_id}/moves

# Resign
curl -X POST http://localhost:8080/api/games/{game_id}/action \
  -H "Content-Type: application/json" \
  -d '{"action": "resign"}'

# Claim draw
curl -X POST http://localhost:8080/api/games/{game_id}/action \
  -H "Content-Type: application/json" \
  -d '{"action": "claim_draw", "reason": "threefold_repetition"}'

# Submit game for deep analysis
curl -X POST http://localhost:8080/api/analysis/game/{game_id} \
  -H "Content-Type: application/json" \
  -d '{"depth": 30}'
# → { "job_id": "a1b2c3d4-...", "message": "Analysis submitted ..." }

# Get analysis results
curl http://localhost:8080/api/analysis/jobs/{job_id}

WebSocket and Real-Time Events

const ws = new WebSocket("ws://localhost:8080/ws");

ws.onopen = () => {
  ws.send(JSON.stringify({ action: "create_game", request_id: "1" }));
};

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);

  if (msg.type === "response" && msg.action === "create_game") {
    const gameId = msg.data.game_id;
    ws.send(JSON.stringify({ action: "subscribe", game_id: gameId }));
    ws.send(JSON.stringify({
      action: "submit_move", game_id: gameId, from: "e2", to: "e4"
    }));
  }

  if (msg.type === "event") {
    console.log("Game event:", msg.event, msg.data);
  }
};

Terminal Commands

These commands are available during an interactive checkai play game (single-letter aliases are shown by help):

Command Description
e2e4 / Nf3 Play a move, in either notation
e7e8Q Pawn promotion (append the piece letter)
moves List all legal moves in SAN
board Redraw the board
flip Flip the board orientation
history Show move history
fen Show the current position as FEN
pgn Print the game as PGN
json Game state as JSON
hint Ask the engine for a suggested move
analyze Deeper multi-line analysis
eval Static evaluation breakdown
book Opening-book moves for this position
tb Endgame tablebase verdict
undo / redo Take back or replay a move
level N Change the engine level mid-game
save [file] Save the game as PGN
load <file> Load a game from PGN
new Start a fresh game
resign Resign the game
draw Claim a draw (if eligible)
help Show help
quit Quit

Updating

CheckAI checks for new versions on startup. Update manually:

checkai update

Project Structure

checkai/
├── build.rs              # Ensures web/dist/ exists for rust-embed
├── Cargo.toml            # Dependencies and project metadata
├── Dockerfile            # Multi-stage Docker build
├── docker-compose.yml    # Container orchestration
├── .github/workflows/
│   ├── ci.yml            # CI (fmt, clippy, test, build)
│   ├── release.yml       # Release (binaries + Docker image)
│   └── docs.yml          # Documentation → GitHub Pages
├── scripts/
│   ├── install.sh        # Installer (Linux / macOS / Windows)
│   └── uninstall.sh      # Uninstaller (Linux / macOS / Windows)
├── docs/                 # VitePress documentation site
├── locales/              # i18n YAML files (8 languages)
├── wasm/                 # WebAssembly crate (wasm-pack)
│   ├── Cargo.toml        # WASM crate manifest
│   └── src/
│       ├── lib.rs        # WASM bindings (game mgmt, export, board)
│       └── search.rs     # Search with web-time::Instant
├── npm/                  # JS package (@josunlp/checkai)
│   ├── package.json      # Scoped to GitHub Packages
│   ├── bin/checkai.mjs   # Node.js CLI entry point
│   ├── src/index.mjs     # Library API exports
│   └── README.md         # package documentation
├── desktop/              # Electron desktop UI (Svelte renderer + native shell)
│   ├── bun.lock          # Bun lockfile for desktop workspace
│   ├── package.json      # Desktop build + packaging scripts
│   ├── index.html        # Renderer entry point
│   └── src/
│       ├── shared-types.ts   # Shared IPC contract (main, preload, renderer)
│       ├── main.ts           # Svelte renderer bootstrap
│       ├── App.svelte        # Root desktop UI component
│       ├── electron-main.ts
│       ├── preload.ts
│       └── styles.scss       # Desktop-specific styles
├── web/                  # TypeScript Web UI (bQuery + Tailwind + Vite)
│   ├── src/              # 12 TypeScript source modules
│   ├── dist/             # Vite production build (embedded into binary)
│   └── index.vite.html   # Vite HTML entry point
└── src/
    ├── main.rs           # Entry point, CLI, server setup
    ├── types.rs          # Core types (pieces, board, JSON protocol)
    ├── movegen.rs        # Move generation and validation
    ├── game.rs           # Game state management
    ├── api.rs            # REST API handlers + OpenAPI
    ├── ws.rs             # WebSocket API + broadcaster
    ├── storage.rs        # Persistent storage (zstd compression)
    ├── export.rs         # Export (text, PGN, JSON)
    ├── eval.rs           # PeSTO evaluation + king safety + mobility
    ├── search.rs         # Alpha-beta (PVS, TT, LMR, NMP, SEE, futility)
    ├── analysis.rs       # Analysis orchestrator (async job queue)
    ├── analysis_api.rs   # Analysis REST endpoints
    ├── opening_book.rs   # Polyglot opening book reader
    ├── tablebase.rs      # Syzygy endgame tablebase interface
    ├── zobrist.rs        # Zobrist hashing
    ├── terminal.rs       # Terminal interface
    ├── i18n.rs           # Internationalization helpers
    └── update.rs         # Self-update + version check

Documentation

Full documentation at https://josunlp.github.io/checkai/

Section Description
Getting Started Installation and first steps
REST API Full REST endpoint reference
WebSocket API Real-time bidirectional API
Analysis API Deep game analysis endpoints
Agent Protocol JSON protocol for AI agents
Chess Rules FIDE 2023 rule reference
Architecture Module overview and design decisions
JavaScript Package WASM package API reference

The raw agent protocol specification is also available at docs/AGENT.md.

License

MIT

About

A Rust application that provides both a terminal interface and a REST API for playing chess. Designed for AI agents to play chess against each other, following the FIDE 2023 Laws of Chess.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages