Commit Graph
80 Commits
Author SHA1 Message Date
DorianandClaude Fable 5 ca5b63468e feat: "let BotFights answer for me" — server-side AI bot (poll mode), + Latest Bouts short-viewport fix
CI / check (push) Failing after 6m8s
New feature, requested live during demo prep: an operator can paste their
own Anthropic or OpenAI API key and have the server itself answer fight
challenges for their poll-mode bot, instead of running an external script.

Storage (server/src/engine/ai-bot-config.ts): one 0600 JSON file per bot
under this app's own data volume — deliberately mirrors Archipelago's own
node-level pattern for the identical class of secret (system.settings.set
"claude_api_key" in core/archipelago/src/api/rpc/system/handlers.rs): never
returns the raw key on GET, only whether one is configured and which
provider. This is a human operator opting in for their own bot via the
app's own UI — a different trust boundary from the unified prompt's "never
ask an AI agent for its API key" rule (BOTFIGHTS.md), not a violation of it.

Execution (server/src/engine/orchestrator.ts): purely additive hook inside
the existing isPollingBot branch of getBotResponse(). waitForPollResponse()
(unchanged) registers the pending challenge synchronously before returning;
right after, answerWithAiIfConfigured() fires a fire-and-forget async LLM
call that races to call submitPollResponse() — the exact function an
external poller already calls — before that promise's own timeout. No AI
config = instant no-op. LLM error/timeout = the existing timeout path
handles it identically to a human forgetting to poll. No new failure mode,
no change to scoring/round timing for any other bot.

Adapter (server/src/engine/llm-adapter.ts): both providers, one call shape
each (Anthropic /v1/messages + x-api-key, OpenAI /v1/chat/completions +
Bearer), same competitive system prompt already documented in BOTFIGHTS.md
for operator-run bots.

API (server/src/routes/bots.ts): POST/GET/DELETE /api/bots/ai-config,
authenticated via the bot's own Authorization: Bot <id>:<secret> (same as
/api/fights/poll). Registered BEFORE GET /:name — same route-shadowing bug
class already found once in fights.ts's /poll route (09-05); a bare /:name
registered first would have swallowed /ai-config as a bot-name lookup.
Covered by a new test (ai-config.test.ts) that asserts the real contract
shape, not just a 200, specifically to catch that regression.

UI (frontend/src/pages/JoinBoutPage.vue): collapsible section in the
bot-setup step, poll mode only (webhook mode already assumes the operator
runs their own infra) — provider picker, password-type key input, links to
get a key from either provider, key cleared from page state immediately
after saving.

Explicitly deferred (not built here): "use this node's key" as an
alternative to pasting your own — the node already has one configured for
AIUI (found live, /opt/archipelago/claude-api-proxy.py), but wiring a
cross-container secret share from the archy orchestrator into this
container needs a manifest change and another catalog signing cycle, which
this session isn't improvising under demo time pressure.

Also: HomePage.vue "Latest Bouts" section hidden on short viewports
([@media(max-height:700px)]:hidden) — the hero layout is a vertically-
centered flex column with overflow-hidden and no scroll by design, so on a
short viewport (embedded node dashboard iframes, small kiosk screens) this
last/least-essential section was what silently clipped, reported live as
"it looks cut off on node screens often".

Fixed a regression-test violation this batch would have introduced
(BUG-F2: no silent .catch(() => {})) in the DocsPage.vue proxy-URL-resolve
fix from the previous commit — both catches now log a warning instead of
swallowing silently.

Verified: full server typecheck clean; orchestrator.test.ts (23) +
poll-responses.test.ts (10) unchanged and passing — the new hook doesn't
alter existing poll-mode behavior; new ai-config.test.ts (7) passing,
including the route-shadowing regression check; regression.test.ts (43,
including the newly-fixed BUG-F2) passing. lifecycle.test.ts/scoring.test.ts
perf-timing failures are pre-existing, documented, unrelated flakiness
under this shared machine's CPU load (see archy's 09-01 deferred-items.md
item 2) — not caused by this change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 08:06:02 -04:00
DorianandClaude Fable 5 12d4b35404 fix(09-05): GET /api/fights/poll was shadowed by GET /:id, always 404d
server/src/routes/fights.ts registered the dynamic GET /:id route before
the static GET /poll route. Hono resolves same-shape single-segment
routes in registration order, so any GET /api/fights/poll request was
matched as a fight-id lookup for id="poll" and always returned
404 {"error":"Fight not found."} instead of the poll handler's
{"pending":false}/{"pending":true,...} response.

