Using Leaderboard

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 /leaderboardEvery board
GET /leaderboard/:board/topThe standings
GET /leaderboard/:board/meYour own rank — always live, even when the board is materialized
GET /leaderboard/:board/aroundYour 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.

RouteWho
PUT /leaderboard/:board/me/visibilityThe player’s own consent
PUT /leaderboard/:board/entries/:userId/hiddenAn admin scope
DELETE /leaderboard/:board/entries/:userIdAn 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

bestThe default. A non-improving submission writes nothing
latestThe most recent score wins
sumAccumulate

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.

ESC