You need: multiplayer and leaderboard composed.
One block on the game
Boards and windows is what that block declares, what it costs is the field to read twice, rating players is the neighbouring question, and sessions is where a score comes from.
multiplayer({
games: [
{
key: "battle",
kind: "battle",
rules: { … },
leaderboard: { board: "wins", points: { win: 3, draw: 1, loss: 0 } },
},
],
})That is the whole integration. When a session resolves, the result is published to that board.
Declare the board
leaderboard({
boards: [
{ key: "wins", direction: "desc", aggregation: "sum", window: "0 0 * * 1" },
],
})sum is usually what you want here — points accumulate across games rather than a single best result standing.
window makes it weekly. Omit it for all-time. See boards and windows.
The publish is one-way
The session writes; it never reads a board back.
Which means a board is a projection of results, not a thing your game logic depends on. Remove the leaderboard capability and the game still plays — it simply publishes nothing.
You do not mint a submit scope for this
A leaderboard board is server-authoritative by default, and a player’s client cannot post to it.
The session is the trusted server here. The publish happens inside your Worker, from a result the Durable Object computed — so there is no scope to hand out and no client in the path.
That is worth stating because it is the usual reason a board submission is refused: a scope minted for a player’s token is the mistake, and here you do not need one at all.
What lands on the board
The result the session already computed — scores, a winner, a draw — mapped through your points block.
The durable result also lands in your own D1 as a row, joinable beside your own tables, whatever the board does.
Two boards from one game
Nothing stops a second board on a different window:
boards: [
{ key: "wins-weekly", direction: "desc", aggregation: "sum", window: "0 0 * * 1" },
{ key: "wins-alltime", direction: "desc", aggregation: "sum" },
]But one game publishes to one board. For two, publish to the weekly one and derive the all-time view from your own D1 — the score ledger is plain SQL.
Costs, briefly
A publish is a submission, and submission writes dominate a board’s bill at scale.
On a sum board every publish writes — the non-improving skip that makes a best board cheap does not apply, because every result changes the total.
Read what it costs before you assume a board is free. It is, to about ten thousand players.