You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Storage: Keyv on top of MongoDB. Each "table" is a MongoDB collection of
JSON documents keyed by string id. There is no relational schema, no
foreign-key enforcement, no transactions across collections.
Source of truth: TypeScript interfaces in server/src/models.ts. This
document is the human-readable mirror.
Migration tool:npm run -w server migrate. See
server/src/migrations/README.md for the workflow when you need to add a new
field.
A user-uploaded AI model. The Python source lives in object storage at
sourceRef; the trained .pth lands at artifactRef after the first
successful training job.
Field
Type
Required
Story
Notes
userId
RecordId
yes
2.4
References UserRecord (owner)
gameKey
GameKey
yes
2.2
Which game this model targets
displayName
string
yes
Shown on leaderboards / model cards
sourceRef
string
yes
2.1
Object-storage key for the uploaded .py
artifactRef
string
no
2.4
Store-relative artifact name <modelId>.pth (see artifactStore.service.ts; migration 001 normalized legacy absolute paths)
artifactMeta
ArtifactMeta
no
2.4
{ bytes, sha256, uploadedAt } recorded when the artifact is stored
forkedFrom
RecordId
no
2.13
Parent model id if this is a fork
visibility
"private" | "public"
yes
2.13
Default "private"
createdAt
DateISO
yes
updatedAt
DateISO
yes
TrainingJobRecord (trainingJob)
A queued, running, or completed training run for a model.
{ episodes, meanReward, winRate, updatedAt } updated by worker
checkpoints
TrainingCheckpoint[]
yes
2.10
{ episode, artifactRef, createdAt }
error
string
no
Set when status is "failed"
createdAt
DateISO
yes
completedAt
DateISO
no
Set when status is terminal
DeploymentRecord (deployment)
A runtime slot holding a trained model that can play matches.
Field
Type
Required
Story
Notes
modelId
RecordId
yes
2.5
References ModelRecord
userId
RecordId
yes
References UserRecord (cap of 3 active per game per user enforced in service code, Story 2.7)
gameKey
GameKey
yes
displayName
string
yes
2.5
Snapshot of ModelRecord.displayName at deploy time
status
"active" | "paused" | "retired"
yes
2.9
createdAt
DateISO
yes
updatedAt
DateISO
yes
RatingRecord (rating)
Glicko 2 rating for one entity in one game. Composite key:${entityType}:${entityId}:${gameKey} so any lookup is O(1) without scanning.
Use the ratingKey() helper in models.ts.
Field
Type
Required
Story
Notes
entityId
RecordId
yes
1.1
userId or modelId
entityType
"human" | "ai"
yes
1.1
gameKey
GameKey
yes
1.1
rating
number
yes
1.1
default 1500
rd
number
yes
1.2
rating deviation (default 350)
vol
number
yes
1.2
volatility (default 0.06)
gamesPlayed
number
yes
lastUpdatedAt
DateISO
yes
MatchRecord (match)
A completed, replayable game. Created when a GameRecord finishes. The original
GameRecord's matchId is set to the new MatchRecord's id.
Present iff visibility is "shared"; used in study URLs
createdAt
DateISO
yes
editedAt
DateISO
no
PuzzleRecord (puzzle)
A daily puzzle for one game. Composite key:${gameKey}:${YYYY-MM-DD} so
daily lookup is O(1). Use the dailyPuzzleKey() helper in models.ts.
Field
Type
Required
Story
Notes
gameKey
GameKey
yes
1.6
date
DateISO
yes
1.6
The day this is featured
position
unknown
yes
1.6
Per-game state to solve from
solution
PuzzleSolution
yes
1.7
{ moves[], explanation? }
sourceMatchId
RecordId
no
1.6
Match the position was mined from
createdAt
DateISO
yes
PuzzleAttemptRecord (puzzleAttempt)
Field
Type
Required
Story
Notes
puzzleId
RecordId
yes
1.7
References PuzzleRecord
attemptedBy
{ id: RecordId; type: "human" | "ai" }
yes
1.10
success
boolean
yes
1.7
timeMs
number
yes
1.7
Used for fastest-solvers list
hintsUsed
number
yes
1.12
Each hint applies a small puzzle Elo penalty
eloDelta
number
yes
1.2
Change applied to puzzle rating
createdAt
DateISO
yes
BroadcastRecord (broadcast)
A live broadcast of an in-progress game.
Field
Type
Required
Story
Notes
gameId
RecordId
yes
3.7
References GameRecord
broadcasterId
RecordId
yes
3.7
References UserRecord
delaySec
number
yes
3.7
0 to 60 seconds
status
"live" | "ended"
yes
chatChannel
RecordId
yes
3.8
References ChatRecord
startedAt
DateISO
yes
endedAt
DateISO
no
ChannelBlockRecord (channelBlock)
Per-channel block list entry. Composite key:${channelId}:${blockerId}:${blockedUserId} for O(1) lookups and easy
channel-wide scans. Use the channelBlockKey() helper in models.ts.
Field
Type
Required
Story
Notes
channelId
RecordId
yes
3.8
References BroadcastRecord or ChatRecord
blockerId
RecordId
yes
3.8
References UserRecord
blockedUserId
RecordId
yes
3.8
References UserRecord
createdAt
DateISO
yes
TrainingTokenRecord (trainingToken)
Expiring API token for the local-training channel (the trainer exchanges its
password once at POST /api/training/token and authenticates session calls
with the token). Keyed by the token string for O(1) lookup; expired records
are ignored at auth time.
Field
Type
Required
Notes
userId
RecordId
yes
References UserRecord
username
string
yes
Snapshot for quick auth
createdAt
DateISO
yes
expiresAt
DateISO
yes
24h TTL; checked at auth
MigrationLogRecord (migrationLog)
Append-only log of applied migrations. Maintained by
server/src/migrations/MigrationRunner.ts.
Field
Type
Required
Notes
id
string
yes
The migration id, e.g. 000_sprint1_arena_baseline
description
string
yes
Copied from the migration definition
appliedAt
DateISO
yes
durationMs
number
yes
ERD (Mermaid)
erDiagram
Auth ||--|| User: "Auth.userId"
User {
userId userId "key"
username username "unique"
string display ""
Date createdAt ""
Glicko puzzleRating "Sprint 1"
object puzzleStreak "Sprint 1"
userId[] following "Sprint 1"
object emailPrefs "Sprint 1 optional"
}
Rating {
compositeKey id "entityType:entityId:gameKey"
recordId entityId ""
string entityType "human or ai"
GameKey gameKey ""
number rating ""
number rd ""
number vol ""
number gamesPlayed ""
}
Model {
modelId id "key"
userId userId "owner"
GameKey gameKey ""
string displayName ""
string sourceRef ""
string artifactRef "optional"
modelId forkedFrom "optional"
string visibility ""
}
Model ||--|| User: "Model.userId"
Model ||--o| Model: "Model.forkedFrom"
TrainingJob {
jobId id "key"
modelId modelId ""
userId userId ""
GameKey gameKey ""
object config ""
string status ""
object progress "live"
array checkpoints ""
}
TrainingJob ||--|| Model: "TrainingJob.modelId"
Deployment {
deploymentId id "key"
modelId modelId ""
userId userId ""
GameKey gameKey ""
string displayName ""
string status ""
}
Deployment ||--|| Model: "Deployment.modelId"
Game {
gameId id "key"
GameKey type ""
unknown state ""
boolean done ""
boolean rated "Sprint 1"
userId[] players ""
AIParticipant[] aiPlayers "Sprint 1"
chatId chat ""
matchId matchId "set on completion"
}
Game ||--o| Match: "Game.matchId"
Match {
matchId id "key"
gameId gameId ""
GameKey gameKey ""
boolean rated ""
array participants ""
array moves ""
object result ""
}
Match ||--o{ Annotation: "Annotation.matchId"
Annotation {
annotationId id "key"
matchId matchId ""
number moveIndex ""
string text ""
string marker "optional"
userId authorId ""
string visibility ""
}
Puzzle {
compositeKey id "gameKey:date"
GameKey gameKey ""
Date date ""
unknown position ""
object solution ""
matchId sourceMatchId "optional"
}
PuzzleAttempt {
attemptId id "key"
puzzleId puzzleId ""
object attemptedBy "id+type"
boolean success ""
number timeMs ""
number hintsUsed ""
number eloDelta ""
}
PuzzleAttempt ||--|| Puzzle: "PuzzleAttempt.puzzleId"
Broadcast {
broadcastId id "key"
gameId gameId ""
userId broadcasterId ""
number delaySec ""
string status ""
chatId chatChannel ""
}
Broadcast ||--|| Game: "Broadcast.gameId"
ChannelBlock {
compositeKey id "channelId:blockerId:blockedUserId"
recordId channelId ""
userId blockerId ""
userId blockedUserId ""
}
Loading
Story coverage matrix
Quick reference for "which fields cover which COS":
# show what's applied and what's pending
npm run -w server migrate:status
# apply every pending migration
npm run -w server migrate
# create a new migration (scaffolds a new file)
npm run -w server migrate:create add_user_avatar_url
See server/src/migrations/README.md for the full authoring workflow.