Skip to main content

Telemetry API

For whoever maintains the service. If you're putting telemetry in a game, you want Adding it to a game instead — you don't need any of this.

Base URL: https://telemetry.ardenttitanite.com Auth: X-Telemetry-Key header.

Two keys, and they must differ:

KeyForCan
INGEST_KEYThe gamesWrite segments, register games
READ_KEYConsumersRead only

A leaked read key can't forge activity. That's the point of splitting them.

POST /v1/segments

{
"eventId": "a-guid-generated-once-per-batch",
"gameKey": "genesius",
"placeId": "137231719757604",
"jobId": "server-job-id",
"serverTime": 1785735243003,
"segments": [
{
"robloxId": "1234567890",
"username": "Someone",
"team": "Blue",
"startedAt": 1785734043003,
"endedAt": 1785735243003,
"reason": "leave"
}
]
}
ResponseMeans
201 {duplicate: false}Stored
200 {duplicate: true}Already had this eventId. Nothing counted twice.
400Malformed. Retrying won't help.
403Game isn't registered, or is disabled.
401Wrong key.

Reuse eventId when retrying. That's the whole idempotency story — the sender never has to know whether its first attempt landed.

robloxId is a string. A Roblox ID above 2^53 rounds as a JSON number, and a rounded ID writes one player's session onto someone else's account.

POST /v1/games

Register a game. Holding the ingest key isn't enough to report — a game has to be registered too, so one game can be switched off without rotating everyone's key.

{ "gameKey": "genesius", "label": "GENESIUS Territory", "placeId": "137231719757604" }

POST /v1/games/:gameKey/disable

Stops that game reporting. Its existing records are untouched.

GET /v1/playtime

?gameKey=genesius&since=<ms>&until=<ms> — defaults to the last 7 days.

Computed from the raw rows on every request, excluded segments left out. Deliberately not a running total: a total that drifts can't be checked, whereas this can be recomputed and compared against the envelopes it came from.

GET /v1/stats

Envelope, segment, excluded and game counts. Good for a health check.

When a segment gets flagged

ReasonRule
ends before it startsendedAt earlier than startedAt
longer than the ceilingover MAX_SEGMENT_MS, default 12 hours
starts in the futurebeyond MAX_CLOCK_SKEW_MS, default 6 hours ahead
non-numeric timestampsnot a number

Flagged segments are stored, excluded from totals, and never deleted.

Running it

cd ~/titan-telemetry
git pull && npm ci && npm run build
sudo systemctl restart titan-telemetry
sudo journalctl -u titan-telemetry -f

Port 8794 on 127.0.0.1, published by the cloudflared-titan-telemetry tunnel. Database at ~/titan-telemetry/data/telemetry.sqlite.