feat: omni-morph sprite swap, bullet time showboats, music tuning, server hardening

- 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>
This commit is contained in:
Dorian
2026-03-08 16:23:47 +00:00
co-authored by Claude Opus 4.6
parent 0acfa4417f
commit e12fb94ae7
13 changed files with 565 additions and 514 deletions
+19 -1
View File
@@ -1,6 +1,8 @@
import { Hono } from 'hono'
import { cors } from 'hono/cors'
import { logger } from 'hono/logger'
import { secureHeaders } from 'hono/secure-headers'
import { bodyLimit } from 'hono/body-limit'
import { botsRouter } from './routes/bots.js'
import { fightsRouter } from './routes/fights.js'
import { queueRouter } from './routes/queue.js'
@@ -20,7 +22,8 @@ export const app = new Hono()
app.onError((err, c) => {
console.error('[botfights] ERROR:', err.message, err.stack)
return c.json({ error: err.message }, 500)
const msg = process.env.NODE_ENV === 'production' ? 'Internal server error' : err.message
return c.json({ error: msg }, 500)
})
app.use('*', logger())
@@ -28,6 +31,21 @@ app.use('*', logger())
const allowedOrigin = process.env.CORS_ORIGIN || '*'
app.use('/api/*', cors({ origin: allowedOrigin }))
// Security headers: X-Frame-Options, X-Content-Type-Options, HSTS, Referrer-Policy, etc.
app.use('*', secureHeaders({
contentSecurityPolicy: process.env.NODE_ENV === 'production' ? {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", 'data:', 'blob:'],
connectSrc: ["'self'"],
fontSrc: ["'self'"],
} : undefined,
}))
// Body size limit: 256KB max for API requests (prevents OOM)
app.use('/api/*', bodyLimit({ maxSize: 256 * 1024 }))
// Rate limit all POST endpoints (60/min per IP)
app.use('/api/*', rateLimit(60_000, 60))