================================================================================ DEMOCRACY RISING / FUNDRAISING PLATFORM — DEEP SITE AUDIT ================================================================================ Generated: 2026-05-19 (UTC) Scope: /root/fundraising-platform (source, config, Prisma, scripts, runtime) Machine: Linux (Proxmox LXC), 4 vCPU, ~7.8 GiB RAM, 64 GiB disk (~7% used) Service: fundraising-platform.service → npm run start → tsx server.ts :8008 Public: https://bwt.democracyrisingbwt.us (Cloudflare proxy, not direct to origin) Typecheck: npm run lint (tsc --noEmit) — PASS at audit time Build: npm run build — PASS (includes /threads-callback, /api/threads-exchange) Severity legend: [CRITICAL] Exploit, money loss, secret exposure, or production outage risk [HIGH] Broken feature, data corruption risk, or major UX failure [MEDIUM] Correctness edge case, maintainability, or meaningful perf waste [LOW] Polish, SEO, docs, minor duplication ================================================================================ 0. EXECUTIVE SUMMARY ================================================================================ This is a Next.js 16 App Router political fundraising app with: - Stripe donations + webhooks - Supporter credits (BWT) wallet + ledger - 10 casino-style games (7 REST solo, 2 Socket.IO PvP, 1 prediction market) - Community features (billboard, spotlight, cards, FAQ board, boost meter) Overall code quality: TypeScript compiles clean; core wallet debits use atomic updateMany; many game routes use optimistic locking (updateMany + outcome:"active"). The largest risks are DEPLOYMENT/DNS (Cloudflare serving stale static pages), SOCKET/PONG in-memory state (single-process assumption), BLACKJACK transaction gaps, and CLIENT-SIDE performance (always-on canvas animations + polling). Approximate codebase: ~15,246 lines in src/**/*.ts(x), 561M node_modules, 1.1G .next Top 10 actions (ordered): 1. Fix Cloudflare routing for bwt.democracyrisingbwt.us → origin :8008 (not static HTML) 2. Align NEXT_PUBLIC_SITE_URL / AUTH_URL with real public hostname 3. Rotate any secrets ever committed or logged (.env, old Threads static page) 4. Wrap blackjack natural/push/resolve in single DB transactions 5. Add gameSessionId FK on ledger entries for reconciliation 6. Document/require npm run start (not dev:next) for Socket.IO games 7. Reduce homepage/global canvas + polling load on this 8GB box 8. Delete or wire DonationCheckout.tsx (~400 lines dead) 9. Harden coin-flip/pong payout failure paths (room stuck ACTIVE) 10. Add rate limits on /api/register and game POST endpoints ================================================================================ 1. DEPLOYMENT & INFRASTRUCTURE (CRITICAL FOR LIVE SITE) ================================================================================ 1.1 [CRITICAL] Cloudflare intercepts traffic before Next.js ------------------------------------------------------------------------------ Symptom verified on live host: - https://bwt.democracyrisingbwt.us/datadeletion → was 404 until service restart; may still vary by cache - https://bwt.democracyrisingbwt.us/threads-callback → serves OLD static HTML ("Threads Bot Setup", client-side secret exchange) NOT the Next.js route - curl shows: server: cloudflare, cf-ray present - DNS: bwt.democracyrisingbwt.us → 104.21.29.229 / 172.67.171.225 (Cloudflare) - Origin direct :8008 on public IP → connection refused (port not exposed) Local origin (correct): - http://127.0.0.1:8008/threads-callback → Next.js page "Threads callback" - http://127.0.0.1:8008/datadeletion → legal page Old static /threads-callback HTML (served via Cloudflare) contains: - client_id in page JS - client_secret in browser fetch to graph.threads.net (SECURITY DISASTER) - Inline OAuth flow bypassing /api/threads-exchange Action: In Cloudflare dashboard, remove Workers/Pages/static asset rules for /threads-callback, /datadeletion, /api/*; proxy pass-through to origin tunnel or open 8008 via cloudflared. Purge cache after fix. Files (new, correct): src/app/threads-callback/*, src/app/api/threads-exchange/route.ts 1.2 [HIGH] Environment URL mismatch ------------------------------------------------------------------------------ .env currently has: NEXT_PUBLIC_SITE_URL=https://democratic.thetempleofdoom.com Public users use: https://bwt.democracyrisingbwt.us Impact: - Canonical URLs, Open Graph, JSON-LD, sitemap base wrong for BWT subdomain - Socket.IO CORS allowedOrigin may reject browser if users hit bwt.* but env says democratic.* - Stripe/NextAuth redirect_uri mismatches if callbacks use SITE_URL Action: Set NEXT_PUBLIC_SITE_URL, NEXTAUTH_URL, AUTH_URL to the hostname users actually use (or support multiple origins in server.ts CORS). Reference: server.ts lines 18-22 (allowedOrigin), src/lib/public-env.ts 1.3 [MEDIUM] Single-process architecture ------------------------------------------------------------------------------ server.ts holds: - pongRooms Map + setInterval game loops (20 Hz per active room) - coin-flip resolution inline on join - No Redis, no horizontal scaling If you scale to 2+ Node processes: PvP games break, rooms split-brain. Action: Document "single instance only" or migrate room state to Redis. 1.4 [LOW] systemd unit note ------------------------------------------------------------------------------ /etc/systemd/system/fundraising-platform.service uses npm run start (good). Unknown key StartLimitIntervalSec ignored on older systemd — harmless. NODE_OPTIONS=--max-old-space-size=8192 on an 7.8GB machine leaves little headroom for PostgreSQL + OS. Consider 4096 unless you see OOM during build. ================================================================================ 2. SECURITY ================================================================================ 2.1 [CRITICAL] Secrets exposure vectors ------------------------------------------------------------------------------ - Old Cloudflare-hosted threads-callback HTML embeds THREADS client_secret in JS - .env on server contains live Stripe sk_live_* (never commit; restrict file perms) - Threads credentials in .env (THREADS_CLIENT_ID, THREADS_CLIENT_SECRET) — OK if server-only; ensure .gitignore blocks .env (it does) Action: Rotate Threads app secret + Stripe keys if static page was ever public. chmod 600 /root/fundraising-platform/.env 2.2 [HIGH] No global API rate limiting ------------------------------------------------------------------------------ Unauthenticated or session-authenticated endpoints can be spammed: - POST /api/register - POST /api/games/* (each debits DB) - POST /api/faq, /api/billboard, etc. Action: nginx/cloudflare rate limits, or middleware with IP buckets. 2.3 [MEDIUM] Auth middleware disabled ------------------------------------------------------------------------------ src/middleware.ts → matcher: [] — no edge protection. All auth is per-route auth() calls. Easy to forget on new routes. Wallet page protected in page.tsx; most /api/* check session — good pattern but not enforced centrally. 2.4 [MEDIUM] FAQ pending submissions ------------------------------------------------------------------------------ src/app/api/faq/route.ts GET: - pending[] only returned when signedIn (line 29-44) — IMPROVED vs old audit - Still: any logged-in user sees ALL pending submissions (moderation leak) Action: pending visible to admin role only. 2.5 [MEDIUM] Socket.IO auth — PARTIALLY FIXED ------------------------------------------------------------------------------ server.ts resolveSocketUserId() uses getToken from cookie — GOOD. CoinFlipRoom no longer sends client userId in join_room — GOOD. Remaining: - CORS origin single string; subdomains need array - No explicit origin check on Engine.IO handshake beyond cors config 2.6 [LOW] Prediction market trust model ------------------------------------------------------------------------------ Creator resolves outcome (PATCH). No oracle — fine for demo, risky for prod. 2.7 [LOW] No email verification enforced ------------------------------------------------------------------------------ User.emailVerified in schema unused in credentials flow. ================================================================================ 3. DEAD CODE, DUPLICATION, ORPHANS ================================================================================ 3.1 [HIGH] Unused component — DonationCheckout.tsx ------------------------------------------------------------------------------ Path: src/components/DonationCheckout.tsx (~400 lines) Imports: NONE in app (only EmbeddedDonationCheckout used) - src/app/donate/page.tsx → EmbeddedDonationCheckout - src/components/DonateSection.tsx → EmbeddedDonationCheckout Action: Delete file or merge unique features into EmbeddedDonationCheckout. 3.2 [MEDIUM] Unused exports / dead helpers ------------------------------------------------------------------------------ - src/lib/auth-links.ts: LOGIN_RETURN_WALLET, LOGIN_RETURN_RAISED — no imports - src/app/api/games/dice/route.ts: imports hashServerSeed, never used - src/app/api/games/tower/route.ts: SAFE_PER_FLOOR constant was reported unused (verify before delete) - src/lib/exchange.ts: @deprecated BLW_* aliases — internal only 3.3 [MEDIUM] Duplicated game metadata ------------------------------------------------------------------------------ - src/app/casino/page.tsx — GAMES array (lobby cards) - src/app/casino/[game]/page.tsx — GAME_META record - src/components/casino/GameHistory.tsx — GAME_ICONS Any new game requires 3 edits. Extract shared catalog module. 3.4 [MEDIUM] Duplicated settlement logic ------------------------------------------------------------------------------ mines/route.ts and tower/route.ts — nearly identical cashout/win transactions. dice, slots, roulette — copy-paste instant-game transaction blocks. crash/mines/tower use creditWalletCredits + manual ledger; dice uses same; blackjack uses creditForWin/refundBet from game-ledger — INCONSISTENT. Action: Single settleGameWin() / settleGameLoss() in game-ledger.ts. 3.5 [LOW] redmeFIXSES.md partially stale ------------------------------------------------------------------------------ Section 3 claims API-only features have no UI — FALSE for: billboard, spotlight, cards, faq-board, boost (all have pages + fetch) Section 1.1 socket userId spoof — FIXED in current server.ts Section 1.2 CORS * — FIXED (restricted origin) Section 2.1 public stats — BOTH use status:"succeeded" now Keep redmeFIXSES.md or merge into this audit after triage. ================================================================================ 4. GAME-BY-GAME AUDIT (LOGIC, BUGS, EXPLOITS) ================================================================================ Shared infrastructure: - lib/provably-fair.ts — HMAC-SHA256, crash 3% edge, deriveMinePositions - lib/game-ledger.ts — debitForBet, creditForWin, refundBet - lib/wallet-safety.ts — atomic debit via updateMany gte check - prisma GameSession — @@index([userId]), @@index([userId, gameType]) ------------------------------------------------------------------------------ 4.1 CRASH (REST: POST + PATCH) — src/app/api/games/crash/route.ts ------------------------------------------------------------------------------ Client: src/components/casino/CrashGame.tsx Behavior: - POST debits wager, stores crashAt in resultData (hidden until loss/cashout) - PATCH tick: client sends currentMultiplier; server settles loss if >= crashAt - PATCH cashout: pays floor(wager * cashoutAt) Issues: [MEDIUM] cashOut() sends `cashoutAt: multiplier` (React state) while tick probe uses multiplierRef.current — possible 1-frame desync under load. Fix: always send multiplierRef.current on cashout. [MEDIUM] Tick interval every 220ms while running = ~4.5 req/s per active player. Many concurrent crash players = DB load. Consider server-side timer or WS. [LOW] Animation runs to 100x client-side even if crashAt is 2x — cosmetic only until tick returns loss. [LOW] No max wager cap server-side beyond integer >= 1. Provably fair: serverSeed returned on end — good for verification. ------------------------------------------------------------------------------ 4.2 DICE (REST: POST instant) — src/app/api/games/dice/route.ts ------------------------------------------------------------------------------ Client: src/components/casino/DiceGame.tsx Issues: [LOW] Unused import hashServerSeed (no commit hash returned to client) [LOW] No serverSeedHash at bet time — weaker commit-reveal than mines/crash Logic: roll 0-99, threshold 2-98, multiplier = 0.98/winProb — OK. Transaction: single prisma.$transaction — GOOD. ------------------------------------------------------------------------------ 4.3 MINES (REST: POST + PATCH) — src/app/api/games/mines/route.ts ------------------------------------------------------------------------------ Client: src/components/casino/MinesGame.tsx Issues: [MEDIUM] Loss path uses prisma.gameSession.update (not updateMany) — double-tile race could overwrite outcome if two PATCHes in flight. [MEDIUM] calcMultiplier() uses inverted product formula — verify house edge matches advertised 1%; not unit tested. [LOW] Cashout with 0 reveals refunds full wager (push) — intentional? [LOW] minePositions sent to client on loss/cashout — correct for transparency; during active play positions hidden — GOOD. ------------------------------------------------------------------------------ 4.4 TOWER (REST: POST + PATCH) — src/app/api/games/tower/route.ts ------------------------------------------------------------------------------ Client: src/components/casino/TowerGame.tsx Same settlement duplication as mines. Pre-generated bomb positions per floor. Issues: [MEDIUM] Same updateMany vs plain update race as mines on some paths [LOW] FLOOR_MULTIPLIERS hardcoded — not derived from provably-fair module ------------------------------------------------------------------------------ 4.5 SLOTS / ROULETTE (REST: POST instant) ------------------------------------------------------------------------------ Files: src/app/api/games/slots/route.ts, roulette/route.ts Clients: SlotsGame.tsx, RouletteGame.tsx Pattern: transaction wraps debit + credit + GameSession — GOOD. Issues: [LOW] No explicit max wager [LOW] Outcome math not documented in UI paytable linkage to server ------------------------------------------------------------------------------ 4.6 BLACKJACK (REST: POST + PATCH) — src/app/api/games/blackjack/route.ts ------------------------------------------------------------------------------ Client: src/components/casino/BlackjackGame.tsx Issues: [HIGH] Natural blackjack / push on POST (lines 91-100): creditForWin/refundBet + gameSession.update are OUTSIDE initial transaction. Failure between them → wallet credited but session still "active" or inverse. [HIGH] resolveStand() (lines 157-193): credit/refund then session update — not atomic. Partial failure → duplicate payout or lost payout. [MEDIUM] Card rank parsing: handValue uses c.slice(0,-1) — works for "10♠" but fragile if format changes. [MEDIUM] Double down debits second wager but doesn't link ledger memo to session. Action: Wrap each resolution path in prisma.$transaction like dice route. ------------------------------------------------------------------------------ 4.7 COIN FLIP (REST rooms + Socket.IO) — rooms/route.ts + server.ts ------------------------------------------------------------------------------ Client: src/components/casino/CoinFlipRoom.tsx Flow: - POST /api/games/rooms creates WAITING room (balance check only, no debit) - Creator connects socket, waits - Joiner connects → atomic claim → debit both → instant resolve Issues: [HIGH] Payout failure (server.ts lines 129-132): logs error, still emits "result" to clients; room may stay ACTIVE — needs reconciliation job. [MEDIUM] Creator never debited until joiner arrives — creator can spam WAITING rooms (DB clutter). Expire job runs every 60s — OK. [LOW] Instant resolution — no rematch within room [LOW] GameHistory does not show coin flip (no GameSession row) Socket auth: server-derived userId — GOOD. ------------------------------------------------------------------------------ 4.8 PONG (REST rooms + Socket.IO + setInterval) — server.ts ------------------------------------------------------------------------------ Client: src/components/casino/PongGame.tsx Issues: [HIGH] Game loop setInterval(50ms) per room in Node process — CPU scales with active pong rooms. On 4-core VPS, cap concurrent rooms. [HIGH] pongRooms Map: if process crashes mid-game, debits already taken, room may stay ACTIVE, interval orphaned until process exit. [MEDIUM] paddle_move has no rate limit — flood events [MEDIUM] Client/server paddle collision logic duplicated — desync possible [LOW] No GameSession history entry Payout path (lines 244-255): improved with async IIFE + logging — still emits game_over even if payout fails. ------------------------------------------------------------------------------ 4.9 PREDICTION MARKET — src/app/api/games/prediction/route.ts ------------------------------------------------------------------------------ Client: src/components/casino/PredictionMarket.tsx POST bet: NOW uses single $transaction (debit + ledger + bet + market increment) — GOOD. PATCH resolve: transaction with resolvedTo null guard — GOOD. - Pays winners via tx.wallet.upsert — bypasses creditWalletCredits integer guard if payout is 0 skipped; dust handling to first winner — clever Issues: [MEDIUM] GET markets: no auth required — OK for public markets [LOW] No GameSession — not in GameHistory [LOW] Creator can resolve early? — throws MARKET_NOT_ENDED if endsAt > now — GOOD ------------------------------------------------------------------------------ 4.10 GAME HISTORY — src/app/api/games/history/route.ts + GameHistory.tsx ------------------------------------------------------------------------------ Issues: [MEDIUM] Client: fetch without res.ok check — silent failure on 401 [MEDIUM] Only GameSession rows — PvP and prediction invisible [LOW] No pagination UI (API may support cursor — verify route) ================================================================================ 5. API ROUTES — COMPLETE INVENTORY & GAPS ================================================================================ 37 HTTP route handlers under src/app/api/: Auth: /api/auth/[...nextauth] Register: /api/register Public: /api/public/stats, /api/leaderboard, /api/exchange/rate Wallet: /api/wallet, summary, ledger, donations, community Stripe: create-checkout-session, create-payment-intent, webhooks/stripe Missions: /api/missions, /api/missions/spend Initiatives: /api/initiatives, [slug]/pledge Poll: /api/polls/next-president Rewards: catalog, redeem, raffle Community: /api/billboard, /api/spotlight, /api/cards, /api/faq, /api/boost Games: crash, dice, mines, tower, slots, blackjack, roulette, rooms, prediction, history Threads: /api/threads-exchange Validation: [MEDIUM] Game routes use manual req.json() casts — no Zod. Malformed body → 500. [GOOD] FAQ, boost, cards, many others use Zod schemas. Missing patterns: - No If-None-Match / caching on public stats (force-dynamic everywhere) - No request ID in logs - Inconsistent error JSON shape { error } vs { issues } ------------------------------------------------------------------------------ 5.1 Feature pages vs API (wiring status) ------------------------------------------------------------------------------ WIRED (page fetches API): /billboard → /api/billboard /spotlight → /api/spotlight /cards → /api/cards /faq-board → /api/faq /boost → /api/boost /casino/* → game APIs /missions → /api/missions /initiatives → /api/initiatives /vote/* → /api/polls/next-president /wallet → wallet APIs /donate → Stripe checkout HOMEPAGE mismatch: [MEDIUM] ProgressSection → /api/public/stats (USD Stripe goal meter) Boost page → /api/boost (BWT community epoch meter) Marketing copy may conflate two different "meters" — clarify UX. ================================================================================ 6. DATABASE / PRISMA ================================================================================ Schema: prisma/schema.prisma (515 lines, 8 migrations) Indexes present: LedgerEntry userId GameSession userId, [userId, gameType] GameRoom status PollVote, Faq*, MovementBoost, etc. Missing indexes (performance at scale): [MEDIUM] GameSession.createdAt — history orders by time, filter by user [MEDIUM] GameRoom [status, gameType, expiresAt] composite for lobby listing [MEDIUM] Donation [status, createdAt] for leaderboard aggregations [LOW] LedgerEntry [userId, createdAt] for wallet ledger pagination Missing FKs: [MEDIUM] LedgerEntry has no gameSessionId / gameRoomId — hard to audit bets Enums: GameType, RoomStatus, LedgerType — comprehensive ------------------------------------------------------------------------------ 6.1 Data integrity notes ------------------------------------------------------------------------------ [GOOD] Wallet debit uses conditional updateMany (balance >= amount) [GOOD] Boost uses Serializable isolation for epoch milestone [GOOD] public/stats and treasury both filter donation status succeeded [MEDIUM] Movement boost milestone race under concurrent POSTs (documented in redmeFIXSES) ================================================================================ 7. FRONTEND / UX / MALFORMED PATTERNS ================================================================================ 7.1 Global layout performance killers ------------------------------------------------------------------------------ src/app/layout.tsx loads on EVERY page: - CursorStreamers — full-screen canvas, mousemove listener, RAF loop - SiteNav - framer-motion on Hero and many sections src/components/ParticleField.tsx — second full-section canvas on homepage Hero src/components/SmokeWisps.tsx — additional animation layer Impact on 8GB Linux VPS: - Constant GPU/CPU use even on /datadeletion legal pages - Battery drain for mobile users - Competes with Node build (npm run build spikes memory) Actions: - Load CursorStreamers only on homepage or casino - Respect prefers-reduced-motion (CursorStreamers already skips reduced motion) - Lazy-load framer-motion below fold 7.2 Polling / duplicate fetches ------------------------------------------------------------------------------ Endpoints polled from multiple components without shared SWR/React Query: /api/exchange/rate — 9+ separate useEffects across site /api/wallet — ExchangePanel every 15s on casino pages /api/public/stats — ProgressSection, HeroLiveStats Action: Single client provider for rate + wallet; stale-while-revalidate. 7.3 Casino page architecture ------------------------------------------------------------------------------ src/app/casino/[game]/page.tsx: - Server component fetches wallet once — GOOD - ExchangePanel re-fetches wallet every 15s — balance can drift from server - Game components receive static `balance` prop — not updated after wins/losses unless user refreshes or ExchangePanel poll updates (partial fix) [MEDIUM] User sees stale balance in wager inputs after big win until poll. 7.4 Error handling gaps ------------------------------------------------------------------------------ - GameHistory: no res.ok check - Many fetch().then without .catch — silent network errors - CrashGame tick: empty catch {} swallows errors — intentional comment 7.5 Accessibility ------------------------------------------------------------------------------ - Canvas decorations aria-hidden — GOOD - Some icon-only buttons may lack aria-labels in casino grids - Focus management on game phase transitions not tested 7.6 SEO / metadata ------------------------------------------------------------------------------ [MEDIUM] layout.tsx alternates.canonical hardcoded to "/" — subpages should override (some do, e.g. privacy-policy) [MEDIUM] sitemap.ts missing /casino, /missions, /boost, /billboard, etc. [LOW] opengraph-image force-dynamic — regenerates every request ================================================================================ 8. STRIPE / PAYMENTS / WALLET ================================================================================ - EmbeddedDonationCheckout → create-checkout-session (primary path) - DonationCheckout → create-payment-intent (UNUSED) - Webhook: src/app/api/webhooks/stripe/route.ts — signature verify — GOOD Issues: [HIGH] Ensure webhook endpoint not cached by Cloudflare (POST body must reach origin) [MEDIUM] CREDIT_RATIO_CENTS_PER_USD — verify webhook grants match UI promises [LOW] Guest checkout path — document in FAQ ================================================================================ 9. AUTH ================================================================================ - next-auth v5 beta with Credentials provider - Email OR username login — GOOD (lib/account-identifiers.ts) - bcrypt password hashing — GOOD - JWT session with role — GOOD - No OAuth providers configured Issues: [MEDIUM] No password reset flow (forgot-password page is static) [MEDIUM] No brute-force lockout on /api/auth/callback/credentials [LOW] ADMIN role only via DB seed ================================================================================ 10. TESTING & OBSERVABILITY ================================================================================ Scripts: scripts/smoke-integration.ts — Prisma/DB scripts/http-smoke.sh — curl stats + wallet 401 scripts/full-site-test.ts — extensive HTTP checks (manual, server must run) scripts/seed-leaderboard.ts Gaps: [HIGH] No automated tests for game math or wallet settlement [HIGH] No CI pipeline evident [MEDIUM] console.error only — no structured logs, no correlation IDs [MEDIUM] No health check endpoint (/api/health) for monitoring [LOW] eslint installed but npm run lint = tsc only ================================================================================ 11. LINUX / NODE PERFORMANCE TUNING (THIS MACHINE) ================================================================================ Current resources (audit snapshot): RAM: 7.8 GiB total, ~1.6 GiB used, 6.2 GiB available CPU: 4 cores Disk: 64G volume, 7% used node_modules: 561M .next: 1.1G Recommendations: A. Process manager - Keep single fundraising-platform.service instance - Add MemoryMax=5G in systemd unit to avoid OOM killing Postgres - Lower NODE_OPTIONS to --max-old-space-size=4096 for steady-state B. PostgreSQL (localhost:5432) - Ensure shared_buffers ~256MB, effective_cache_size ~2GB on this VM - Enable log_min_duration_statement = 500 for slow query discovery - Run EXPLAIN on leaderboard + poll aggregation queries C. Next.js production - Run `npm run build` after every deploy (already required for new routes) - Consider output: 'standalone' in next.config for smaller runtime footprint - Disable turbopack for production build if instability observed (using default) D. Reverse proxy - Prefer cloudflared tunnel → 127.0.0.1:8008 over exposing :8008 - Enable gzip/brotli at edge; Next already compresses E. Static assets - next.config.ts sets immutable cache for /_next/static — GOOD - Ensure Cloudflare respects cache for hashed assets only F. Background jobs - server.ts setInterval 60s for room expiry — OK - Add cron for stuck ACTIVE GameRoom reconciliation G. Build/deploy workflow - systemctl restart after git pull + npm run build - Avoid running build while service uses CPU (schedule maintenance window) H. Monitoring - journalctl -u fundraising-platform -f - Add node_exporter or simple /api/health returning { ok, db, version } ================================================================================ 12. LEGAL / COMPLIANCE PAGES (RECENT) ================================================================================ Added: /privacy-policy — src/app/privacy-policy/page.tsx /datadeletion — src/app/datadeletion/page.tsx /threads-callback — OAuth UI (blocked on live domain by Cloudflare static file) Issues: [MEDIUM] legal-contact falls back to privacy@example.com if env unset [LOW] Template disclaimer on pages — counsel review still required [LOW] threads-callback not in sitemap (robots noindex — OK) ================================================================================ 13. PACKAGE / DEPENDENCY NOTES ================================================================================ next@16.2.6, react@19.2.6, prisma@7.8.0, next-auth@5.0.0-beta.31 socket.io@4.8.3 — matches client framer-motion@12 — large bundle; tree-shake or lazy load zod@4.4.3 — v4 API; ensure team knows breaking changes vs zod 3 [LOW] npm audit not run in this audit — run `npm audit` separately ================================================================================ 14. FILE-BY-FILE HOTSPOT INDEX (QUICK REFERENCE) ================================================================================ server.ts Socket.IO, pong loop, coin flip, room expiry src/middleware.ts matcher [] — auth not global src/auth.ts Credentials only src/app/api/games/blackjack/route.ts NON-ATOMIC payouts — FIX FIRST src/app/api/games/crash/route.ts tick spam, cashout state sync src/app/api/games/mines/route.ts loss race, ledger bypass src/app/api/games/prediction/route.ts resolve logic OK; trust model src/components/DonationCheckout.tsx DELETE (dead) src/components/CursorStreamers.tsx perf — scope to homepage src/components/ParticleField.tsx perf — homepage only src/components/casino/ExchangePanel.tsx 15s polling src/components/casino/CrashGame.tsx 220ms PATCH polling src/components/casino/GameHistory.tsx no error handling src/app/casino/[game]/page.tsx stale balance prop src/lib/game-ledger.ts canonical money API src/lib/provably-fair.ts all RNG prisma/schema.prisma indexes + missing FKs next.config.ts security headers, HSTS preload .env / .env.example URL + Threads vars ================================================================================ 15. SUGGESTED FIX ROADMAP (PHASED) ================================================================================ Phase 0 — Today (ops): [ ] Cloudflare: route all paths to Node origin; delete static threads HTML [ ] Fix NEXT_PUBLIC_SITE_URL for bwt.democracyrisingbwt.us [ ] Rotate Threads secret if static page was public [ ] systemctl restart after build Phase 1 — Money safety (1-2 days dev): [ ] Blackjack transactions [ ] Coin-flip/pong stuck room reconciler [ ] LedgerEntry.gameSessionId optional FK + backfill Phase 2 — Games UX (2-3 days): [ ] Unify game catalog [ ] Fix CrashGame cashout ref [ ] GameHistory error states + PvP rows (or document omission) [ ] Balance sync via context after each game Phase 3 — Performance (1-2 days): [ ] Remove/limit CursorStreamers + ParticleField scope [ ] Shared exchange rate provider [ ] Add DB indexes listed above [ ] Lower Node heap to 4GB Phase 4 — Hardening (ongoing): [ ] Rate limits [ ] Health endpoint [ ] Integration tests for dice/mines settlement [ ] Delete DonationCheckout.tsx ================================================================================ 16. AUDIT METHODOLOGY ================================================================================ Performed: - Full tree listing src/, prisma/, scripts/ - tsc --noEmit (pass) - npm run build route list (pass) - Manual read of all game API routes + server.ts + key components - Grep for TODO, console.*, fetch patterns, dead imports - Live HTTP comparison Cloudflare vs 127.0.0.1:8008 - Subagent explore pass for architecture map - Cross-check against redmeFIXSES.md (partially outdated) Not performed: - npm audit / dependency CVE scan - Load testing (k6/ab) on game endpoints - Stripe live transaction test - Full line-by-line read of all 15k lines (hot paths covered) - PostgreSQL EXPLAIN ANALYZE - Accessibility audit tooling (axe) ================================================================================ END OF AUDIT — 16 sections, ~450+ line items reviewed ================================================================================