games: the wire the euros cross

Three bearer-authed endpoints and gogobee can work the border: poll what's
waiting, claim a row, report what happened to the money. The storage layer
underneath was already done; this is the transport, and deliberately nothing
more.

All three are idempotent, because the thing on the other end of them is a
retrying queue and the thing they move is money. A verdict delivered three
times creates chips once. A rejected buy-in moves nothing and clears the
pending amount so it stops eating the table cap. A cash-out gogobee couldn't
pay gives the chips back rather than vanishing them from both sides.

A verdict for a row Pete has never heard of is a 400, not a shrug: gogobee has
by then moved real euros against it, and no amount of retrying invents the
missing row. Under the contract the adventure seam set, a 400 parks it in
gogobee's queue where a human can find it.
This commit is contained in:
prosolis
2026-07-13 23:00:19 -07:00
parent a442cfccaa
commit 44613c4760
4 changed files with 430 additions and 8 deletions

View File

@@ -47,23 +47,38 @@ A multi-session build. This section is the handover; read it before anything els
their euros are in flight) and come back if the credit fails. Table cap, 30-minute
reaper, per-hand audit log with seeds. 17 tests. *(pete `f9a98f7`)*
- **The wire protocol.** Pete serves `GET /api/games/escrow/pending`, `POST …/claim`,
`POST …/settled` (`internal/web/games.go`), bearer-authed on the adventure ingest
token. gogobee polls every 3s (`internal/plugin/pete_games.go`), claims a row,
calls `DebitIdem`/`CreditIdem` against the escrow GUID, and pushes the verdict
back through `pete_emit_queue` — which grew a `path` column so escrow verdicts
ride the same durable queue as adventure facts rather than getting a second one.
`peteclient.Flush` sends the verdict immediately instead of waiting out the 15s
sender tick, because a player is watching a spinner. A row re-offered after a
gogobee crash replays as a no-op: 13 tests across both repos, including a fake
Pete that offers the same row three times and a player who is charged once.
### Next, in order
1. **Wire protocol.** Pete: `GET /api/games/escrow/pending`, `POST …/claim`,
`POST …/settled`, all bearer-authed like the adventure seam. gogobee: the poll
loop in `internal/peteclient` — its first GET path — calling `DebitIdem`/`CreditIdem`
and pushing verdicts back through the existing `pete_emit_queue`. The storage layer
underneath this is done and tested; only the HTTP and the loop remain.
2. **Identity.** `PreferredUsername` into the OIDC claims struct and the signed cookie;
1. **Identity.** `PreferredUsername` into the OIDC claims struct and the signed cookie;
cookie `Domain: ".parodia.dev"` so a news session travels to games. Add the games
redirect URI to the `pete` app in Authentik.
3. **Frontend.** Host branching in the mux, lobby + blackjack table, animated dealing.
4. Then Phase 2 (trivia, hangman), 3 (UNO), 4 (hold'em) as below.
2. **Frontend.** Host branching in the mux, lobby + blackjack table, animated dealing.
Nothing yet *opens* an escrow row from a browser — `RequestBuyIn`/`RequestCashOut`
have no HTTP surface, on purpose: they need a signed-in Matrix identity, which is
step 1.
3. Then Phase 2 (trivia, hangman), 3 (UNO), 4 (hold'em) as below.
### Notes for whoever picks this up
- SQLite runs at `MaxOpenConns(1)` in *both* repos. Any `db.Get().Exec` inside an
open transaction deadlocks against itself. Do the pre-work before `Begin`.
- **A buy-in can currently take a player into debt.** `DebitIdem` inherits
`BLACKJACK_DEBT_LIMIT` (default 1000), so someone with an empty wallet can buy
€1,000 of chips, win, and cash out while still €1,000 down. That is exactly what
gogobee's Matrix blackjack already allows, so it is consistent rather than a bug —
but a web casino runs far more hands, and this is the knob to turn if the economy
starts leaking. A buy-in-specific floor of 0 is a two-line change.
- gogobee's blackjack taxes 5% of the *gross* payout into a community pot
(`communityTax`). Pete's rake takes 5% of the *profit*. Deliberately different, and
gentler; don't "fix" one to match the other without deciding which is right.