- 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>
All magic numbers in scoring.ts now reference constants from
lib/constants.ts: damage multipliers, score thresholds, quality
parameters, narration margins, and rounding factors.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fontSrc blocked fonts.gstatic.com and styleSrc blocked fonts.googleapis.com,
causing Honk font to fail in production. The chromatic aberration pseudo-elements
then appeared as doubled plain text instead of a styled 3D effect.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The sqlite.prepare() calls ran at module import time, before
runMigrations() created the analytics table, causing a crash loop.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add periodic cleanup of fightReactions Map (clears when >100 entries).
Add RSS/heap logging every 10 fights in fight-loop for memory monitoring.
Set --max-old-space-size=256 in Docker CMD as safety net.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add PRAGMA synchronous=NORMAL, temp_store=MEMORY, mmap_size=256MB for
better write throughput and query performance. Create 11 indexes on
commonly queried columns (fights.status, fights.created_at, bots.elo,
rounds.fight_id, payments, tournament_matches). Run PRAGMA optimize
on startup for query planner statistics.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
readLimitedBody already enforces 10KB body limit. Answer truncation
(2000 chars) and trash_talk truncation (200 chars) now reference
named constants from lib/constants.ts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add 526 new prompts across all 16 challenge types with heavy
Bitcoin/cypherpunk themes. Factual types include multiple-choice
options. Separate file for maintainability, merged at runtime.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Lightning Round (daily 8-bot at noon UTC) and The Halvening
(weekly 32-bot Saturday 20:00 UTC). Auto-fills with mock bots,
runs matches via background loop.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
POST /api/tournaments (create, admin-only),
POST /api/tournaments/:id/join, POST /api/tournaments/:id/start,
GET /api/tournaments (list), GET /api/tournaments/:id (bracket).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Create, join, seed by Elo, generate bracket matches with byes,
advance rounds on fight completion, finish tournament on champion.
Wire into orchestrator fight finalization.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add tournaments, tournament_entries, and tournament_matches tables
with Drizzle ORM definitions and SQLite CREATE TABLE migrations.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add per-IP SSE connection limit (max 5 concurrent streams). Track
activeSSECount and ssePerIp maps with proper decrement in finally
blocks. Export getActiveSSECount() for admin stats. Change heartbeat
ping from 5s to 15s interval. Connections already clean up on client
disconnect via Hono's streamSSE try/catch/finally pattern.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add MAX_MAP_SIZE (10,000) cap to botHitCounts Map. During the 5-minute
cleanup interval, evict oldest entries when the map exceeds the limit.
Export cleanupInterval handle for graceful shutdown.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Create server/src/lib/logger.ts with info/warn/error methods that add
[botfights:tag] timestamps. Replace bare console.log/warn/error calls
in index.ts, seed.ts, routes/fights.ts, engine/queue.ts, engine/mock.ts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- FightPage.vue: store SSE listener refs in array, removeEventListener
on each before close() in disconnectSSE()
- FightScene.ts: add cleanupTimers Set with trackedInterval/trackedTimeout
helpers; replace key setInterval calls (projectile trails, entrance
flames, talking animation, exhaust effects, dimensional shift, hole
fade) with tracked versions; clear all on scene destroy()
- rate-limit.ts: export cleanupInterval handle for graceful shutdown
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- app.ts: type serveFile context parameter as Hono Context
- bets.ts: replace catch(err: any) with err: unknown + narrowing
- fight-loop.ts: type pickMatchup style as union literal, replace
as any casts with proper result type
- tts-worker.ts: import KokoroTTS type, use ProgressInfo inference
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Elo-bracket matchmaking: prefer ±200, widen by 100 every 15s of waiting.
Add pickRankedChallenge() that filters to creative/open-ended only, never
multiple choice. Show ranked queue status, wait estimate, and "HARDER
PROMPTS" notice on JoinBoutPage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add GET /api/bots/leaderboard?season=current endpoint. LeaderboardPage
now toggles between "This Season" and "All Time" views. Shows season
name, countdown timer, and top 3 placement badges.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Create seasons engine with getCurrentSeason(), getSeasonLeaderboard(),
and resetSeasonElo(). 20 Bitcoin-themed season names. Add currentSeason
column to fights table, set on fight creation. Elo soft-reset formula:
finalElo * 0.6 + 1200 * 0.4.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add ZAP WINNER button in FightViewer after fight ends. Server endpoint
POST /api/payments/zap increments zapsReceived on winner bot. Show zap
count on bot profile page. Add zaps_received column with migration.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fetch full kind:0 metadata (display_name, about, banner, nip05) from
relays with a 5-min TTL cache. Show Nostr banner, display name, and
NIP-05 on bot profile pages. Expose ownerPubkey in stats API.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
NIP-01 kind:1 notes posted for KOs, perfects, upsets, and big Elo
swings. Includes bot names, Elo changes, arena, and replay link.
Configurable via BOTFIGHTS_NOSTR_RELAYS and BOTFIGHTS_NOSTR_NSEC env
vars. Silently skips if NSEC not set.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Lock bets when fight goes live, settle all bets atomically when fight
ends. Winners get Cashu payout tokens, losers forfeit. Draws refund
all bets. Settlement results persisted to bets table.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
POST /api/fights/:fightId/react endpoint accepts emoji reactions (fist,
fire, skull, 100, clown), aggregates counts, broadcasts via SSE.
Reaction bar added to FightViewer with floating emoji particles that
rise and fade. Live fight views receive reactions via SSE in real-time.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Enable real-time fight spectating for all live fights (not just human
fights). Multiple spectators can watch simultaneously via SSE. Spectator
count is tracked per-fight and broadcast with every SSE event.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Creator omni-morph now generates actual sprite sheets for morphed archetypes
- 3 new Creator showboats: bullet time attack, ₿ throne summon, disco dance
- Music: subtle tempo shift (+10 BPM max), longer phrases (8/16/24 bars),
smoother crossfades, less chaotic hi-hat at high intensity
- Server: security headers, body size limit, production error masking,
CORS origin warning, graceful shutdown with drain
- Payments: atomic consume (eliminates SELECT/UPDATE race), release reverts DB
- Fight loop: round events for live TUI, retro displayPrompt
- Frontend: pass pubkey in payment/queue requests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
TTS fixes:
- Remove 60-char truncation in FightViewer — let speakAnswer handle limits
- Smart truncation at sentence boundaries (period, comma, etc.) up to 200 chars
- Chrome keepalive: periodic pause/resume prevents silent 15s cutoff bug
- Deduplicated cleanup logic in speakAsync/speakAsyncWithRate
Retro mode:
- Simplified challenge prompt from verbose wall of text to clean 3-line format
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove all super/ultra combo inputs, exact damage values, discovery
multiplier (1.5x), and Konami Code from BOT_SETUP.md, bot-guide.md,
docs.ts API, and example bot. Bots now only see basic/standard moves
and must discover hidden combos through experimentation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update BOT_SETUP.md with full retro mode combo reference, scoring,
and strategy tips. Add docs/bot-guide.md as a standalone developer
guide covering all challenge types including retro mode. Add
docs/sprite-guide.md and sprite-reference.html for custom sprite
creation. Update docs.ts API endpoint and Python example bot with
retro_mode handling.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Every fight now includes one Retro Mode round where bots submit
gamepad combo inputs (↑↓←→ A B). 24 moves across 4 tiers: basic
(always shown), standard (partially revealed), super (must discover),
and ultra (KONAMI CODE for 50 dmg). Discovery bonus gives 1.5x damage.
Includes pixel-art gamepad overlays (P1/P2) with animated button
presses, retro-specific narrations, and mock bot combo responses
scaled by ELO.
Also adds loops/plan.md with 11-phase production hardening roadmap.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Login auto-creates a human fighter for the Creator pubkey if no DB entry
exists. register-human now applies the_creator archetype for the Creator.
Update endpoint can no longer override the Creator's archetype.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move crypto key init from module-level throw to lazy getKey() — only
errors when encrypt/decrypt are actually called. Downgrade env var
check in index.ts from fatal exit to warning.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>