SPB Git

spb/spinza

Public
4.6 KB

# Spinza — repository guide

Spinza (www.spinza.dev) is a premium fictional social casino. Virtual credits only: no deposits, no withdrawals, no purchases, no crypto, no cash value, no prizes. The full product brief is in docs/SPEC-original.md; this file is the condensed working guide.

# Layout (pnpm workspaces + Turborepo)

Path Package Role
packages/shared @spinza/shared Economy constants (10,000 SC start, bet levels, daily rewards, XP curve), zod schemas, formatting (formatSC — never a currency sign), API DTO types.
packages/game-core @spinza/game-core Server-only game engine: CryptoRng (crypto.randomBytes, rejection sampling — Math.random is banned for outcomes), reels, ways/lines evaluation, wild/scatter/cascade/hold-respin/pick-bonus/meter/heat/mystery/quantum/dynamic-grid features, runSpin(), simulate(), certify(), validateDefinition(), defineGame(). ./client export = types only (safe for the browser).
games/ @spinza/games 20 original game definitions (src/<slug>/index.ts), registry.ts, calibration.json (payScale per slug+version, written by the simulator), certifications/<slug>.json (PASS/FAIL reports).
packages/database @spinza/database Drizzle schema (25 tables, snake_case casing), migrations in drizzle/, seed.ts (games sync, achievements, missions, levels, flags, settings), sync-games.ts.
apps/api @spinza/api Fastify on 127.0.0.1:8231. Auth (argon2id, opaque 256-bit sessions hashed, recovery codes), atomic spin (services/spin.ts), wallet ledger (services/wallet.ts), progression, rewards, leaderboards, admin (TOTP), health.
apps/simulator @spinza/simulator CLI `pnpm sim <validate
apps/web @spinza/web Next 16 (port 8230). /api/* is rewritten to the Fastify service. Game screen = src/components/game/* (PixiJS renderer, procedural symbols, WebAudio sound). Admin console under /admin.

# Non-negotiable rules

  • Outcomes are decided only in apps/api via runSpin. The browser animates result.steps; it never computes wins.
  • Every balance change goes through lockWallet → applyCredit (ledger row) → saveWallet inside one Postgres transaction. sum(credit_transactions.amount) == wallets.balance is an invariant checked in admin.
  • Spin idempotency: unique (user_id, client_round_id); duplicates (even concurrent) return the stored round with replayed: true.
  • A game is published only if games/certifications/<slug>.json is PASS for its exact version. Changing pays/weights ⇒ bump version, re-run pnpm sim calibrate <slug> then pnpm sim certify <slug>.
  • Never log passwords, hashes, recovery codes, session tokens (pino redact list in apps/api/src/app.ts).
  • No payments, no $/€ anywhere in UI copy. Credits are formatted 10,000 SC.

# Commands

bash
pnpm install
createdb spinza && pnpm db:migrate && pnpm db:seed          # local Postgres 17 + Redis required
pnpm dev:api   # 8231     pnpm dev:web   # 8230
pnpm sim validate | pnpm sim run <slug> --spins 1000000 | pnpm sim calibrate all --spins 10000000 --threads 26 | pnpm sim certify all --spins 10000000
pnpm --filter @spinza/game-core test    # engine unit tests
pnpm --filter @spinza/api test          # integration tests (needs local DB/Redis)
pnpm admin:create <username> [password] # prints TOTP secret once

# Deployment (MacLustr)

mld deploy spinza from the laptop (~/Desktop/cluster-skill/mld). Manifest with secrets: M1M32:~/dispatch/apps/spinza.json (copy: deploy/spinza.mld.json, gitignored). Node M3U96a, PM2 spinza-api (8231 loopback) + spinza-web (8230) + spinza-ngrok (www.spinza.dev). Postgres db spinza + Redis local to the node. Hooks: install → migrate + seed → next build. Backups: deploy/backup.sh (daily launchd on the node).

# Gotchas

  • drizzle-orm ≥0.44 wraps pg errors: use pgCode(e) (apps/api/src/lib/pg.ts), not e.code.
  • Way/line pays are in bet units (betDivisor: 25 for 5-reel ways, 50 for 6+ reels, lines.length for line games); scatter/bonus/coin values are × total bet. payScale from calibration.json scales all non-jackpot wins.
  • Certification tolerance is statistical: max(0.4%, 3σ/√n) capped at 1.5% — high-volatility games need ≥10M spins.
  • Turbopack: import workspace packages without .js extensions; browser code imports @spinza/game-core/client only.
  • PixiJS renderer must be created client-side only (GameClient is "use client"; Application.init is async).
Files · 0b40ac1a7181cdec915de0b36dee3e78427df0c3
Menu

Theme