Chess

Play Stockfish in your browser, over a REST API

GoFiberSQLite3RESTful APIStockfishJWTArgon2idWASMxterm.js

About

A Go server that exposes chess as a REST API, with Stockfish behind it for move validation and computer play. The server owns the rules; clients own nothing but the display, which is why three very different ones talk to the same endpoints.

Games are anonymous by default. An account is optional and only changes who the game is recorded against.

Modes
Human vs computer, human vs human, computer vs computer.
Engine
Stockfish, skill level 0–20, per-move search budget 100–10000 ms.
Positions
Any legal FEN — puzzles, endgames, mid-game starts.
Export
PGN move list plus the current FEN, straight to the clipboard.
Clients
Web UI, terminal CLI, and the same CLI compiled to WebAssembly.

Play

Drag-free click-to-move, last-move highlighting, undo, live server and storage indicators, and a move list that copies out as PGN.

Note: This interactive board requires JavaScript to function.

Terminal Client

The CLI from the repository, compiled to WebAssembly and wired to xterm.js. It opens in its own tab rather than in a frame here: the binary is around 10 MB, and there is no reason to spend that on a visitor who only came to look at the board.

Note: The terminal client requires a modern browser with JavaScript and WebAssembly support.

Start
help for the command list, then new white 10 1000.
Shares state
Same API as the board above — a game started in one shows up in the other.
Native build
The same client runs as a normal terminal program against any instance; the WASM build only swaps the I/O layer.

Under the Hood

Transport

Fiber over fasthttp. Routing, per-route rate limiting, content-type validation and JWT middleware live here and nowhere else; the handler’s only job is to translate an HTTP request into a Command object and a Command result back into JSON.

Processing

One Execute(Command) entry point holds the game logic, which is what keeps the transport swappable. Validation runs against a synchronous UCI engine; computer moves go to an asynchronous queue so the request returns immediately.

State and Long-Polling

A single RWMutex guards the game map: concurrent reads, serialised writes. A wait registry tracks each client’s move count and blocks its request for up to 30 seconds, so a client learns about a move on the move rather than on a timer.

Engine Pool

Two workers, each with its own Stockfish process, fed from a queue. A separate mutex-protected instance handles synchronous legality checks, so a long search never blocks a move validation.

Persistence

SQLite. Game writes queue onto a buffered channel and are drained by one goroutine, so a slow disk cannot stall a move; user writes go straight through in a transaction, because a registration must not report success before it is durable. Write failure degrades to memory-only rather than failing the request.

Accounts

Argon2id password hashing, HS256 tokens with a seven-day expiry, constant-time verification, and case-insensitive username and email matching so the same account cannot be registered twice under different casing. Registration and login carry their own rate limits.

API

EndpointMethodNotes
/api/v1/auth/registerPOSTUsername, password, optional email. Returns a token. 5 req/min per IP.
/api/v1/auth/loginPOSTUsername or email plus password. 10 req/min per IP.
/api/v1/auth/meGETCurrent user, from the bearer token.
/api/v1/auth/logoutPOSTInvalidates the session server-side.
/api/v1/gamesPOSTPer-colour player config, optional starting FEN.
/api/v1/games/{id}/movesPOSTA UCI move, or cccc to hand the turn to the engine.
/api/v1/games/{id}GET?wait=true&moveCount=N long-polls for up to 30 seconds.
/api/v1/games/{id}/undoPOSTTakes back count plies.
/healthGETServer and storage status.

General rate limit is 10 req/s per IP. Authentication is optional on game endpoints; an anonymous game is issued a generated player ID instead.

Technical Details

Runtime

  • Server: Go, Fiber, SQLite (WAL), Stockfish over UCI
  • Web client: vanilla JavaScript and CSS, no framework, no build step
  • Terminal client: Go → WASM, xterm.js with the WebGL renderer
  • Auth: JWT (HS256), Argon2id password hashing
  • Single instance enforced by a PID lock file

Build

Clone
git clone https://github.com/lixenwraith/chess --depth 1
Build
cd chess && make release
Run
bin/chessd (Stockfish must be on PATH)