openvibers/openvibe.casino AGENTS.md
AGENTS.md
| 1 | # AGENTS.md — OpenVibe.Casino |
| 2 | |
| 3 | ## Project overview |
| 4 | |
| 5 | "Learn it, play it, never pay it." Every casino game with its insides showing: the rules, the exact |
| 6 | odds and what each bet costs, tables playable with chips worth nothing, and a simulator that runs a |
| 7 | million rounds in the browser. Node.js/Express, PostgreSQL (optional identity state), CommonJS, no |
| 8 | build step. |
| 9 | |
| 10 | - Origin `https://openvibe.casino`, port `5210`, service id `casino`, env prefix `CASINO`, unit |
| 11 | `openvibe-casino.service`, release root `/opt/openvibe.casino`. |
| 12 | - **No real money, ever.** Play chips are free (1,000 a day), live in the player's browser, and cannot |
| 13 | be bought, sold, won as anything or cashed out. No operator listings and no affiliate links until the |
| 14 | owner's legal review. The tables sit behind a self-declared 18+ gate, remind a player every 30 |
| 15 | minutes, and every page carries one calm line pointing at free help (1-800-GAMBLER, BeGambleAware, |
| 16 | Gamblers Anonymous). |
| 17 | |
| 18 | ## What it owns |
| 19 | |
| 20 | - The pure, deterministic game engines in [engine/](engine/): `blackjack.js` (6 decks, S17, DAS, split |
| 21 | to 4, split aces one card, peek, insurance, 3:2), `roulette.js` (European/American, every layout |
| 22 | bet), `baccarat.js` (punto banco, third-card tableau, 5% commission), `craps.js` (pass/don't pass, |
| 23 | 3-4-5x odds, place, field), `slots.js` (three 24-stop reels, a paytable). `engine/rng.js` is a seeded |
| 24 | mulberry32; `engine/games.js` is the catalogue every page and the API read. |
| 25 | - Every edge/odds figure on the site and in the API, computed from the engines — no edge is typed in. |
| 26 | - The pages and API: `/`, `/play`, `/play/:game`, `/learn`, `/learn/:game`, `/simulate`, `/s/:id` |
| 27 | (shared run, noindex), `/safe`, `/developers`, `GET /api/v1/*` (open, no key), `/engine/*` (the UMD |
| 28 | files). |
| 29 | - The simulator ([server/sims.js](server/sims.js) for shared runs; `public/js/sim-worker.js` in the |
| 30 | browser), the 18+ gate, contrast/`prefers-reduced-motion` behaviour and the safe-play copy. |
| 31 | - Account-data receipts and telemetry; a small PostgreSQL database for shared runs and receipts. |
| 32 | |
| 33 | Not owned: money, balances, purchases, cashouts (Billing owns money and this product must never touch |
| 34 | it), any gambling operator's listing, identity (Network). |
| 35 | |
| 36 | ## Who calls it, and whom it calls |
| 37 | |
| 38 | - Callers: people in the browser; the open API and the shared-run pages; sites linking to `/learn`; |
| 39 | OpenVibe's launcher/discovery. |
| 40 | - Depends on: **Network** for optional SSO (OAuth client `casino`, PKCE) and account export/deletion |
| 41 | events; PostgreSQL and Valkey for shared runs/limits; `openvibe-shared` for the Frame, legal, SEO and |
| 42 | `/shared/*`. It does **not** call Billing, Live, AI or any gaming provider. |
| 43 | - The API is open and keyless by design (the README says so); it serves probabilities and simulations, |
| 44 | never a player's balance. |
| 45 | |
| 46 | ## Repository map |
| 47 | |
| 48 | - **Entry:** [server/index.js](server/index.js) — `createApp()`, listen, key warm-up, graceful stop. |
| 49 | - **App:** [server/app.js](server/app.js) — helmet/CSP, `/auth`, `/internal/events`, `/shared`, |
| 50 | `/engine`, `/api/v1`, pages, machine endpoints. |
| 51 | - **Config/db:** [server/config.js](server/config.js), [server/db.js](server/db.js). |
| 52 | - **Sims and storage:** [server/sims.js](server/sims.js), [server/db.js](server/db.js). |
| 53 | - **HTTP:** [server/http/api.js](server/http/api.js), `pages.js`, `principal.js`, `discovery.js`, |
| 54 | `router.js`; views [server/views/index.js](server/views/index.js), `server/render/`. |
| 55 | - **Engines:** [engine/](engine/) — `rng.js`, `blackjack.js`, `roulette.js`, `baccarat.js`, `craps.js`, |
| 56 | `slots.js`, `games.js`; UMD so the same file runs on the server, at the table and in the worker. |
| 57 | - **Browser:** [public/js/table.js](public/js/table.js), `casino.js` (the chips), `simulate.js`, |
| 58 | `sim-worker.js`; [public/css/casino.css](public/css/casino.css). |
| 59 | - **Deploy:** [deploy/systemd/openvibe-casino.service](deploy/systemd/openvibe-casino.service), |
| 60 | [deploy/nginx/openvibe.casino.conf](deploy/nginx/openvibe.casino.conf). |
| 61 | |
| 62 | ## Commands |
| 63 | |
| 64 | ```bash |
| 65 | npm install |
| 66 | npm run dev # NODE_ENV=development node server/index.js |
| 67 | npm start # node server/index.js, http://127.0.0.1:5210 |
| 68 | npm test # test/run.js, PGlite, mock Network, fixture games |
| 69 | npm run test:pg # same on PostgreSQL (CASINO_TEST_STORE=pg) |
| 70 | npm test -- engines # filter by file name; --strict makes a skip fail |
| 71 | ov test # the harness's sharded run of what the change reaches |
| 72 | ``` |
| 73 | |
| 74 | ## Database and migrations |
| 75 | |
| 76 | One migration ([migrations/0001_initial.sql](migrations/0001_initial.sql)) for shared simulations and |
| 77 | account-data receipts; PGlite in development/tests, PostgreSQL in production through |
| 78 | `openvibe-sdk/db`/`DATABASE_URL`. Play chips are **not** in the database — they live in the player's |
| 79 | browser. Never edit an applied migration; add a new number. No table stores a real balance. |
| 80 | |
| 81 | ## Auth and the 18+ gate |
| 82 | |
| 83 | - The games and `/api/v1` are open and need no account; a signed-in person is only used for shared |
| 84 | runs and account export/deletion. Network SSO is optional (OAuth client `casino`, PKCE). |
| 85 | - The 18+ gate is self-declared and stored in the browser; it is not an age verification. The 30-minute |
| 86 | reminder and the help line are product requirements, not decoration. |
| 87 | - Service tokens, if added, need a `casino.*` capability from `openvibe-contracts`; today there is no |
| 88 | money or account operation to authorize. |
| 89 | |
| 90 | ## Frontend loading model |
| 91 | |
| 92 | Server-rendered pages plus plain browser modules: [public/js/table.js](public/js/table.js) plays, |
| 93 | [public/js/casino.js](public/js/casino.js) holds the chips, [public/js/simulate.js](public/js/simulate.js) |
| 94 | with `public/js/sim-worker.js` simulates in a Web Worker. The engines are UMD and shared by server, |
| 95 | page and worker, so a rule cannot drift between the demonstration and the simulator. No SPA, bundler |
| 96 | or transpiler; `openvibe-shared` supplies the Frame/legal/SEO and `/shared/*`. |
| 97 | |
| 98 | ## Deploy |
| 99 | |
| 100 | Unit [deploy/systemd/openvibe-casino.service](deploy/systemd/openvibe-casino.service) runs |
| 101 | `/opt/openvibe.casino/server/index.js` on `127.0.0.1:5210`, env `/etc/openvibe/casino.env`; vhost |
| 102 | [deploy/nginx/openvibe.casino.conf](deploy/nginx/openvibe.casino.conf). The README records the deploy |
| 103 | files, not a live `ovhost` command; use the host's own installer. A change is frontend + server only; |
| 104 | there is no money migration to run. |
| 105 | |
| 106 | ## Tests |
| 107 | |
| 108 | `npm test` runs each `test/*.test.js` in its own process with a temp PGlite and a mock Network. |
| 109 | `test/engines.test.js` checks **every published edge/RTP** against Wizard of Odds (basic-strategy |
| 110 | 0.40%, roulette 2.70/5.26/7.89%, baccarat 1.06/1.24/14.36%, slots 93.63%); `test/contrast.test.js` |
| 111 | pins readable colours; `test/account-data.test.js` the export/deletion; `test/agent-*.test.js` the |
| 112 | agent descriptor/twins. `ov test` runs the same. |
| 113 | |
| 114 | ## Conventions |
| 115 | |
| 116 | - CommonJS on the server, UMD in `engine/`; 4-space indent, single quotes, semicolons; factories with |
| 117 | injectable config/clock/fetch/log. |
| 118 | - Numbers that a player might rely on are **computed by the engine** and tested against a published |
| 119 | source; never hard-code an edge in a page or in `server/`. |
| 120 | - Copy is calm, short and honest: "Expiry unknown" style rules apply to odds too — an unstated number |
| 121 | is not guessed. |
| 122 | - Accessibility: contrast, focus and reduced motion are part of the product; the 18+ gate must be |
| 123 | reachable by keyboard. |
| 124 | |
| 125 | ## Pitfalls |
| 126 | |
| 127 | - **No real money and no affiliate/operator links** until the owner's legal review; adding a purchase, |
| 128 | balance, cashout or referral path is out of bounds and Billing is not a dependency. |
| 129 | - **Do not type an edge or RTP into a view.** It comes from `engine/games.js`/the engines and is |
| 130 | checked by `test/engines.test.js`; a copied figure will drift. |
| 131 | - **Play chips never leave the browser.** Do not add a server balance, leaderboard prize or transfer. |
| 132 | - **The 18+ gate, the 30-minute reminder and the help line are required** on every play page; a new |
| 133 | table that omits them is a bug. |
| 134 | - **Keep the engines deterministic from a seed** and free of browser/Node globals in their core; the |
| 135 | same file must run in the worker and on the server. |
| 136 | - **Shared runs are noindex** (`/s/:id`) and contain no personal data. |
| 137 | - **Do not use a dark pattern** (fake urgency, "hot" streaks) — the product is a teaching tool. |
| 138 | |
| 139 | ## Documentation |
| 140 | |
| 141 | - [README.md](README.md) — the engines and their edges, pages, API, data and deploy files. |
| 142 | - [STATUS.json](STATUS.json) — what works and what does not. |
| 143 | - The new-domains plan §5.14 is the product brief; `openvibe-contracts` holds any `casino.*` shapes. |
| 144 |