Tutorial 164: Connecting a game to the CNA Gamer Services server

CNA Tutorials  ·  CNA snapshot b0e97bb1

ℹ

What you’ll learn: how to start a development server, register a title and an account, point a game at the server without changing its code, meet the HTTPS rules and sign in through the Guide.

ℹ

Status: the CNA Gamer Services server is a CNA service, not Xbox LIVE, and no public instance is operated: you run your own. The server commands below are quoted from its README (commit 9bd5173) and the client rules from CNA’s source at snapshot b0e97bb1; none of them was run for this page. Read Tutorial 162 first; running a server for other people is Tutorial 168.

What changes, and what does not

A CNA game does not choose between offline and online in its code. The XNA calls from Tutorials 162 and 163 stay exactly as they are; what changes is the deployment configuration the game finds when it starts. With an endpoint configured:

  • players sign in to server accounts through the Guide, and getIsSignedInToLiveProperty() is true; local offline profiles are not offered;
  • friends, rich presence, messages, gamer pictures, provisioned achievements and leaderboards, avatars stored per account, and PlayerMatch/Ranked sessions become available;
  • asynchronous results from the server complete on the game thread during GamerServicesDispatcher::Update, which the component runs from Game::Update.

The server provides CNA’s own protocol, accounts and relay. Nothing reads or produces Xbox LIVE data or credentials.

1. Start a development server

The server is a separate repository (MIT, C++23; it needs OpenSSL 3, Boost with Beast, SQLite and nlohmann/json from the system). For a first test on one machine, run it in its development mode, which serves plain HTTP on the loopback address only:

# in a clone of https://github.com/libcna/cna-gamer-services-server
cmake -S . -B build -G Ninja && cmake --build build --parallel
build/cna-gamer-services-server --database service.sqlite3 --insecure-loopback   # http://127.0.0.1:47831

Without --listen and --port the server listens on 127.0.0.1, port 47831. The production form, with a certificate, is in Tutorial 168.

2. Register the title and an account

There is no self-registration: the operator creates titles and accounts with the admin tool, which works on the same database while the server runs. A title ID and an account name are 1 to 64 characters from A–Z a–z 0–9 . _ -; the gamertag, which games display, uses the same characters and at most 32 of them. The password (8 to 256 characters) is read as one line from standard input, so keep it out of the shell history:

build/cna-gamer-services-admin service.sqlite3 title my.game "My Game"
read -rs PASSWORD
printf '%s\n' "$PASSWORD" | build/cna-gamer-services-admin service.sqlite3 user alice Alice
unset PASSWORD

Achievements, leaderboards, avatar catalogs and privileges are provisioned the same way; see Tutorial 168.

3. Point the game at the server

The quickest way is the environment of the process that starts the game:

export CNA_GAMER_SERVICES_ENDPOINT=http://127.0.0.1:47831/cna/v1
export CNA_GAMER_SERVICES_INSECURE_LOOPBACK=1   # development only: plain HTTP on a numeric loopback address
export CNA_GAME_ID=my.game
./MyGame

A shipped game more often carries a title manifest, cna-title.json, in the directory it is started from (or wherever CNA_GAMER_SERVICES_MANIFEST points; a named manifest that is missing is an error):

{
  "endpoint": "https://games.example.org:47831/cna/v1",
  "gameId": "my.game",
  "titleVersion": "1.0.0"
}

Players can also keep user settings in $XDG_CONFIG_HOME/cna/gamer-services.json (or ~/.config/cna/gamer-services.json). All three sources use the same keys:

JSON keyEnvironment variableMeaning
endpointCNA_GAMER_SERVICES_ENDPOINTFull URL ending in /cna/v1; empty or absent means offline
gameIdCNA_GAME_IDThe title ID the operator registered; keep it stable
caBundleCNA_GAMER_SERVICES_CA_BUNDLEPath of an extra trust bundle, for a private certificate authority
insecureLoopbackCNA_GAMER_SERVICES_INSECURE_LOOPBACK (0/1)Allows plain HTTP to 127.0.0.1 or [::1] only
titleVersionCNA_GAME_VERSIONThis build’s version, one to four dot-separated numbers
avatarCatalogUpdatesCNA_AVATAR_CATALOG_UPDATES (0/1)Install avatar catalogs this build lacks (default on)
maxAvatarCatalogBytes—Largest catalog pack installed (default 64 MiB)

Precedence, highest first: a programmatic override, then the environment, then the title manifest, then user settings. Below the override the sources merge key by key: a key set in the environment wins over the manifest, and the manifest over user settings. Configuration files are limited to 16 KiB, and an unknown key is refused. There is deliberately no field for a password or token: credentials are user data, never configuration.

Test and deployment hosts can set the whole configuration in code instead. The override replaces every other source and must be set before gamer services initialize:

