New Game Service
This guide walks through scaffolding a new game service that implements CGSP v1.1 (see the CGSP v1.1 Specification).
A CGSP service is a regular HTTP + WebSocket process that:
- Exposes a small set of platform-facing internal endpoints (the control plane).
- Calls a small set of platform endpoints (config pull + ledger + wallet).
- Owns its own database.
- Starts, upgrades, and shuts down independently of every other service.
The reference implementations satisfy this contract end to end; use them as working examples. The layout below follows a Node + TypeScript stack, but the protocol is language-agnostic — only the HTTP and WebSocket calls are required.
1. Prerequisites
Section titled “1. Prerequisites”- A runtime and an HTTP/WebSocket stack (the reference implementations use Node 22+).
- Familiarity with the engine package pattern: a pure game-logic package (seeded, headless, no HTTP) wired to the network, the platform, and the database by a thin service layer.
- A persistent datastore the service owns (the reference implementations use Postgres).
2. Choose the engine type
Section titled “2. Choose the engine type”Pick a short, lowercase identifier — for example roulette or dice. In the
admin UI this becomes the service slug. The wire identity, however, is
always the service_uid (16 chars) the platform assigns at registration; a
service learns it from the first desired-state push and never hardcodes it. See
Engine identity.
The slug is also a natural basis for the database name (cloud1x2_<slug>) and
the app role (<slug>_app).
3. Scaffold the service directory
Section titled “3. Scaffold the service directory”A typical layout:
<service-name>/├── package.json # name, scripts (dev, start, db:migrate, build, typecheck)├── tsconfig.json├── scripts/│ └── db/│ └── migrate.ts # CGSP-compliant, self-contained migrator├── src/│ ├── server.ts # HTTP + WebSocket + CGSP endpoints│ ├── db/│ │ ├── pool.ts # service-owned datastore pool│ │ └── migrations/ # SQL files starting at 001_<name>_engine.sql│ ├── platformRuntimeProfileClient.ts # config pull│ ├── platformLedgerClient.ts # open/settle ledger│ └── platformWalletClient.ts # debit/credit/rollback└── public/ # optional static assets / the game clientThe three platform clients are small (30–60 lines each) and differ only in the auth header and the endpoints they call.
3.1 Required scripts
Section titled “3.1 Required scripts”{ "scripts": { "dev": "tsx watch src/server.ts", "start": "node src/server.ts", "db:migrate": "tsx scripts/db/migrate.ts", "typecheck": "tsc --noEmit" }}4. Define the engine package
Section titled “4. Define the engine package”Create a pure-logic package:
packages/<name>-engine/├── package.json├── tsconfig.json└── src/ ├── index.ts # public exports ├── engine.ts # createEngine() — the pure game loop └── types.ts # config shape, bet input, etc.Three design rules:
- No database access from the engine. The engine receives a
loadRuntimeConfig(profileId)callback from the hosting service. - Emit events, do not write. On each bet placement/settle, the engine emits a ledger event; the service layer forwards it to the platform.
- Per-bet detail lives in the service DB. The platform ledger row is
financial only, keyed by
service_bet_uid; it never duplicates per-bet game metadata.
5. Implement the required CGSP endpoints
Section titled “5. Implement the required CGSP endpoints”The minimum viable service hosts:
PUT /internal/engines/desired-state— reconcile to the platform’s desired state.GET /internal/engines— return in-memory engine summaries.GET /internal/engines/:engineId/status— per-engine detail.GET /health— liveness.- A WebSocket connection handler that broadcasts round events to clients.
Two more endpoints are optional but unlock v1.1 LiveOps features:
POST /internal/engines/:engineId/pauseand/resume— admin pause/resume.- the service also calls
POST /internal/engines/round-start-checkeach round for operator-suspend handling.
Full request/response shapes are in Control Plane.
6. Wire up the platform clients
Section titled “6. Wire up the platform clients”Three HTTP clients call back to the platform:
| Client | Header | Purpose |
|---|---|---|
| Runtime-profile client | x-internal-config-secret | Pull the runtime profile per-round / on reconcile |
| Ledger client | x-internal-wallet-secret | POST /internal/consolidated/tx + /settle |
| Wallet client | x-internal-wallet-secret | Operator wallet debit / credit / rollback |
See Config + Ledger and the Wallet API.
7. Database setup
Section titled “7. Database setup”-
Create the role and database (one-time, with the platform’s superuser):
CREATE ROLE <name>_app LOGIN PASSWORD '<name>_app';CREATE DATABASE cloud1x2_<name> OWNER <name>_app; -
Add
<NAME>_DATABASE_URLto the environment. -
Write the first migration at
src/db/migrations/001_<name>_engine.sql. No cross-DB foreign keys — referencegames.external_idand the runtime profile ID by integer only. -
Verify locally:
Terminal window <NAME>_DATABASE_URL=postgres://<name>_app:<name>_app@127.0.0.1:5432/cloud1x2_<name> \npm run db:migrate --prefix <service-name>
8. Compose + launcher integration
Section titled “8. Compose + launcher integration”- Add a container service block mirroring the reference implementations
(
DATABASE_URL, the fourINTERNAL_*_SECRETvalues,PLATFORM_INTERNAL_BASE_URL). - Register the service in the platform so the launcher proxy can route browser traffic to it (see Register and Launch).
- If the service serves a web client, configure the launcher upstream URL for the new service.
9. Acceptance checklist
Section titled “9. Acceptance checklist”Before marking a new service integration-complete:
-
typecheckpasses. - The service imports no platform DB modules (the isolation contract is strict).
- The container boots, runs its migrations, and reports healthy.
- Admin actions on a matching runtime profile or game propagate to the service (confirm via the desired-state log line).
- A full smoke scenario works: launch → bet → settle → admin suspend/resume → the ledger reflects amounts, status, and dates.
-
protocolVersion: "1.1"appears in every request / response payload.
10. FAQ
Section titled “10. FAQ”Can the engine read platform tables directly? No. Every piece of platform data comes through a CGSP endpoint. If a needed field is not yet exposed, request a minor-bump endpoint.
Can a different database engine be used? The protocol is HTTP-only and does not constrain storage. The reference tooling assumes Postgres; another store is possible with equivalent migration tooling.
How are per-operator / per-currency bet limits handled?
Through bet templates — GET /internal/bet-templates (v1.1).
Once the service is scaffolded, continue to Register and Launch to connect it to a running platform.