Electric Cherry has no bank balance store, so the in-game bank commands can't keep points between matches #146

Closed
opened 2026-10-12 10:14:36 -04:00 by goddard · 1 comment
Collaborator

josh/tws-zombies-scripts#3 adds .b, .d and .w bank commands to the game scripts. Balances have to outlive a match, so Electric Cherry needs to store them by GUID and give the scripts an endpoint to read and change them. Nothing in Electric Cherry does this today.

  • access.ts:40-41 lets game servers in through two routes only: POST /api/telemetry (machine) and GET /api/leaderboard (public, read-only). Neither changes anything a player can spend.
  • auth/telemetry.ts:12-42 checks one shared secret, EC_TELEMETRY_TOKEN. The scripts carry it as a literal (tws_t5_telemetry.gsc:34 and tws_t6_telemetry.gsc:39 in josh/tws-zombies-scripts), so it's in that repo and in every deployed copy.
  • api/telemetry/+server.ts:60 takes the server name from the request body, and player GUIDs come from the body too, so the token holder can speak for any server and any player.
  • player-profile.ts:30 already shows one player per GUID across T5 and T6, while leaderboard_players stays keyed by game and GUID (docs/guides/leaderboard.md, "Player identity").

A bank endpoint is riskier than telemetry: whoever holds its credential can create points for any GUID, so reusing the shared telemetry token would let anyone with a copy of the scripts do that. Requests also fail and time out (the scripts send each report once, josh/tws-zombies-scripts#2), so a withdrawal Electric Cherry applied but the game never heard about would lose points, and a retried deposit would add them twice.

Fix

  • Store a balance per GUID, shared across T5 and T6 as player profiles are, and a ledger entry for every change (GUID, server, Electric Cherry match id when known, amount, resulting balance, time).
  • Add machine endpoints for the scripts to read a balance, deposit and withdraw, routed in access.ts like /api/telemetry.
  • Authenticate them with a credential per game server instead of EC_TELEMETRY_TOKEN, and refuse a request whose server name doesn't match its credential.
  • Refuse a deposit or withdrawal for a GUID that Electric Cherry hasn't recorded in that server's active match.
  • Each deposit and withdrawal carries a request id, and repeating an id returns the first result without applying it again.
  • Refuse a withdrawal larger than the balance, and a deposit that would take the balance over a configurable maximum, without changing anything.
  • Match retention and server deletion don't remove balances or ledger entries.
  • Show the balance and recent ledger entries on the player profile.
  • Document the endpoints, the credential and the limits in docs/guides/.
  • Add tests: a repeated request id applies once, overdraws and deposits over the maximum are refused, the telemetry token and another server's credential are refused, a GUID outside the server's active match is refused, and T5 and T6 share one balance.
[josh/tws-zombies-scripts#3](https://gitea.thewrightserver.net/josh/tws-zombies-scripts/issues/3) adds `.b`, `.d` and `.w` bank commands to the game scripts. Balances have to outlive a match, so Electric Cherry needs to store them by GUID and give the scripts an endpoint to read and change them. Nothing in Electric Cherry does this today. - `access.ts:40-41` lets game servers in through two routes only: `POST /api/telemetry` (machine) and `GET /api/leaderboard` (public, read-only). Neither changes anything a player can spend. - `auth/telemetry.ts:12-42` checks one shared secret, `EC_TELEMETRY_TOKEN`. The scripts carry it as a literal (`tws_t5_telemetry.gsc:34` and `tws_t6_telemetry.gsc:39` in josh/tws-zombies-scripts), so it's in that repo and in every deployed copy. - `api/telemetry/+server.ts:60` takes the server name from the request body, and player GUIDs come from the body too, so the token holder can speak for any server and any player. - `player-profile.ts:30` already shows one player per GUID across T5 and T6, while `leaderboard_players` stays keyed by game and GUID (`docs/guides/leaderboard.md`, "Player identity"). A bank endpoint is riskier than telemetry: whoever holds its credential can create points for any GUID, so reusing the shared telemetry token would let anyone with a copy of the scripts do that. Requests also fail and time out (the scripts send each report once, josh/tws-zombies-scripts#2), so a withdrawal Electric Cherry applied but the game never heard about would lose points, and a retried deposit would add them twice. **Fix** - [x] Store a balance per GUID, shared across T5 and T6 as player profiles are, and a ledger entry for every change (GUID, server, Electric Cherry match id when known, amount, resulting balance, time). - [x] Add machine endpoints for the scripts to read a balance, deposit and withdraw, routed in `access.ts` like `/api/telemetry`. - [x] Authenticate them with a credential per game server instead of `EC_TELEMETRY_TOKEN`, and refuse a request whose server name doesn't match its credential. - [x] Refuse a deposit or withdrawal for a GUID that Electric Cherry hasn't recorded in that server's active match. - [x] Each deposit and withdrawal carries a request id, and repeating an id returns the first result without applying it again. - [x] Refuse a withdrawal larger than the balance, and a deposit that would take the balance over a configurable maximum, without changing anything. - [x] Match retention and server deletion don't remove balances or ledger entries. - [x] Show the balance and recent ledger entries on the player profile. - [x] Document the endpoints, the credential and the limits in `docs/guides/`. - [x] Add tests: a repeated request id applies once, overdraws and deposits over the maximum are refused, the telemetry token and another server's credential are refused, a GUID outside the server's active match is refused, and T5 and T6 share one balance.
goddard added the
priority
low
type/ux
labels 2026-10-12 10:14:36 -04:00
josh was assigned by goddard 2026-10-12 10:14:36 -04:00
Author
Collaborator

Reviewed e071e5b574, d14c6a7ff4. All fix items are complete; closing this issue.

Checklist

  • Shared account and ledger. Migrations 20/21 add GUID accounts, request identities and a ledger; fleet/bank.ts updates them transactionally.
  • Machine API. POST balance/deposit/withdraw/commit/cancel routes use dedicated machine authentication.
  • Instance credentials. Credentials are generated per immutable instance, bound to the request server, preserved on rebuild and revoked on retirement. Telemetry credentials and cross-server requests are rejected.
  • Active membership. New transfers require a present GUID in the exact latest active reporter match.
  • Idempotency. Request fingerprints, durable results and a unique ledger identity prevent duplicate application; conflicting requests are rejected.
  • Limits. Overdraw, configurable bank maximum and game point capacity are checked before preparation; one prepared transfer per GUID serializes cross-server spending.
  • Retention. Account/ledger tables have no cascading match/server foreign keys; tests retain balances and settle prepared work after deletion.
  • Profile. Player profiles show balance, available/pending status and the latest 20 ledger entries, including expired match references.
  • Documentation. docs/guides/bank.md covers authentication, endpoints, limits, deployment and recovery.
  • Tests. bank.test.ts covers all requested cases plus cancellation-before-prepare, lost replies, DB reopen, rollback, concurrent preparations and scoped agent settlement.

Review notes

  • Reviewed current EC main at fa0baa8db5. Full application suite passed: 230 files, 3,120 tests, including migration-memory coverage; no failures or skips.
  • Python agent suite: 332 tests passed, no skips. Type/style checks: zero errors and warnings.
  • This closes the backend store/API scope, not the live game-bank rollout. Scripts #3/#13 still need live validation. The separate private-config drift/reconciliation defect remains tracked in EC #152 and is not resolved by these commits.
Reviewed e071e5b57432dcf6f354be9c65971e87b59dcf61, d14c6a7ff48e013b4ebe75b81c77424d50f188f5. All fix items are complete; closing this issue. **Checklist** - [x] **Shared account and ledger.** Migrations 20/21 add GUID accounts, request identities and a ledger; `fleet/bank.ts` updates them transactionally. - [x] **Machine API.** POST balance/deposit/withdraw/commit/cancel routes use dedicated machine authentication. - [x] **Instance credentials.** Credentials are generated per immutable instance, bound to the request server, preserved on rebuild and revoked on retirement. Telemetry credentials and cross-server requests are rejected. - [x] **Active membership.** New transfers require a present GUID in the exact latest active reporter match. - [x] **Idempotency.** Request fingerprints, durable results and a unique ledger identity prevent duplicate application; conflicting requests are rejected. - [x] **Limits.** Overdraw, configurable bank maximum and game point capacity are checked before preparation; one prepared transfer per GUID serializes cross-server spending. - [x] **Retention.** Account/ledger tables have no cascading match/server foreign keys; tests retain balances and settle prepared work after deletion. - [x] **Profile.** Player profiles show balance, available/pending status and the latest 20 ledger entries, including expired match references. - [x] **Documentation.** `docs/guides/bank.md` covers authentication, endpoints, limits, deployment and recovery. - [x] **Tests.** `bank.test.ts` covers all requested cases plus cancellation-before-prepare, lost replies, DB reopen, rollback, concurrent preparations and scoped agent settlement. **Review notes** - Reviewed current EC main at fa0baa8db5f7629de3f58f2e6bf8da0568018be0. Full application suite passed: 230 files, 3,120 tests, including migration-memory coverage; no failures or skips. - Python agent suite: 332 tests passed, no skips. Type/style checks: zero errors and warnings. - This closes the backend store/API scope, not the live game-bank rollout. Scripts #3/#13 still need live validation. The separate private-config drift/reconciliation defect remains tracked in EC #152 and is not resolved by these commits.
Sign in to join this conversation.