This meant the polling protocol — one of the two bot integration modes
BOT-02's unified prompt documents — never actually worked. Found while
verifying bot auth against the freshly-rolled 1.2.0 arena (plan 09-05
Task 2 acceptance criterion), reproduced independently on a throwaway
container with a fresh DB to confirm it wasn't an artifact of the
arena's seeded data.

Fix: move the /poll and /poll/respond route registrations above /:id.
No other GET route in this router collides in shape with /:id (verified
by listing every registered path/method pair).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 23:21:34 -04:00
DorianandClaude Fable 5 2a343ac746 feat(09-02): retire the bare-pubkey session path (BOT-01)
CI / check (push) Failing after 6m11s
Client: useNostr.ts's auto-restore now calls GET /api/auth/me (a plain
authFetch, no body) instead of POSTing {pubkey} to /api/auth/login —
identity is derived server-side from the JWT alone, never claimed by
the client.

Server: POST /login is reduced to a pure, documented-deprecated read.
Removed the creator auto-create branch and the creator auto-upgrade
db.update block — an unauthenticated request can no longer mutate the
database via this endpoint. The identical creator auto-create/upgrade
logic already exists, correctly gated behind NIP-98 verification, in
POST /nostr/session, so a creator signing in with a real signer still
gets the same row created/upgraded. Added a handler doc comment plus a
new auth.test.ts case asserting an unregistered creator pubkey now
returns exists:false and leaves the bots table row count unchanged.

e2e/helpers/auth.ts: doc comments updated to describe loginWithPubkey
as a read-only test lookup helper, not a login; request/signature
unchanged so existing e2e specs keep working.

Verification: auth.test.ts + auth-edge.test.ts + auth-audit.test.ts +
auth-me.test.ts = 56/56 pass. Full server suite (bypassing pnpm's
install-gate via ./node_modules/.bin/vitest, since this environment's
pnpm needs an interactive build-approval step unrelated to this task)
= 810/817 pass, remaining 7 are pre-existing timing/perf flakes under
CPU load (lifecycle/speed-meta/tier-balance/bot-auth constant-time),
none touching auth. tsc (server) and vue-tsc (frontend) both exit 0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 22:31:26 -04:00
Dorian a0809565f2 feat(09-03): serve the unified AI bot-setup prompt at GET /api/docs/prompt
CI / check (push) Has been cancelled
- New docsRouter.get('/prompt') resolves the shipped container path
  (server/public/docs/BOTFIGHTS.md) then falls back to the dev-checkout
  path (frontend/public/docs/BOTFIGHTS.md), matching app.ts's publicDir
  derivation pattern
- Substitutes {{ARENA_URL}} with PUBLIC_ARENA_URL when set, otherwise the
  request's own origin, so a cloud agent that curls the prompt gets
  working examples pointed back at the arena it fetched from
- Responds as text/markdown so an agent can pipe the response straight
  into its context
- 5 new Vitest cases: 200+content-type, no leftover {{ARENA_URL}} token,
  PUBLIC_ARENA_URL precedence, origin fallback, YOUR_BOT_ID placeholder
  preserved for the in-app substitution flow
