Submit
POST /leaderboard/weekly-distance
{ "score": 4213 }The body carries a score and nothing else. Publishing to a leaderboard walks it end to end. The player comes from the authenticated session, the timestamp from the server’s clock, the rank from the data.
Submitting requires the submit scope when the board is server-authoritative, which is the default. Mint it for your trusted server’s token and never for a player’s.
Set serverAuthoritative: false to let players post directly. That is the vendor default, which is why it is not ours.
Read
| Route | |
|---|---|
GET /leaderboard | Every board |
GET /leaderboard/:board/top | The standings |
GET /leaderboard/:board/me | Your own rank — always live, even when the board is materialized |
GET /leaderboard/:board/around | Your neighbors |
Add ?window=<key> to any read to read a closed window.
Friends and segments cost no extra board
A friends view is a collection dimension over the same store:
POST /leaderboard/weekly-distance/segment
{ "userIds": ["u1", "u2", "u3"] }Capped at 80 players per query — the reference has every limit, and what it costs has why the read path is the one to watch. D1 allows 100 bound parameters, and the my-rank path spends 10 of them on filters and the tiebreak predicate — so 80 leaves safe margin.
Tiers are classified on read
tiers: [{ key: "bronze", from: 0 }, { key: "gold", from: 1000 }]Computed from the score already stored, so they cost nothing on write. Adding a tier is a config change with no backfill.
Visibility is consent-gated, and moderation is separate
| Route | Who |
|---|---|
PUT /leaderboard/:board/me/visibility | The player’s own consent |
PUT /leaderboard/:board/entries/:userId/hidden | An admin scope |
DELETE /leaderboard/:board/entries/:userId | An admin scope |
They are separate flags on purpose: a player cannot undo a moderation action by toggling their own consent.
There is no public surface
An entry with no authenticated player has nothing to key on, no way to upsert on improve, and no way to rate-limit.
So an unauthenticated board is not a degraded board — it is an append log of unattributable scores.
Leaderboard reads core’s auth seam, never the auth package’s internals. Without auth composed, every route denies.
Aggregation
best | The default. A non-improving submission writes nothing |
latest | The most recent score wins |
sum | Accumulate |
best is the cost lever, not just a semantic. See what it costs.
Bounds are the anti-cheat baseline
{ min: 0, max: 100_000 }Server-side, per board. A score outside them is leaderboard/score_rejected.
That plus server-authoritative writes plus the admin hide/remove API is the entire anti-cheat surface the platform vendors actually demonstrate.
Do not use it for XP
A leaderboard board is right for a score. It is also fine for XP if you want ranking on it.
What is not right is the ledger — XP only goes up, you never spend a level, and none of the hold, overdraft or transfer machinery applies.