Telemetry, Device Registration, Tier-3 Support Short Code & Canary QA
Every installation of a Flutter application embedding the OTA runtime generates an anonymous unique UUID v4 (client_id) on its first launch. This mechanism powers auto-discovery of devices in the backend, Tier-3 Technical Support triage using an 8-character short code, and Canary QA device enrollment.
1. Generation & Persistence of client_id (state.json)
On first native boot, the Rust engine (UpdaterState) generates a random UUID v4 (e.g., "6959347f-ec8d-4b50-8562-a9562443ac3b") and writes it to a local JSON file (state.json or patches_state.json):
| Platform | Paths Inspected by TkOtaPush.readClientId() |
|---|---|
| Android | /data/user/0/<package>/files/shorebird_updater/state.json (probed via Directory.systemTemp.parent + /files) |
| iOS | <Sandbox>/Library/Application Support/shorebird_updater/state.json and fallback <Sandbox>/Documents/shorebird_updater/state.json |
| Custom / Test | Any path passed through optional appSupportDirectoryPath |
Example structure of shorebird_updater/state.json:
{
"client_id": "6959347f-ec8d-4b50-8562-a9562443ac3b",
"release_version": "1.0.0+13",
"queued_events": []
}
2. Reading Device Identity & Tier-3 Support Short Code
Rather than forcing users to dictate all 36 characters of a raw UUID over a support call, TkOtaPush derives an 8-character Tier-3 Support Short Code:
- Strips
-hyphens fromclient_idand converts to uppercase. - Takes the first 8 characters and prefixes with
#(e.g.,"6959347f-ec8d-4b50-8562-a9562443ac3b"becomes"#6959347F").
Using Identification Methods
final ota = TkOtaPush.instance;
// 1. Read full UUID v4
final String? clientId = await ota.readClientId();
// -> "6959347f-ec8d-4b50-8562-a9562443ac3b"
// 2. Read 8-character Support Code
final String? shortCode = await ota.readShortSupportCode();
// -> "#6959347F"
// 3. Read full OtaDeviceIdentity object
final OtaDeviceIdentity? identity = await ota.readDeviceIdentity();
if (identity != null) {
print(identity.toJson());
// {
// "client_id": "6959347f-ec8d-4b50-8562-a9562443ac3b",
// "short_support_code": "#6959347F",
// "release_version": "1.0.0+13",
// "track": "stable",
// "current_patch_number": 2
// }
}
Ready-to-Use Widget for Settings / Support Screens
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:tk_ota_push/tk_ota_push.dart';
class OtaSupportDiagnosticTile extends StatelessWidget {
const OtaSupportDiagnosticTile({super.key});
Widget build(BuildContext context) {
return FutureBuilder<OtaDeviceIdentity?>(
future: TkOtaPush.instance.readDeviceIdentity(),
builder: (context, snapshot) {
final identity = snapshot.data;
final shortCode = identity?.shortSupportCode ?? 'Not initialized';
final fullUuid = identity?.clientId ?? 'Waiting for OTA engine';
final patchLabel = identity?.currentPatchNumber != null
? 'Patch #${identity!.currentPatchNumber}'
: 'Original Store Binary';
return Card(
margin: const EdgeInsets.symmetric(vertical: 8),
child: ListTile(
leading: const Icon(Icons.phonelink_setup, color: Colors.teal),
title: Text(
'Support Code: $shortCode • $patchLabel',
style: const TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Text(
'UUID: $fullUuid',
style: const TextStyle(fontFamily: 'monospace', fontSize: 11),
),
trailing: IconButton(
icon: const Icon(Icons.copy, size: 18),
tooltip: 'Copy full UUID',
onPressed: identity == null
? null
: () {
Clipboard.setData(ClipboardData(text: identity.clientId));
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Code ${identity.shortSupportCode} (${identity.clientId}) copied!',
),
),
);
},
),
),
);
},
);
}
}
3. Automatic Device Registration & Canary QA Whitelist
When a device makes its first request to POST /api/v1/patches/check or POST /api/v1/patches/events, the server records the terminal into the fleet registry with:
- Its
client_idand#8CARshort code - Its
release_version,platform(android/ios), andarch(aarch64/arm) - Its resolved country (
X-Country-Code,CF-IPCountry,X-Geo-Country, or querycountry)
In the Device Fleet & Support dashboard (/support) of the TkushPatch Web Console, search #6959347F to:
- Assign a friendly name to the handset (e.g.,
"QA Lab — iPhone 12 Pro"). - Enroll the device in the Canary QA Whitelist (
allowed_client_ids_csv): the handset receives targeted or experimental patches before wide public rollout. - Acknowledge a Rollback Alert (
rollback_acknowledged_at).
4. Telemetry Payload & Event Lifecycle (PatchEvent)
When an event occurs, the Rust engine writes it to queued_events inside state.json (preventing event loss if offline during app boot), then forwards it to POST /api/v1/patches/events.
JSON Payload Sent to POST /api/v1/patches/events
{
"event": {
"event_id": "c4f1d820-9a1b-4e3c-8c7a-123456789abc",
"app_id": "com.saramoney.retail",
"arch": "aarch64",
"client_id": "6959347f-ec8d-4b50-8562-a9562443ac3b",
"type": "__patch_install__",
"patch_number": 2,
"platform": "android",
"release_version": "1.0.0+13",
"timestamp": 1731150000,
"failure_category": null,
"message": null
}
}
Deduplication via event_id
Every event generates a unique UUID v4 event_id upon instantiation (PatchEvent::new in updater/library/src/events.rs). If the server's 201 Created acknowledgment is lost on an unstable mobile connection and the phone retries delivery, the backend safely deduplicates using event_id.