Tutorial 164: Connecting a game to the CNA Gamer Services server
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/Rankedsessions become available; - asynchronous results from the server complete on the game thread during
GamerServicesDispatcher::Update, which the component runs fromGame::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 key | Environment variable | Meaning |
|---|---|---|
endpoint | CNA_GAMER_SERVICES_ENDPOINT | Full URL ending in /cna/v1; empty or absent means offline |
gameId | CNA_GAME_ID | The title ID the operator registered; keep it stable |
caBundle | CNA_GAMER_SERVICES_CA_BUNDLE | Path of an extra trust bundle, for a private certificate authority |
insecureLoopback | CNA_GAMER_SERVICES_INSECURE_LOOPBACK (0/1) | Allows plain HTTP to 127.0.0.1 or [::1] only |
titleVersion | CNA_GAME_VERSION | This build’s version, one to four dot-separated numbers |
avatarCatalogUpdates | CNA_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
httpis accepted only wheninsecureLoopbackis set and the host is the numeric address127.0.0.1or[::1];localhostor a LAN address is refused withSECURE_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:
| Host | Where the refresh credential is kept |
|---|---|
| Linux with a desktop keyring | The 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 |
| Browser | Nothing 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
| Symptom | Cause |
|---|---|
INVALID_CONFIGURATION when gamer services start | An unknown key, a malformed game ID or version, an endpoint not ending in /cna/v1, or a flag other than 0/1 |
SECURE_TRANSPORT_REQUIRED | Plain HTTP without the opt-in, or to a host that is not a numeric loopback address |
CONFIGURATION_NOT_FOUND | CNA_GAMER_SERVICES_MANIFEST names a file that does not exist |
SERVICE_TRANSPORT_UNAVAILABLE, BROWSER_SERVICE_TRANSPORT_UNAVAILABLE | An endpoint configured in a build that cannot reach a server: Android without libcurl, or a browser build |
GamerServicesNotAvailableException from a call | The server could not be reached or refused the request, or the player has no account |
GameUpdateRequiredException | The 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.