Skip to 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:

Key For Can
INGEST_KEY The games Write segments, register games
READ_KEY Consumers Read 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"
    }
  ]
}
Response Means
201 {duplicate: false} Stored
200 {duplicate: true} Already had this eventId. Nothing counted twice.
400 Malformed. Retrying won't help.
403 Game isn't registered, or is disabled.
401 Wrong 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

Reason Rule
ends before it starts endedAt earlier than startedAt
longer than the ceiling over MAX_SEGMENT_MS, default 12 hours
starts in the future beyond MAX_CLOCK_SKEW_MS, default 6 hours ahead
non-numeric timestamps not 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.