Quickstart

You will launch a game, serve a wallet, and watch real signed calls cross in both directions. Everything below runs on your own machine against local copies of our services; nothing here needs production access.

1. Make a key pair

openssl genpkey -algorithm ed25519 -out operator.key        # PRIVATE: never share it
openssl pkey -in operator.key -pubout -out operator.pub     # PUBLIC: this goes to us

Use a different pair for every environment. A sandbox key must never be able to sign a production call.

2. Pick an SDK

LanguagePackageNotes
TypeScript / Node 22+typescript/node:crypto and fetch, no dependencies
Python 3.9+python/standard library only (pure-Python Ed25519)
Go 1.22+go/standard library only
Java 17+java/JDK only (java.net.http, built-in Ed25519)
Rustrust/signing, verification and request building; you bring the HTTP client
PHP 8.1+php/ext-sodium and ext-json only (both ship with PHP)
C# / .NET 8+csharp/the .NET base library only (pure Ed25519 over System.Numerics)

Each SDK passes the same known-answer vectors (vectors/signing.json) and is tested against a real operator-api (interop/run.sh), so a signature that one SDK produces is the one every other produces. Usage is in SDKs.

3. Call the operator API

Your operator id is the slug we give you at registration (for example acme, and acme-sbx for sandbox).

import { OperatorClient } from "@runecode/operator-sdk";

const rgs = new OperatorClient({
  baseUrl: process.env.RUNECODE_OPERATOR_API!,        // from your onboarding email
  operatorId: "acme",
  privateKeyPem: process.env.RUNECODE_OPERATOR_PRIVATE_KEY_PEM!,
  fullRequestSigning: true,                           // recommended: timestamped, covers method, path and query
});

const games = await rgs.games("runecode");            // what you may launch
const { url } = await rgs.launch({
  product: "runecode", publisher: "wizards", game: "royal-tiger-v2",
  user: "player-48213",                               // your stable, opaque player id
  token: crypto.randomUUID(),                         // one-time session token; comes back on every wallet call
  currency: "USD", language: "en", device: "desktop",
});
// redirect the player's browser to `url` (single use, valid about 120 seconds). Never log it.

4. Serve the wallet

The RGS calls you back on /v1/balance, /v1/debit, /v1/credit and /v1/rollback. Start from the reference wallet, which already enforces the rules that matter (signature before parsing, idempotency on every id, fencing):

cd examples/reference-wallet
RGS_PUBLIC_KEYS=rgs.pem OPERATOR_SLUG=acme PORT=8080 node wallet.ts

rgs.pem is the RGS wallet public key we give you (both the current and the standby key; accept either at all times). The full contract is in Implementing the wallet.

5. See it work, end to end

examples/reference-wallet/e2e.sh starts the real RGS (play-core) next to the reference wallet, creates a player session, plays rounds, and checks that the wallet's books equal what the RGS paid. It prints one line per step:

ok  authenticate: the RGS asked the wallet for the balance (signed /v1/balance) …
ok  7 plays, 2 wins: the RGS sent signed /v1/debit and /v1/credit calls
ok  the wallet's books agree with the RGS: 1000000000 -> 994000000 (expected 994000000) …
ok  a signature from another key is refused (401 ERR_ATE)

interop/run.sh does the same for the operator API with every SDK, including the refusal cases.

6. Next

Read Testing and going live for the onboarding flow and the go-live checklist.