2026-07-30 22:23:20 -04:00
DorianandClaude Fable 5 635ee39373 feat(09-02): GET /api/auth/me — identity from JWT, never a claimed pubkey (BOT-01)
CI / check (push) Has been cancelled
Adds a JWT-gated, read-only session-restore route. Mirrors POST /login's
projection and 200 body shape exactly so normalizeBotData on the client
is unchanged. extractPubkeyFromAuth (already imported later in the file
for /regenerate-secret) covers missing/malformed/forged/expired/
blacklisted tokens via verifyJwt; deduped the now-redundant import at
the /nostr/session section. All 7 auth-me.test.ts cases pass; tsc clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 22:09:11 -04:00
DorianandClaude Fable 5 e824f4ca7f test(09-02): add failing test for GET /api/auth/me (BOT-01)
Covers missing / malformed / forged / blacklisted / unregistered / valid
JWT cases for the JWT-only identity route that replaces the bare-pubkey
auto-restore path. Route does not exist yet — 6/7 fail as expected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 22:03:18 -04:00
DorianandClaude Fable 5 0511b97cb9 feat(09-01): arena-proxy survives SSE, client-IP forwarding, upstream-down
CI / check (push) Has been cancelled
- Forward client IP via x-forwarded-for/x-real-ip so the canonical arena's
  per-IP rate limiting isn't collapsed to one bucket per node.
- 30s AbortSignal.timeout on non-stream requests; SSE fight streams
  (/api/fights/:id/stream) are exempt (long-lived by design).
- On upstream fetch failure, log and return 502 {error} instead of a
  buffered hang or a 500 stack trace.
- fights.ts: set X-Accel-Buffering: no on the SSE stream response so an
  nginx-fronted arena (nginx-proxy-manager) doesn't buffer live fight events.
- docker-compose.yml: document ARENA_UPSTREAM_URL / TRUSTED_PROXY (commented,
  no active value set here — the canonical arena gets its own compose file
  in a later plan).

