Skip to content

Register and Launch

This is the practical path from a locally-built service to a working demo launch on Cloud1x2. At the end, the service is reachable on a public URL, registered in the admin, configured with its secrets, and serving a demo round end to end.

sequenceDiagram
participant Dev as Developer
participant Svc as Game Service (built locally)
participant Net as Internet (tunnel/VPS)
participant CL as Cloud1x2 Admin
participant PL as Cloud1x2 Platform
Dev->>Svc: build, run locally
Dev->>Net: expose on a public URL
Dev->>CL: New Game Service
CL-->>Dev: service_uid + secrets
Dev->>Svc: put secrets in .env, restart
Dev->>CL: add Runtime Profile + Game
CL->>PL: register
PL->>Svc: desired-state push (60s heartbeat)
Svc-->>PL: 200 OK (engine up)
Dev->>Dev: open the demo launch URL in a browser

Seven steps: build → expose → register → configure secrets → add profile + game → wait for reconcile → test.

The platform reaches the service over HTTPS (desired-state push, runtime-profile fetch), so a public URL is required. Three options:

ToolCommandNotes
ngrokngrok http 3030Free-tier URLs are short-lived; a new URL on each restart
Cloudflare Tunnelcloudflared tunnel --url localhost:3030Free; a persistent URL is available
localtunnelnpx localtunnel --port 3030Quick, but not suited to production-like testing
Terminal window
ngrok http 3030
# Forwarding https://abcd-1234.ngrok-free.app -> http://localhost:3030

For longer testing, a small cloud server is more comfortable. Point a domain at it and front the service with Caddy or nginx + Let’s Encrypt:

your-game-svc.your-domain.com {
reverse_proxy localhost:3030
}

Caddy handles HTTPS automatically.

Fly.io, Railway, Render, Kubernetes, on-premise — the only requirement is that the platform can reach the service over HTTPS.

In Services → New Game Service, fill in:

FieldExampleDescription
Slugyour-game-slugLowercase identifier shown in the admin (the wire identity is always the service_uid)
Display NameHi-Lo by StudioName shown in the UI
Internal URLhttps://abcd-1234.ngrok-free.appWhere the platform reaches the service

Saving produces a service_uid (16-char Crockford Base32, sent in the desired-state payload) and a set of secrets.

On the new service row, open Secrets. Four values appear:

INTERNAL_CONTROL_SECRET = <random>
INTERNAL_WALLET_SECRET = <random>
INTERNAL_CONFIG_SECRET = <random>
INTERNAL_VERIFY_LAUNCH_SECRET = <random>

These are shown once. Copy them immediately; lost secrets must be regenerated (which invalidates the old ones).

Add to the service’s .env:

Terminal window
PORT=3030
PLATFORM_INTERNAL_BASE_URL=https://api.dev.cloud1x2.net
INTERNAL_CONTROL_SECRET=<value>
INTERNAL_WALLET_SECRET=<value>
INTERNAL_CONFIG_SECRET=<value>
INTERNAL_VERIFY_LAUNCH_SECRET=<value>
DATABASE_URL=postgres://your_game_app:your_game_app@localhost:5432/cloud1x2_your_game

Restart the service. At first there is no desired-state push yet (no profile or game exists); only GET /health and GET /internal/engines (empty) respond.

Terminal window
curl https://abcd-1234.ngrok-free.app/health
# → {"ok":true,"service":"your-game-slug","engines":{}}

In Runtime Profiles → New profile:

FieldExample
Namedefault
Engine typeresolved automatically (the service’s service_uid)
RTP target96
Round wait (seconds)5
Bot min / max0 / 0
Demo currencyUSD
Demo start balance10000
Save demo betstrue
Config JSON{ "deckSize": 13, "winMultiplier": 1.95 }

Config JSON is the engine-opaque config — the platform stores it and returns it unchanged. See Engine-Opaque Config. Saving produces a runtimeProfileId.

In Games → New game:

FieldExample
External ID9000 (any unused positive integer)
NameHi-Lo Classic
Engine typeresolved automatically (the service’s service_uid)
Runtime profiledefault
Skin keya UUID from the service’s skin catalog

Within 1–60 seconds the platform pushes a desired-state. The service log should show (example service_uid wp9dvdgp4wfqmxq4):

PUT /internal/engines/desired-state { "protocolVersion": "1.1", "serviceUid": "wp9dvdgp4wfqmxq4", ... }
[bootstrap] service_uid set: wp9dvdgp4wfqmxq4
[reconcile] +1 engine: wp9dvdgp4wfqmxq4:p:42
[engine] round_open wp9dvdgp4wfqmxq4:p:42 anchor=7

The engine is up and the first round has opened.

Use Games → Hi-Lo Classic → Demo Launch (or open https://gameplay.dev.cloud1x2.net/?gameId=9000&demo=true). In the browser:

  1. The frontend loads inside the iframe.
  2. The WebSocket connects; a session event arrives: { ok: true, demo: true, balance: 10000, currency: 'USD', betMin: 1, betMax: 1000, ... }
  3. A round opens (round_open with the anchor card).
  4. The UI places a bet (higher / lower + 10 USD).
  5. betPlaced arrives; the balance drops to 9990.
  6. The round closes (round_close with the next card).
  7. A correct guess pays winAmount=19.5; otherwise 0. The balance updates.
ErrorLikely causeFix
connect_error: Unknown gameIdWrong or deleted game IDVerify the game ID in the admin
connect_error: Service not readyEngine has not reconciled yetWait up to 60 seconds (heartbeat)
betRejected: Service not readyservice_uid unknownWait for the first desired-state after restart
502 / 504 from the launcherInternal URL unreachable (tunnel closed)Restart the tunnel; update the internal URL

Once demo works, real mode needs additional partner setup, done with the Cloud1x2 team: a test partner and operator, a wallet endpoint (or mock), the game added to the partner allowlist, and a bet template. A real-mode launch URL:

https://gameplay.dev.cloud1x2.net/?gameId=9000&demo=false&currency=USD&token=test-token-123&customer=test-cust-1&organizationId=4567&trader=cx-12
GoalPage
Add skin-scoped localizationTranslations
Manage in-game content (lists, etc.)Content Modules
Understand per-round bet limitsConfig + Ledger
MistakeCauseFix
Desired-state stops arrivingTunnel URL changed (ngrok free tier)Use Cloudflare Tunnel or a VPS (stable URL)
403 Forbidden on desired-stateWrong INTERNAL_CONTROL_SECRETRe-copy from the admin, update .env, restart
Demo works, real failsMissing partner setupGet the wallet endpoint from the Cloud1x2 team
CORS error in the browserMissing CORS header when serving the frontendAllow the gameplay domain
Round never opensThe reconcile response returned 5xxCheck the service log; fix the reconciler