v0.13.0 web and terminal client enhance, replay api, linux server deploy, doc and dependency update
test / test (push) Canceled after 0s

This commit is contained in:
lixen committed 2026-09-28 22:35:52 -04:00
1 parent 52ac0fe31a
commit f1ac4cbbe7
71 files changed
+7677 -537

No files matched your search

+157 -13
View File
@@ -157,6 +157,11 @@ Authorization: Bearer <token>
Note: When authenticated, human player IDs match the user's ID. Anonymous players receive unique UUIDs.
A custom `fen` must describe a position that can arise in a game: one king
per side, no pawn on the first or last rank, and the side not to move not in
check; otherwise the response is 400 `INVALID_FEN` with the reason. Castling
rights whose king or rook has left its original square are dropped.
### Get Game
`GET /games/{gameId}`
@@ -190,7 +195,8 @@ the game ID, matching the existing public live-game read model. Persistent
storage must be enabled.
The response contains the initial FEN and an ordered FEN after every move, so a
client can replay the game without running a chess engine.
client can replay the game without running a chess engine. Each move also
carries its SAN, derived on read from the stored position before it.
**Response (200):**
```json
@@ -198,6 +204,8 @@ client can replay the game without running a chess engine.
"gameId": "a1b2c3d4-e5f6-7890-1234-567890abcdef",
"initialFen": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
"result": "white_wins",
"pgnResult": "1-0",
"termination": "checkmate",
"startTimeUtc": "2026-09-07T12:00:00Z",
"endTimeUtc": "2026-09-07T12:15:00Z",
"players": {
@@ -208,6 +216,7 @@ client can replay the game without running a chess engine.
{
"moveNumber": 1,
"moveUci": "e2e4",
"san": "e4",
"fenAfterMove": "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1",
"playerColor": "w",
"moveTimeUtc": "2026-09-07T12:00:05Z"
@@ -217,28 +226,87 @@ client can replay the game without running a chess engine.
```
`result` is omitted while a game is ongoing. Persisted terminal values are
`white_wins`, `black_wins`, `draw`, and `stalemate`. A claimed player's `name`
is their username when the claim was recorded; it survives later renames and
`white_wins`, `black_wins`, `draw`, and `stalemate`. `pgnResult` is the PGN
token for the same outcome (`1-0`, `0-1`, `1/2-1/2`, or `*` while ongoing),
and `termination` names how it was reached (see
[Game End](#game-end-draws-and-resignation); omitted while ongoing).
`moveNumber` counts plies from 1. `san` is omitted only for a stored move the
rules core cannot notate, which the server logs. A claimed player's `name` is
their username when the claim was recorded; it survives later renames and
account deletion, and is omitted for anonymous and computer players.
Undo stays available after a result (a finished game can be rewound and
played on), so a stored game is never final. Responses carry a strong `ETag`
and `Cache-Control: private, no-cache`: send `If-None-Match` to get 304 while
nothing changed.
Returns 400 for a non-canonical game ID, 404 when the game has no durable
record, and 503 when persistence is disabled or degraded.
### Export PGN
`GET /games/{gameId}/pgn?ply=N`
Returns the stored game in PGN export format as
`application/x-chess-pgn; charset=utf-8` with
`Content-Disposition: attachment; filename="chess-<yyyymmdd>-<id8>.pgn"`.
Same visibility, `ETag`, and error responses as the history. `ply` (optional,
0 up to the number of stored plies) exports only the first N plies; the
result is then `*`, the `Termination` tag is left out, and the filename ends
`-ply<N>.pgn`. A `fetch` of the URL returns the text, so clients can copy it
as well as download it.
```
[Event "Casual game"]
[Site "?"]
[Date "2026.09.27"]
[Round "-"]
[White "alice"]
[Black "Stockfish level 3"]
[Result "0-1"]
[GameId "68007fd1-8970-4d84-950d-2f6ec6375c0b"]
[UTCDate "2026.09.27"]
[UTCTime "08:07:00"]
[WhiteType "human"]
[BlackType "program"]
[PlyCount "4"]
[Termination "normal"]
1. f3 e5 2. g4 Qh4# 0-1
```
The movetext of a finished game ends with a comment naming the outcome
before the result, e.g. `{ Black wins by resignation. } 0-1` or
`{ Draw by threefold repetition. } 1/2-1/2`.
Players are the name snapshot, `Anonymous`, or `Stockfish level N`. A game
from a custom position adds `SetUp "1"` and `FEN` tags, and its movetext
starts at that position's move number (`55. g8=Q` or `40... Kd8`).
Movetext wraps at 80 columns. `Termination` is `normal` for a finished game
and `unterminated` otherwise.
### List My Stored Games
`GET /users/me/games?limit=50&offset=0`
`GET /users/me/games?limit=50&cursor=<nextCursor>&status=finished&color=white`
Returns games associated with the authenticated user at creation time or by a
later first-move slot claim. Requires `Authorization: Bearer <token>` and
persistent storage.
later first-move slot claim, newest first. Requires
`Authorization: Bearer <token>` and persistent storage.
- `limit`: 1-100; defaults to 50
- `offset`: 0-1,000,000; defaults to 0
- `cursor`: the previous page's `nextCursor`; continues after that game, so
games created meanwhile do not shift the page. Opaque; do not construct it.
- `status`: `ongoing` or `finished`
- `color`: `white` or `black`, the side the user claimed
- `offset`: 0-1,000,000; position-based paging for older clients. It cannot
be combined with `cursor`, and pages shift when games are added.
Each item contains game ID, initial FEN, final FEN (after the last stored move,
or the initial FEN when there is none), result/timestamps, players, and move
count. `nextOffset` is present only when another page exists. A game belongs to
a user who created it while authenticated or claimed a slot with a first move;
the association survives later player reconfiguration.
or the initial FEN when there is none), result and its `pgnResult` token,
timestamps, players, and move count. `nextCursor` is present only when another
page exists; `nextOffset` also, on requests without a cursor. A game belongs
to a user who created it while authenticated or claimed a slot with a first
move; the association survives later player reconfiguration.
Returns 400 `INVALID_REQUEST` for a malformed cursor or filter.
**Response (200):**
```json
@@ -249,6 +317,7 @@ the association survives later player reconfiguration.
"initialFen": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
"finalFen": "r1bqkb1r/pppp1Qpp/2n2n2/4p3/2B1P3/8/PPPP1PPP/RNB1K1NR b KQkq - 0 4",
"result": "white_wins",
"pgnResult": "1-0",
"startTimeUtc": "2026-09-07T12:00:00Z",
"endTimeUtc": "2026-09-07T12:15:00Z",
"moveCount": 7,
@@ -259,7 +328,8 @@ the association survives later player reconfiguration.
}
],
"limit": 50,
"offset": 0
"offset": 0,
"nextCursor": "MTc5MDQ5NjQyMDU4MTg2NS42ODAwN2ZkMS04OTcwLTRkODQtOTUwZC0yZjZlYzYzNzVjMGI"
}
```
@@ -273,15 +343,89 @@ Submits human move or triggers computer move.
{"move": "e2e4"}
```
Moves are UCI: castling is the king's two-square move (`e1g1`), and a
promotion appends the piece (`e7e8q`, `r`, `b`, or `n`). A pawn move to the
last rank without the piece returns 400 `INVALID_MOVE` with
`promotion piece required`.
**Computer move trigger:**
```json
{"move": "cccc"}
```
A move that ends the game (mate, stalemate, or an automatic draw) returns the
terminal state and `termination` in the same response.
### Game End: Draws and Resignation
A live game (`GET /games/{gameId}` and every game response) carries:
| Field | Meaning |
|---|---|
| `state` | `ongoing`, `pending` (computer thinking), `stuck` (engine failed), `white wins`, `black wins`, `stalemate`, or `draw` |
| `termination` | How a finished game ended: `checkmate`, `resignation`, `stalemate`, `insufficient_material`, `threefold_repetition`, `fifty_move_rule`, or `agreement`; omitted while unfinished |
| `drawOffer` | `w` or `b`: that side's draw offer awaits an answer; omitted when none |
| `drawOutcome` | Only on responses to `POST .../draw`: `offered`, `accepted`, or `declined` |
**Automatic draws.** After every move the game is drawn, without a claim, as
on most online servers, when:
- neither side can mate: bare kings, one minor piece, or only bishops all on
squares of one color (two knights do not count: mate is possible);
- the position occurs for the third time with the same side to move,
castling rights, and en-passant possibility (threefold repetition);
- 50 moves by each side pass without a capture or pawn move (the halfmove
clock reaches 100).
Mate and stalemate take precedence. A custom starting position that is
already dead, or whose halfmove clock is at 100, is created drawn.
#### Resign
`POST /games/{gameId}/resign`
```json
{"color": "w"}
```
Ends the game in the other side's favor, with `termination: "resignation"`.
`color` (`w`, `b`, `white`, `black`) may be omitted when the server can infer
the side: the one side the authenticated caller claimed, else the only human
side. Allowed while the game is unfinished, including while the computer is
thinking (its move is discarded) or the engine is stuck.
#### Offer, accept, or decline a draw
`POST /games/{gameId}/draw`
```json
{"action": "offer", "color": "b"}
```
`action` is `offer`, `accept`, or `decline`; `color` works as for resign.
Allowed only while `state` is `ongoing`.
- **To a human:** the offer stands (`drawOffer`) until the opponent accepts,
declines, or makes a move instead, which declines it; the offerer's own
move keeps it. Offering while the opponent's offer stands accepts it.
Repeating a standing offer is a no-op. Waiting long-poll clients are woken
on offers and answers.
- **To the computer:** answered at once in `drawOutcome`. The computer
declines before both sides have made ten moves; after that it accepts when
a short full-strength search rates its own side level or worse.
- **One offer per move:** after an offer is declined, the same side must make
a move before offering again (409 `GAME_CONFLICT`).
Resign and draw share the move endpoint's authorization: a side claimed by a
user acts only for that user (403 `UNAUTHORIZED`), and an authenticated
resignation or acceptance claims an unclaimed side as a first move would.
Other errors are 400: `GAME_OVER` for a finished game, `INVALID_REQUEST` for
an ambiguous or computer side, a missing offer, or a game that is not
ongoing. Both return the game.
### Undo Moves
`POST /games/{gameId}/undo`
Reverts moves from history.
Reverts moves from history. Undo stays available after a result, including a
resignation or an agreed draw: it rewinds the result and any draw offer.
### Configure Players
`PUT /games/{gameId}/players`