Michael Freno 7108f77bf9 pvp: make the blood token ledger server-authoritative
The PvP_Characters table tallied wins and losses but held no token
balance and no purchase endpoint: earnings were client-reported
(winners credited themselves locally), so verifying only a spend
would have been security theater — a cheater could mint tokens by
self-reporting battle wins.

The server is now the ledger authority (FRE-142): registerCharacter
seeds the balance from the client cache exactly once and reconciles
it on every sync; battleResult requires the verified Lineage JWT
bearer, rejects non-participants, and credits the winner via the
reward formula ported from the client (computed from both registered
rows); a new purchase mutation checks cost, balance, and cooldown in
one conditional UPDATE scoped to the caller-owned row, so the spend
is atomic under concurrency and rate limiting survives cold starts.
Schema changes ship as an idempotent pvp-schema bootstrap
(pvpTokens, ownerUserId, lastPurchaseAt), mirroring the
analytics-schema pattern.

Client-side counterpart shipped in the lineage repo; design record
in its docs/security/pvp-server-side-purchases.md.

Tests: pvp-schema (DDL + legacy-column migration + idempotency),
pvp-rewards (formula parity with the client port, cost table),
pvp-ledger (seed-once reconciliation, owner binding, participant
verification, atomic spend, cooldown, refusal reasons) — 31 new tests
against in-memory SQLite; suite 572 pass (1 pre-existing audit
date-filter failure, unrelated).
2026-10-06 10:56:29 -04:00
2026-07-23 22:53:40 -04:00
2026-10-03 15:01:23 -04:00
2026-09-05 16:06:27 -04:00
2026-10-05 11:05:24 -04:00
2026-09-06 13:53:49 -04:00
2026-10-03 11:04:37 -04:00
2025-12-17 13:51:13 -05:00
2025-12-16 22:42:05 -05:00
Description
No description provided
153 MiB
Languages
TypeScript 64.5%
JavaScript 33.6%
CSS 1.2%
HTML 0.4%
Shell 0.3%