Client telemetry
Every XMB app — iPhone, iPad, Apple TV, Android, and the web player — keeps a
black box: a structured stream of diagnostic events (playback lifecycles,
screen changes, connectivity probes, errors) that ships to the platform in
batches and can be read back by a server admin reconstructing what happened
after the fact. One service carries the surface, under xmb.api.v1:
| RPC | Scope | What it does |
|---|---|---|
ClientEventService.ShipClientEvents | xmb:user:write | An app ships a batch of its own events |
ClientEventService.ListClientEvents | xmb:admin | Read the stream, filtered and paged |
ClientEventService.ListClientInstalls | xmb:admin | Which devices have reported, summarised |
The event shape
There is exactly one event message, for every kind of event:
kind— a dot-namespaced string the client owns (app.launch,nav.screen,playback.attach,localpath.probe,rpc.error,js.error). The platform indexes it and interprets it never; new instrumentation is a new kind constant in the app, not a protocol change.fields— stringified detail, at most 32 pairs with 512-byte values.message— the human sentence, when kind and fields alone would not read.level— the sharedxmb.node.v1.LogLevelvocabulary.playback_session_id— set when the event belongs to one play, which is what stitches a session's negotiation, attach, stalls and teardown into a single trace.
Ordering and clocks
Each batch carries a boot_id (one process lifetime — a change between
adjacent events is the restart marker) and each event a per-boot monotonic
seq. Within a boot, seq is the true order; it survives the clock steps
and NTP corrections a device clock is prone to. The platform additionally
stamps every row with its own receipt time, which is what retention and time
filters trust — a device with a broken clock cannot write rows the reaper
never collects.
Delivery contract
Shipping is at-least-once: an app re-sends a batch that may or may not have landed, and the platform tolerates the occasional duplicate rather than demanding exactly-once from a process built to be killed without warning. When an app's in-memory buffer overflows, it counts what it lost and the platform records that count as a warning row of its own — a gap reads as a gap, never a seamless lie.
Identity comes from the credential: the batch names its install_id (the
same stable id playback sessions carry) and the server it belongs to, but the
user attribution is taken from the access token, so an app cannot file events
as somebody else. Membership of the named server is checked exactly as it is
for playback.
Retention and privacy
Events are pruned on the platform's standard retention window (30 days by
default, SCHEDULER_RETENTION_DAYS).
The stream is intentionally detailed: it includes which titles were played, which screens were opened, and error text, attributed to a user and a device. Server admins can read all of it for their server. Apps strip URLs to origin and path before recording them — media tokens and other query-string credentials never leave the device — but operators of multi-tenant servers should treat this surface as personal data and say so in their privacy documentation.