Skip to content

Latest commit

 

History

History
135 lines (111 loc) · 7.43 KB

File metadata and controls

135 lines (111 loc) · 7.43 KB

Privacy disclosures for SDK telemetry

What the SDK's telemetry collects, and what that means for the two app-store privacy declarations. Everything here applies only once an app calls enableTelemetry; before that call succeeds, the hosted telemetry client does not collect, buffer, persist, or upload telemetry. docs/telemetry.md is the complete data inventory this page summarises.

The React Native package ships an Apple privacy manifest (bindings/react-native/ios/PrivacyInfo.xcprivacy) in a resource bundle of the MeshSdk pod, so on iOS the SDK's own declaration travels with it.

You still have work to do, for two reasons:

  1. Apple does not reliably aggregate the manifests that libraries ship in their resource bundles, especially under static linking. Fold the entries below into your app-level PrivacyInfo.xcprivacy, and answer the matching questions in App Store Connect under App Privacy: the manifest does not fill those in for you.
  2. Google Play has no manifest equivalent. Fill in the Data safety form by hand; the table below is the starting point.

What the SDK collects is constrained by design: no personal information, no precise location, no advertising identifiers, no message content. The device sends typed counts, durations and the engine's own fixed reason tokens; the ingest derives a coarse country from the request address and then discards the address. The one exception is opt-in and yours to switch on: the stable per-install id, below.

If you set includeDeviceId

Everything else on this page describes telemetry as it behaves by default, where it collects no device identifier. One option changes that, and it is the only one that changes your store answers:

await protocol.enableTelemetry({ apiKey, appId, includeDeviceId: true });

It stamps a stable per-install identifier on every batch, so the service counts distinct devices instead of distinct sessions. The id is opaque: a hash the SDK derives from its per-install scrub secret, which the ingest hashes again with a rotating secret before storing. It identifies an install of your app on one device and nothing more: it is not a hardware id, not an advertising id, not shared with any other app, and cannot be reversed to the secret it came from.

It is still a persistent per-install identifier, which is what the two stores ask about:

Without includeDeviceId (default) With includeDeviceId
Google Play, "Device or other IDs" Not collected. The per-session UUID is random and rotates on every background. Collected. Google's definition names Firebase's installation id as an example, and the same reasoning applies. Purpose Analytics, not linked to identity, not used to track.
Apple, NSPrivacyCollectedDataTypeDeviceID Not declared, and the shipped manifest omits it. Declare it in your app-level manifest and in App Store Connect: not linked to your identity, not used to track you, purpose Analytics.

The manifest the SDK ships is unchanged either way: it declares what the SDK collects on its own, and on its own it collects no device id. Because the option is yours to set, the disclosure is yours to make. The safe default is to leave it unset and stay at session grain, which costs only precision in distinct-device counts.

Apple: PrivacyInfo.xcprivacy

The manifest the pod ships declares three collected data types, each not linked to identity, not used for tracking, purpose analytics:

  • NSPrivacyCollectedDataTypeProductInteraction (messages sent, delivered, relayed; routing decisions; session summaries)
  • NSPrivacyCollectedDataTypePerformanceData (delivery latency, transport dwell, queue depths, handshake latency)
  • NSPrivacyCollectedDataTypeOtherDiagnosticData (failure reasons, decryption failures, transport status changes, battery level and charging state, relay role, and the OS major, app version and SDK version stamped on every batch)

It also declares the required-reason APIs the SDK's iOS code already uses (UserDefaults under CA92.1, file timestamps under C617.1), and declares no tracking and no tracking domains. The ingest endpoint is not a tracking domain: nothing sent to it is used for cross-app tracking.

App Store Connect answers that correspond to the above: declare Product Interaction (Usage Data), Performance Data and Other Diagnostic Data; for each, not used to track you and not linked to your identity; purpose Analytics.

Location. The manifest declares no location data, because the SDK accesses no location API. The ingest derives a coarse country code from the request address and then discards the address. App Store Connect answers are app-scoped, not SDK-scoped; confirm with counsel whether a server-derived country counts as "coarse location" for your app.

Google Play: Data safety

Data type Collected? Shared? Purpose Notes
App activity, App interactions Yes No Analytics Message and routing events, session summaries
App info and performance, Other app performance data Yes No Analytics Latencies, dwell, queue depth, handshake timing
App info and performance, Diagnostics Yes No Analytics Failure reasons, decryption failures, transport status, battery level and charging state, relay role, OS major, app and SDK version
Device or other IDs Only with includeDeviceId No Analytics See above
Location No No The ingest derives a country from the address; declare per your counsel's reading
Personal info, Messages, Photos, Contacts, Files No No Never collected by the SDK

"Shared" is No because the data goes to Offline Protocol's ingest as your processor, not to a third party; confirm how your own agreements describe that relationship. Data is encrypted in transit (TLS through the SDK's bundled roots).

Users can request deletion through you. You can stop new collection at any time with setTelemetryEnabled(false) or disableTelemetry(), but neither deletes anything or stops network activity at once: after setTelemetryEnabled(false), records already queued continue to drain, and disableTelemetry() starts a final flush, waits up to three seconds for shutdown, and may leave an in-flight request to complete after it returns. There is no in-SDK deletion request, because the SDK holds nothing to delete beyond the pending queue. That queue is bounded (64 batches, 4 MiB) and sealed with the install's record key, and it outlives disableTelemetry(): remaining batches may upload when telemetry is enabled again under the same appId, subject to the queue limits and the six-day expiry. It is cleared by uninstalling the app, by wipePersistedState(), and by enabling telemetry under a different appId, which discards it rather than uploading another application's batches under your key. It is also cleared when the ingest reports that the application's telemetry toggle is off in the developer portal: the SDK discards the queue, and whatever it collects until the next enableTelemetry, rather than holding them for an upload once the toggle is back on.

What is never collected

Message content, attachments, file names, usernames, peer addresses, group ids, message ids, precise or coarse location from the device, advertising identifiers, hardware identifiers, contacts. The pipe's event projections have no field for any of them; a test in the SDK pins the field lists, and the golden fixture in crates/offline-protocol/tests/fixtures/ pins the bytes.