#include "CNA/GamerServices/Configuration.hpp"

CNA::GamerServices::Configuration config;
config.endpoint = "https://games.example.org:47831/cna/v1";
config.gameId = "my.game";
config.titleVersion = "1.0.0";
CNA::GamerServices::setConfigurationOverride(config);   // validated now; std::nullopt removes it

4. HTTPS and certificates

  • The endpoint must use https. The client requires TLS 1.2 or later and verifies the certificate chain and the host name; it follows no redirects and uses no proxy.
  • Plain http is accepted only when insecureLoopback is set and the host is the numeric address 127.0.0.1 or [::1]; localhost or a LAN address is refused with SECURE_TRANSPORT_REQUIRED.
  • The server’s certificate must name the host the endpoint uses. A certificate from a private authority needs caBundle; the bundle adds trust, it never switches checks off.
  • The path must be exactly /cna/v1, with no user name, password, query or fragment in the URL.

5. Sign in through the Guide

Start the game and open sign-in, with Guide::ShowSignIn(1, false) or the Home key. With an endpoint, the picker offers Sign in with a CNA account: the Guide asks for the account name (alice), then the password, and the game sees a SignedInGamer named after the account’s gamertag (Alice). Up to four accounts can be signed in on one machine. With ShowSignIn(panes, true), a further player may type Guest to play as a guest of the first account; guests make no server calls, earn no achievements and cannot join online sessions.

CNA keeps only a refresh credential, never the password or access token, so the next launch signs the player in again without asking:

HostWhere the refresh credential is kept
Linux with a desktop keyringThe freedesktop Secret Service, loaded at run time
Linux without a keyring, macOS, other POSIX~/.local/state/cna/gamer-services/credentials (or under $XDG_STATE_HOME), owner-only files: protected by permissions, not encrypted; the macOS Keychain is not used
Windows%LOCALAPPDATA%\cna\gamer-services\credentials, sealed with DPAPI — checked under Wine only
BrowserNothing is kept (and there is no service there at all)

CNA_GAMER_SERVICES_CREDENTIALS_DIR names another directory (always plain files), =0 keeps nothing, and CNA_GAMER_SERVICES_KEYRING=0 keeps Linux on files. The dispatcher refreshes access before it expires and sends a heartbeat every 30 seconds. If the connection drops, the player stays signed in and CNA retries; if the operator revokes the account, the player is signed out at the next update and SignedOut is raised.

Title versions

A game states its version with titleVersion or CNA_GAME_VERSION. When the operator sets a minimum (title-minimum-version my.game 1.1.0), older games — and games that state no version — are refused at sign-in, where the Guide says the game must be updated, and get XNA’s GameUpdateRequiredException when a network session starts. CNA installs no title updates itself.

When something goes wrong

SymptomCause
INVALID_CONFIGURATION when gamer services startAn unknown key, a malformed game ID or version, an endpoint not ending in /cna/v1, or a flag other than 0/1
SECURE_TRANSPORT_REQUIREDPlain HTTP without the opt-in, or to a host that is not a numeric loopback address
CONFIGURATION_NOT_FOUNDCNA_GAMER_SERVICES_MANIFEST names a file that does not exist
SERVICE_TRANSPORT_UNAVAILABLE, BROWSER_SERVICE_TRANSPORT_UNAVAILABLEAn endpoint configured in a build that cannot reach a server: Android without libcurl, or a browser build
GamerServicesNotAvailableException from a callThe server could not be reached or refused the request, or the player has no account
GameUpdateRequiredExceptionThe title’s minimum version is newer than this build

The configuration codes are thrown when gamer services start (the component’s Initialize), as exceptions whose message is the code: a bad configuration stops the game rather than silently falling back to offline. Native desktop builds with networking enabled (CNA_ENABLE_NET, on by default) link libcurl with TLS — 7.85 or newer for the service, 7.86 with WebSocket support for the online relay.

Evidence and qualification

  • CNA’s final audit run of this work (2 October 2026, Linux): the GamerServices suite 649 passed and the Net suite 524 passed, with no failures; the server’s 40 registered tests: 33 passed, 7 network-isolation tests skipped for lack of a test tool, none failed. The passing server tests include an end-to-end TLS test that signs in through the Guide with the standard XNA API. These are CNA-recorded runs; this site did not re-run them.
  • Not qualified: Windows and macOS clients and server hosts, a real wide-area network with NAT, voice on physical audio devices, and avatar pixels on Direct3D and Metal. Public-Internet deployment has not been independently qualified.
  • Browser builds have no service, relay or voice.

Next

With an account signed in, Tutorial 165 uses friends, presence and gamer pictures, and Tutorial 166 plays online sessions. Tutorial 168 runs the server properly, and Tutorial 123 covers provisioned achievements and leaderboards.