Sandbox self-test
operator-conformance is a command-line tool that calls your wallet endpoints exactly the way the platform does and checks every rule of the protocol: idempotency, rollbacks and tombstones, staged debits, batch semantics and signature rejection. Passing it is a prerequisite for production review.
Get the tool
Section titled “Get the tool”The tool is written in Go. Request it from your Star Game contact (contact@sentosatower.com): we send a prebuilt binary for Linux, macOS or Windows, or the Go source. To build from source (Go 1.26 or later):
go build -o operator-conformance ./cmd/operator-conformanceRun it
Section titled “Run it”-
Create a test player in your sandbox wallet with a balance of at least 1000 minor units.
-
Issue two fresh launch codes for that player.
-
Run the tool with your sandbox wallet base URL and the sandbox signing secret:
Terminal window OPERATOR_WALLET_URL=https://api.example.com/game-wallet/v1 \OPERATOR_WALLET_SECRET=whsec_… \GAME_ID=lantern \./operator-conformance <launch_code_1> <launch_code_2>GAME_IDis optional (defaultconformance). The run takes a few seconds and has a 2-minute limit.
When it finishes, the test player’s balance is back where it started: every debit is either rolled back or offset by an equal credit. Transaction ids are prefixed conf-<random>: so you can find them in your logs.
Exit codes: 0 all checks passed (WARN lines are advisory), 1 at least one check failed, 2 the run could not start (bad configuration, authentication failed, balance below 1000).
What the 19 checks cover
Section titled “What the 19 checks cover”| # | Check | Rule |
|---|---|---|
| 1 | authenticate |
Launch code returns player_id, session_id, currency |
| 2 | launch code is single-use (SHOULD) |
Reusing the first code is refused — advisory (WARN, not a failure) |
| 3 | balance sufficient for the suite |
Test player has ≥ 1000 |
| 4 | debit books and returns balance |
Debit of 100 → ok, balance reduced |
| 5 | replayed debit returns first result without booking again |
Idempotency |
| 6 | same id with different amount is duplicate_mismatch |
Content mismatch detection |
| 7 | debit above balance is insufficient_funds |
Refusal |
| 8 | replayed failed debit returns the first result |
Failures are idempotent too |
| 9 | credit referencing a booked debit books |
Payout |
| 10 | several debits in one round all book; payout references the first |
Staged debits |
| 11 | rollback refunds the debit |
Rollback |
| 12 | second rollback of the same debit does not refund twice |
Rollback idempotency |
| 13 | credit referencing a rolled-back debit is reference_not_found |
No payout for reversed stakes |
| 14 | rollback before its debit is ok, and the late debit is rolled_back and not booked |
Tombstones |
| 15 | batch returns one result per item in order; one failure does not fail the batch |
Batch semantics (21 items) |
| 16 | batch rollback restores the balance |
Batch of 20 rollbacks |
| 17 | second launch code authenticates the same player |
New session for the same player |
| 18 | unsigned, wrong-key, tampered-body and stale requests are rejected with 401 |
Signature verification |
| 19 | net balance unchanged after the suite |
Nothing leaked |
Example output
Section titled “Example output”Output from a run against our reference operator:
PASS authenticate (player=p1 session=true currency=GOLD err=<nil>)PASS launch code is single-use (SHOULD) (second use err=wallet: invalid launch code)PASS balance sufficient for the suite (balance=100000 need>=1000)PASS debit books and returns balance (status=ok balance=99900 want=99900)PASS replayed debit returns first result without booking again (status=ok balance=99900)PASS same id with different amount is duplicate_mismatch (status=duplicate_mismatch)PASS debit above balance is insufficient_funds (status=insufficient_funds)PASS replayed failed debit returns the first result (status=insufficient_funds)PASS credit referencing a booked debit books (status=ok balance=100000 want=100000)PASS several debits in one round all book; payout references the first (debits=ok,ok credit=ok balance=100000 want=100000)PASS rollback refunds the debit (debit=ok rollback=ok balance=100000 want=100000)PASS second rollback of the same debit does not refund twice (status=ok balance=100000)PASS credit referencing a rolled-back debit is reference_not_found (status=reference_not_found)PASS rollback before its debit is ok, and the late debit is rolled_back and not booked (rollback=ok late debit=rolled_back balance=100000 want=100000 err=<nil>)PASS batch returns one result per item in order; one failure does not fail the batch (ok=20/20 last=insufficient_funds)PASS batch rollback restores the balance (all ok=true balance=100000 want=100000)PASS second launch code authenticates the same player (player=p1 err=<nil>)PASS unsigned, wrong-key, tampered-body and stale requests are rejected with 401 (unsigned=401 wrong key=401 tampered=401 stale=401)PASS net balance unchanged after the suite (balance=100000 start=100000)19/19 passedAfter the tool passes
Section titled “After the tool passes”Open a few games from your App against the sandbox entry URL, play real rounds (stake, win, lose, top up mid-round), and compare your wallet ledger with Console → Data (Sandbox tab). Then click Apply for production on Console → Overview.