v0.12.0 migrate sqlite3 to psql18, updated auth, deploy docs, account and replay updates
test / test (push) Canceled after 0s

This commit is contained in:
lixen committed 2026-09-26 20:58:19 -04:00
1 parent 5d2be4abb2
commit d0326b059e
66 files changed
+4016 -2433

No files matched your search

+34 -14
View File
@@ -26,6 +26,10 @@ Creates new user account and returns JWT token.
- `email` (string, optional): Valid email address
- `password` (string, required): Minimum 8 characters, must contain letter and number
Accounts created here are identical to CLI-created accounts and do not
expire. Returns 409 when the username or email is taken, and 503 when the
registration limit (`-max-users`) or password-hashing capacity is exhausted.
**Response (201):**
```json
{
@@ -101,7 +105,7 @@ Returns server and storage status.
```
Storage states:
- `"disabled"` - No storage path configured
- `"disabled"` - No database configured (`-dsn` or `CHESS_DSN`)
- `"ok"` - Database operational with auth enabled
- `"degraded"` - A persistence write failed or the write queue filled; live games continue in memory, but durable history is no longer complete
@@ -177,7 +181,9 @@ Response includes all game data. Compare `moves` array length to detect changes.
`GET /games/{gameId}/history`
Returns the persisted replay line even after the live game has been unloaded
from memory or the server has restarted. History is public to anyone who knows
from memory or the server has restarted. Games without a registered player
are deleted 24 hours after their last activity; their history then returns
404. History is public to anyone who knows
the game ID, matching the existing public live-game read model. Persistent
storage must be enabled.
@@ -193,7 +199,7 @@ client can replay the game without running a chess engine.
"startTimeUtc": "2026-09-07T12:00:00Z",
"endTimeUtc": "2026-09-07T12:15:00Z",
"players": {
"white": {"id": "user-id", "color": 1, "type": 1, "claimedBy": "user-id"},
"white": {"id": "user-id", "color": 1, "type": 1, "claimedBy": "user-id", "name": "alice"},
"black": {"id": "player-id", "color": 2, "type": 1}
},
"moves": [
@@ -209,10 +215,12 @@ 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`.
`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
account deletion, and is omitted for anonymous and computer players.
Returns 404 when the game has no durable record and 503 when persistence is
disabled.
Returns 400 for a non-canonical game ID, 404 when the game has no durable
record, and 503 when persistence is disabled or degraded.
### List My Stored Games
`GET /users/me/games?limit=50&offset=0`
@@ -224,8 +232,11 @@ persistent storage.
- `limit`: 1-100; defaults to 50
- `offset`: 0-1,000,000; defaults to 0
Each item contains game ID, initial FEN, result/timestamps, players, and move
count. `nextOffset` is present only when another page exists.
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.
**Response (200):**
```json
@@ -234,12 +245,13 @@ count. `nextOffset` is present only when another page exists.
{
"gameId": "a1b2c3d4-e5f6-7890-1234-567890abcdef",
"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",
"startTimeUtc": "2026-09-07T12:00:00Z",
"endTimeUtc": "2026-09-07T12:15:00Z",
"moveCount": 41,
"moveCount": 7,
"players": {
"white": {"id": "user-id", "color": 1, "type": 1, "claimedBy": "user-id"},
"white": {"id": "user-id", "color": 1, "type": 1, "claimedBy": "user-id", "name": "alice"},
"black": {"id": "player-id", "color": 2, "type": 1}
}
}
@@ -314,7 +326,12 @@ Error codes:
- Registration: 5 requests/minute/IP
- Login: 10 requests/minute/IP
Exceeding limit returns 429 status.
Exceeding limit returns 429 status. Behind a reverse proxy the client IP is the
proxy's `X-Real-IP` header, trusted only from addresses listed in
`-trusted-proxies`.
Password hashing is bounded to four concurrent operations. When all are busy
for five seconds, registration and login return 503 with `RESOURCE_LIMIT`.
## JWT Token Format
@@ -323,6 +340,9 @@ Tokens are HS256-signed JWTs valid for 7 days. Include in Authorization header:
Authorization: Bearer <token>
```
Token claims include `sub` (user ID), `username`, `email`, `session_id`, and
`exp` (expiration). Authentication requires the session to exist, be unexpired,
and belong to the JWT subject.
The scheme is case-insensitive and the token must use RFC 6750 syntax; a
malformed header returns 401 even on endpoints where authentication is
optional. Claims are `iss` (`chess-server`), `aud` (`chess-api`), `sub` (user
ID), `iat`, `nbf`, `exp`, and `extra.session_id`. Tokens carry no profile data;
use `/auth/me`. Authentication requires the session to exist, be unexpired, and
belong to the JWT subject. Logging in again replaces the previous session.