TDD: added the SSE/XFF/502 tests, confirmed the 502 test failed against the
prior implementation, then implemented to green (9/9 arena-proxy tests,
17/17 combined with rate-limit.test.ts).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-30 21:32:57 -04:00
Dorian 32e6c19f72 stuff 2026-04-11 19:46:37 +01:00
DorianandClaude Opus 4.6 18e4b05399 fix: practice fights send challenges to polling/webhook bots instead of forcing human mode
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 23:23:37 +00:00
DorianandClaude Opus 4.6 8076d860c7 fix: creator auto-creates as bot (poll mode), auto-upgrade existing human records
The creator was being registered with webhookUrl='http://human.local/' and
isHuman=true. Now uses poll.local and isHuman=false. The auto-upgrade logic
on login also converts any existing creator record from human to bot mode.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 22:28:43 +00:00
DorianandClaude Opus 4.6 cef9f4188f feat: fix setup guide download with embedded credentials + profile page regenerate secret
- downloadSetupGuide() now triggers a real file download with bot_id/secret injected
- Add POST /api/auth/regenerate-secret endpoint (JWT auth, 3/hour rate limit)
- Add "Download Setup Guide" section to BotProfilePage with secret regeneration flow
- Old secret immediately invalidated on regeneration

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 13:35:48 +00:00
DorianandClaude Opus 4.6 cae8f0b83e test: add comprehensive auth audit — 28 tests verify endpoint protection
Covers all 7 admin endpoints (403 without creator pubkey), polling
endpoints (bot auth required), tournament mutations (creator-only),
ranked queue (pubkey ownership), NIP-98 session (signature required),
and 10 public endpoint accessibility checks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 10:37:46 +00:00
DorianandClaude Opus 4.6 18b92fbbdf test: API auth audit — 18 tests verify auth, rate limiting, validation, error sanitization
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 10:34:08 +00:00
DorianandClaude Opus 4.6 f15504b400 test: add auth registration edge cases — concurrent names, expired JWT, NIP-98 tampering
Tests concurrent same-name registration (exactly one succeeds),
case-insensitive name collisions, expired JWT rejection, NIP-98
pubkey mismatch, and duplicate pubkey prevention.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 10:15:13 +00:00
DorianandClaude Opus 4.6 9b0d251d1c fix: sanitize error responses to prevent internal detail leakage
Add sanitizeError() helper that strips file paths, stack traces, SQLite
errors, and system errors from messages before returning them to clients.
Applied to all route-level catch blocks in payments, queue, fights, and
admin routes. Includes 12 tests for the sanitizer and static analysis
test verifying no route files leak raw err.message.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 09:46:38 +00:00
DorianandClaude Opus 4.6 dc9884e27e test: verify wallet disconnect wipes connection data from DB
Add test confirming disconnect-wallet deletes walletConnections rows
and sets hasWallet=false on the bot record.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 09:33:50 +00:00
DorianandClaude Opus 4.6 c5744f5984 test: pentest payment edge cases with 10 attack vector tests
Cover amount=0, over-max (999999999), zap non-winner, zap unfinished
fight, invalid wallet method, empty cashu token, claim nonexistent
payment, confirm already-confirmed, confirm outbound payment, claim
payment with no token.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 09:32:19 +00:00
DorianandClaude Opus 4.6 2fea2bf8c9 test: verify error responses never leak stack traces or file paths
Add tests confirming production error handler sanitizes all internal
errors (ENOENT, stack traces, file paths). Add static analysis test
verifying no route file passes err.stack to c.json() responses.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 09:30:55 +00:00
DorianandClaude Opus 4.6 21bb46c1b0 test: verify admin endpoints require creator pubkey on all routes
Test all 7 admin endpoints (stats, bots, deactivate, activate,
reset-elo, fights, backup) reject non-creator pubkeys and missing
pubkeys with 403. Verifies global middleware guard works correctly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 09:29:08 +00:00
DorianandClaude Opus 4.6 d4c51f0aac fix: tighten auth rate limits to 10/min and add rate limit tests
Reduce login and nostr/session rate limits from 30 to 10 requests per
minute per IP to prevent brute-force attacks. Add tests verifying 429
response after exceeding the limit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 09:28:38 +00:00
DorianandClaude Opus 4.6 2051a95e13 refactor: add centralized Zod validators for all API inputs
Create server/src/lib/validators.ts with reusable schemas for all API
inputs (auth, fights, bets, payments, tournaments, queue, docs).
Import and use in all route handlers, replacing inline validation.
Add formatZodError helper for user-friendly error messages.
77 test cases in validators.test.ts cover valid, invalid, boundary,
and attack inputs (SQL injection, XSS, prototype pollution).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 09:16:04 +00:00
DorianandClaude Opus 4.6 5e40221a2f test: SSE, polling, human, and concurrent fight integration tests
14 integration tests covering full fight lifecycle with real in-memory
DB: SSE event ordering, polling bot challenge/response flow, human
player response submission, and 3 concurrent fights without interference.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 05:10:27 +00:00
DorianandClaude Opus 4.6 41c66d7732 test: full fight flow integration test with real in-memory DB
8 tests covering: complete fight lifecycle, HP progression, ELO
updates, concurrent fight prevention, round data validity, ELO
conservation, status transitions, and win streak tracking.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 05:00:25 +00:00
DorianandClaude Opus 4.6 11a76cc249 fix: selective leaderboard cache invalidation instead of full clear (BUG-S10)
Only invalidates __alltime__ and current season cache keys on fight completion,
preserving historical season caches. Test verifies selective behavior.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 00:03:16 +00:00
DorianandClaude Opus 4.6 c224776c90 test: add payments.test.ts (12 cases) and expand bets.test.ts (30 cases)
Covers wallet connection, invoice creation, payment confirmation, zap validation,
odds calculation, escrow settlement, bet validation, and display conversion.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 00:01:17 +00:00
DorianandClaude Opus 4.6 f18b04ba20 test: verify global error handler returns 500 without stack traces (BUG-S7)
Global app.onError handler already in app.ts catches all unhandled route
exceptions. Production mode returns "Internal server error" only.
Tests verify no stack traces or file paths leak in responses.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 23:51:12 +00:00
DorianandClaude Opus 4.6 34a83fb0fc test: add auth routes test suite with 13 cases
Tests check-name validation, login pubkey validation, register name/pubkey
validation, register-human validation, NIP-98 session (valid + expired).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 23:43:57 +00:00
DorianandClaude Opus 4.6 5e0bc1dc00 test: verify correct/incorrect feedback in respond endpoint (BUG-1)
Tests confirm checkAnswer integration: correct answer returns
correct: true, wrong answer returns correct: false.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 23:17:16 +00:00
DorianandClaude Opus 4.6 370d8643b7 fix: add Zod enum validation for challenge types in webhook tester (BUG-S6)
Validates challenge type against the full CHALLENGE_TYPES enum before
processing. Invalid types now return 400 instead of silently falling
back to speed_blitz.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 22:56:26 +00:00
DorianandClaude Opus 4.6 e4a7f47e0f fix: validate Cashu token format before placing bets (BUG-S4)
Added getDecodedToken validation that rejects malformed tokens with
400 before any DB lookups. Tests cover empty, non-base64, truncated,
and random base64 tokens.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 22:46:24 +00:00
DorianandClaude Opus 4.6 c6a54d63c4 fix: add rate limiting to /poll endpoint + fix test type errors (BUG-S3)
- Add rateLimit(1_000, 30) middleware to GET /poll endpoint
- Fix Challenge type errors in human-responses test files (missing baseDamage)
- Add rate-limit unit test verifying 429 after exceeding limit

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 22:41:59 +00:00
DorianandClaude Opus 4.6 8468c89352 test: verify tournaments .get() is sync + add route tests
better-sqlite3 driver is synchronous — .get() does NOT need await.
Added tests for unknown pubkey (404) and missing pubkey (400) on join.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 22:32:37 +00:00
Dorian 29a0a48eb1 human fight sequence fix 2026-03-11 10:02:37 +00:00
Dorian bcbcd17fce challenges fix 2026-03-11 08:35:03 +00:00
DorianandClaude Opus 4.6 68e292183a fix: polling bots play practice fights as human players in browser
Polling bots have no external script running during practice mode,
so the poll would time out giving empty answers. Now overrides the
webhook URL to human.local so the browser UI handles challenges.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 19:02:35 +00:00
DorianandClaude Opus 4.6 95ed80335a feat: polling API, HMAC webhook signing, session-only keys, prod audio fix
- Add polling API (GET/POST /api/fights/poll) so bots don't need public URLs
- Add HMAC-SHA256 webhook signing (X-Botfights-Signature header)
- Stop auto-persisting nsec keys — session-only by default with opt-in "Remember on this device"
- Fix production TTS: add wav/mp3/ogg MIME types, /audio/* route, SPA blocklist
- Overhaul docs: mode selector (poll vs webhook), AI-first bot examples, security tab
- Fix duplicate sign-in buttons, login flow bugs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 18:34:22 +00:00
DorianandClaude Opus 4.6 6dc50f5d5d feat: move creator pubkey to env, fix mobile TTS + signer, button loaders
Security:
- Move CREATOR_PUBKEY from hardcoded constant to BOTFIGHTS_CREATOR_PUBKEYS
  env var. Shared isCreatorPubkey() in constants.ts used by auth, admin,
  tournaments. Frontend checks authorization via API, not client-side.

Mobile fixes:
- Nostr signer: poll for window.nostr up to 3s (Amber injects late).
- TTS: auto-unlock AudioContext on first user interaction via
  installAutoUnlock() on fight page mount.

UX:
- Add loading spinners to "I BUILD BOTS" and "I FIGHT MYSELF" buttons.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 15:31:58 +00:00
DorianandClaude Opus 4.6 3ba05a66b4 feat: NIP-98 + JWT authentication with signer support
Replace insecure raw-pubkey auth with cryptographic NIP-98 signed
requests and server-issued JWT sessions. Logout now fully clears
all state including nsec. Add yellow "Use Nostr Signer" button
for Amber/NIP-07 remote signers.

- Server: JWT middleware (HMAC-SHA256, 24h expiry), NIP-98 verification
- Server: POST /api/auth/nostr/session endpoint
- Frontend: NIP-98 token builder + authFetch wrapper with JWT Bearer
- Frontend: All authenticated API calls use authFetch
- Security: logout clears JWT, pubkey, bot, nsec, and profile pic

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 12:31:25 +00:00
DorianandClaude Opus 4.6 e31d49898b feat: show rejoin link when bot is already in a fight
- Track active fight IDs per bot in orchestrator (Set → Map)
- Return fightId in "already in fight" error responses (409)
- Frontend shows "REJOIN FIGHT" link instead of generic error

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 09:27:48 +00:00
DorianandClaude Opus 4.6 3c32f01aa7 feat: add Retry-After header and countdown timer for rate limits
- Rate limiter returns retryAfterSec in response body + Retry-After header
- Registration rate limits: 10 per 10min (was 15/hour)
- Frontend surfaces retry timer in error messages

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 09:15:30 +00:00
DorianandClaude Opus 4.6 4cc18048e8 refactor: replace console.log/error with structured logger across server
Migrate all server modules to use the centralized logger (lib/logger.ts)
instead of raw console calls. Lint warnings reduced from 74 to 25.
Remaining warnings are only no-floating-promises in game engine code.

Files updated: orchestrator.ts, ranked-queue.ts, human-responses.ts,
payments.ts, fight-loop.ts, app.ts, routes/payments.ts
Files suppressed: logger.ts, fight-loop-cli.ts, migrate.ts (legitimate console use)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 09:06:36 +00:00
DorianandClaude Opus 4.6 3c95adb545 feat: convert all human fights to multiple choice, remove text input
Creative challenges (roast_battle, creative_writing, meme_war,
code_golf, wrestling_match) now auto-generate multiple choice options
from per-type response pools: 1 good answer + 3 weaker distractors.
The free text input UI is commented out but preserved for future use.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 08:28:22 +00:00
DorianandClaude Opus 4.6 4074ef94eb feat: add leaderboard cache, performance benchmarks, and verify SSE cleanup
- Add 30s TTL leaderboard cache with invalidation on fight completion
- Add scoreRound performance benchmark: 1000 rounds in <100ms
- Add checkAnswer performance benchmark: 1000 checks in <50ms
- Add adversarial regex backtracking test for checkAnswer
- Verify SSE cleanup: connections, IP counters, spectator counts, event listeners
  all properly decremented in finally block on disconnect

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 07:50:46 +00:00
DorianandClaude Opus 4.6 7d9b8b1dbf chore: add ESLint with no-floating-promises and no-console rules
Sets up ESLint 10 flat config with @typescript-eslint/no-floating-promises (error)
and no-console (warn, allow warn/error). Fixes all floating promise errors in
server routes, orchestrator reader cleanup, and frontend composables with void operator.
Game engine files get warning-level for intentional fire-and-forget async.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 07:47:43 +00:00
DorianandClaude Opus 4.6 fbc61ef154 fix: add amount validation for zap and bet endpoints
Validate amountSats is a positive integer (1–1,000,000) on both
/zap and /bets/place endpoints to prevent negative, zero, or
absurdly large amounts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 07:34:51 +00:00
DorianandClaude Opus 4.6 5bfb63aa7f fix: add input validation, Zod schemas, rate limiting, and IP trust
- Add Zod schema for webhook response parsing (orchestrator.ts)
- Add Zod schemas for POST /respond and /react request bodies
- Add safe integer validation for batch count param
- Prefer cf-connecting-ip over spoofable x-forwarded-for
- Add ID format validation on URL params
- Add rate limiting on /auth/login (30/min) and /update (10/min)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 07:30:49 +00:00
DorianandClaude Opus 4.6 69601cce3a feat: automated backup and production monitoring
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 00:33:35 +00:00
DorianandClaude Opus 4.6 8be71e9929 fix: admin and docs bugs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 00:20:00 +00:00
DorianandClaude Opus 4.6 c654ff9f73 feat: interactive bot developer documentation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 00:18:06 +00:00
DorianandClaude Opus 4.6 e10c1dc8aa feat: privacy-respecting analytics
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 00:15:20 +00:00