Concepts
These six ideas are the whole mental model behind CGSP. Read them once; refer back as needed. With them, the rest of the docs make sense.
1. Platform vs. Game Service
Section titled “1. Platform vs. Game Service”Cloud1x2 splits the world in two.
- Platform — Launcher + Wallet API + Back Office + Ledger, all on one
Postgres database (
cloud1x2_platform). Owned by the platform. A game service developer writes no code here. - Game Service — one process per game (or per family of game variants). It owns its own datastore. The game logic, the bet rows, the per-round state all belong to the service.
The boundary is HTTP-only. A game service never reads a platform table directly, and the platform never reads a service table. Every cross-side data flow goes over HTTP.
Why so strict? Three reasons:
- Independence — a service deploys, scales, fails, and recovers without coordinating with the platform database.
- Stability — the platform can refactor its internal schema freely; nothing in the service breaks, because the service depends only on the HTTP contract.
- Isolation — fifty services on one platform, none able to reach another’s data.
2. Engines, Runtime Profiles, and Skins
Section titled “2. Engines, Runtime Profiles, and Skins”Three layers, all owned by the platform and exposed to the service over the protocol.
| Layer | What it is | Where it lives |
|---|---|---|
| Engine | A running state machine producing rounds. One process can host many engines. | The game service (in memory) |
| Runtime Profile | Config describing how an engine behaves: RTP, max multiplier, bot count, round wait, plus an engine-specific opaque config_json. | Platform DB |
| Skin | Visual style + localized strings, scoped per game/operator. | Service DB (assets), platform DB (translator assignments) |
Bucketing rule: multiple games rows that share the same
runtime_profile_id share one engine instance at runtime. The platform
calls this a “bucket.” Players on different skins of the same bucket sit in the
same round — the platform decides the bucketing, the service does not
compute it.
3. Engine Identity and the Bet UID
Section titled “3. Engine Identity and the Bet UID”Every engine has a deterministic identity, so the platform and the service always converge on the same string for the same logical engine — even after a restart:
engineId = "{service_uid}:{bucketKey}"bucketKey = "p:{runtimeProfileId}" // preferred | "g:{gameExternalId}" // when no profile existsFor example wp9dvdgp4wfqmxq4:p:42.
Every bet a service mints has a unique ID with a fixed shape:
{service_uid}-{ulid} 16 1 26service_uid— 16-char Crockford Base32, assigned to the service at creation time. The platform sends it in the desired-state payload. Immutable.ulid— a standard 26-character ULID (48-bit ms timestamp + 80-bit randomness), lowercase. The service mints one per bet.-— a single separator.
Total: 43 characters, self-identifying — the prefix names the owning
service. It is the idempotency key when the ledger entry is written:
re-posting the same (engineType, serviceBetUid) pair is a no-op. The service
mints bet IDs through the shared helper and never invents its own format. See
Bet UID Format.
4. The Four Protocol Surfaces
Section titled “4. The Four Protocol Surfaces”CGSP is exactly four surfaces. Knowing which side calls which removes most of the confusion.
flowchart TB subgraph Platform[Cloud1x2 Platform] direction LR CP[Control Plane API] CL[Config + Ledger API] WAPI[Wallet API] VL[Verify-Launch API] end subgraph Service[Game Service] direction LR CPS[Control Plane Server] GP[Gameplay WS Server] end CPS -->|implements| CP Service -->|calls| CL Service -->|calls| WAPI Service -->|calls| VL Browser -->|WebSocket| GP| # | Surface | Direction | The service… |
|---|---|---|---|
| 1 | Control Plane | Platform → Service (HTTP) | implements it (desired-state, status, health, pause/resume) |
| 2 | Config + Ledger | Service → Platform (HTTP) | calls it (runtime profiles, open/settle ledger, round-start-check, bet templates) |
| 3 | Wallet | Service → Platform → Operator (HTTP) | calls it (debit, credit, rollback) |
| 4 | Gameplay | Browser → Service (WebSocket) | serves it (session, placeBet, round_*) |
Each surface is guarded by its own secret header: x-internal-control-secret,
x-internal-config-secret, x-internal-wallet-secret, and
x-internal-verify-launch-secret. The secrets are independent.
5. Real vs. Demo Mode
Section titled “5. Real vs. Demo Mode”Every player session is exactly one of two modes. The launch URL decides, and the mode never changes mid-session.
| Demo | Real | |
|---|---|---|
| Triggered by | demo=true in the URL | demo=false or omitted |
| Currency | from the runtime profile (demo_currency) | from the URL, validated against the partner wallet |
| Starting balance | demo_start_balance from the profile | real partner wallet balance |
| Partner wallet calls | skipped | required: authenticate, debit, credit, rollback |
| Bet persistence | optional (save_demo_bets flag) | always |
6. The Runtime Flow
Section titled “6. The Runtime Flow”Two round-trips matter most: the launch handshake that starts a session, and the wallet flow that moves money on each bet.
Launch handshake
Section titled “Launch handshake”sequenceDiagram participant Browser participant LN as Cloud1x2 Launcher participant GS as Game Service participant PL as Platform
Browser->>LN: GET launch URL (gameId, token, currency) LN->>GS: proxy the iframe content GS-->>Browser: game SPA + assets Browser->>GS: WebSocket connect (launch query) GS->>PL: POST /internal/verify-launch PL-->>GS: ok + session fields<br/>(currency, balance, demo, locale, limits) GS-->>Browser: emit 'session' (+ first round events)verify-launch is the single source of truth for whether a launch is allowed,
whether it is real or demo, and the starting balance, currency, bet limits, and
locale. If it fails, the service rejects the WebSocket connection.
Wallet flow (real mode)
Section titled “Wallet flow (real mode)”sequenceDiagram participant Player participant GS as Game Service participant PL as Platform participant OP as Partner Operator
Player->>GS: place bet GS->>PL: POST /internal/wallet/debit PL->>OP: partner-side debit (signed) OP-->>PL: ok + new balance PL-->>GS: ok + new balance GS->>PL: POST /internal/consolidated/tx (open ledger) GS-->>Player: bet accepted
Note over GS: round runs...
GS->>GS: settle the round (win / lose) GS->>PL: POST /internal/wallet/credit (only on win) PL->>OP: partner-side credit PL-->>GS: ok GS->>PL: POST /internal/consolidated/tx/settle (status) GS-->>Player: outcome (+ payout if won)Three rules:
- A game service never speaks to the operator directly. The platform is always the bridge — secret signing, retries, and format translation happen there.
- Demo skips the whole wallet half. The ledger is still written when
save_demo_betsis set, but partner endpoints are not called. - If something fails mid-flight (a debit succeeded but the round irrecoverably errored before settle), the service calls rollback to compensate.
That is the model. When ready, build the smallest working version in the Quickstart, then reach for the Protocol Reference while implementing each endpoint.