Guides
Privacy & consent
Consent isn't a setting Pharen bolts on — it's stamped on every event and enforced at the point the SDK collects anything. This page is the honest account of what that means in practice.
Consent on every event
Every event Pharen sends carries an explicit consent context — the set of purposes granted, and when. It's present even when nothing is granted (an empty set is still explicit). An event is only queued if the purpose it needs is currently granted; otherwise it's dropped at the boundary, before it's ever stored or sent.
The five purposes
Purposes map to why data is processed, not to product features. Each event type resolves to exactly one:
| Purpose | Covers |
|---|---|
telemetry | Crashes, handled errors, performance. |
behavioral | Sessions, screen views, your track() events. |
identity | Associating activity with a known user (identify()). |
content | User-submitted content, such as feedback. |
health | Special-category / sensitive data. Explicit, runtime-only — and reserved in the 1.0 SDK (see below). |
Opt-in by default
Nothing is granted until you grant it. A freshly started SDK with no purposes collects nothing — it installs its handlers and then drops every event. That's a deliberate, privacy-protective default: you decide the policy for your users; Pharen provides the mechanism to honour it.
Granting consent
Grant purposes at build time for classes you process from the first launch (crash reporting under legitimate interest is the common case). List them in Info.plist:
<key>PharenConsentGrantedAtBuild</key>
<array>
<string>telemetry</string>
<string>behavioral</string>
</array>Or grant them at runtime — for example, after your consent banner returns. Grants and revocations take effect immediately:
// Replace the whole granted set (e.g. from a consent banner result):
Pharen.shared?.consent.setConsent(granted: [.telemetry, .behavioral])
// Or change one purpose at a time:
Pharen.shared?.consent.grant(.behavioral)
Pharen.shared?.consent.revoke(.behavioral)Personal data never touches the event stream
When you identify a user, you pass a pseudonymous id and, optionally, traits:
Pharen.shared?.identify("cus_482", traits: ["plan": "pro"])Events carry only the user id your app passes, which the platform replaces with its own reference before anything is stored. Traits — and any device attributes you pass to identifyDevice() — route to the identity store, a restricted store separate from events (and separate again from the special-category store the health purpose gates). They are never inlined into an event's properties, and they are never written to logs. The properties bag on track() is guarded the same way: obvious identifying keys are dropped before sending, and the server enforces the rule regardless.
Your tenant is stamped by the server, not your app
The SDK never sends your tenant_id or your environment. Both — along with your app id, when you use a per-app key — are derived on the server from the ingest key the request authenticates with. The key is public by design (it ships in your binary and carries no read access); the isolation boundary is the server-side derivation, which can't be spoofed from the client. That's also what makes the rings unspoofable: a Debug build physically cannot write to your production data.
How long data is kept
These are the windows the Service applies today. Where a row says for the life of your account, nothing deletes that data on a schedule: it stays until you instruct its deletion or your agreement ends, and then the deletion windows in the Data Processing Agreement apply. We may set or shorten a window with the notice the Terms describe for usage limits.
| Data | Kept |
|---|---|
| Events, sessions, installs, crash reports and issues, uploaded symbol files, identity traits | For the life of your account |
| Builds you distribute, their install pages, and testers' enrollment and device records | For the life of your account |
| App-store data retrieved with a store credential you connect | For the life of your account |
| Diagnostic bundles, including a report's attached pictures and captured network activity | 30 days |
| Feedback reports, and a reporter's contact address kept beside a report | 365 days |
| Session network addresses, where you enable their capture | 90 days |
| A link open's network address, user agent and referrer, where you enable their capture | 90 days, or the window you configure; the open itself stays counted |
| Push delivery records, and a push provider's refusal of a misconfigured send | 90 days |
| A push provider's report that a push address is no longer valid | For the life of your account |
| Push registrations | Until the push provider reports the address invalid, or you delete it |
| The ingestion log, a processing copy of each event | Pruned once processed and at least 7 days old |
| Events refused for carrying data the Service does not accept, kept with that data removed | 30 days |
On the device. Until the Service accepts it, the current SDK keeps what it records on the device within one budget: 30 MB and 15,000 items across the event queue, crash reports and identity updates. Past either bound, the oldest detail goes first, and the newest three crash reports are never dropped to make room. On iOS, diagnostic bundles and a reporter's contact address waiting to send keep their own limits and are deleted after 30 days. While your workspace is suspended, the SDK keeps recording within that budget, sends none of it, and sends what it kept once service is restored. When your agreement ends, or your account has no plan, the SDK stops recording and deletes what it held on the device the next time the device reaches the Service; until then, what it held stays on the device. The limits, and what happens during a limit hold, are set out in Service limits & data freshness.
Remembered for the reporter. To offer it back next time, the address a reporter last sent with a report is remembered until a report is sent with the field emptied or the app stops asking for an address. On iOS it is kept on the device, and is also forgotten when the permission that allowed it is withdrawn, when, while the app is running, a different person is identified, or when the Service stops the install, as it does when your agreement ends. On the hosted report page it is kept in a cookie in that browser, for up to 365 days.
Data that exists only on a device is lost if the app is removed or its data is cleared, with one exception on iOS: with telemetry granted when it starts, the SDK keeps one Keychain item on the device, never synchronised, which can outlive the app's deletion. It holds a random marker, a random salt and, once someone has signed in, the install they last used and a hash of their id under that salt; it is what lets a reinstall say it was installed before. It exists only while telemetry is granted: it is removed whentelemetry is withdrawn, and at every launch without it, whether the user declined or the app never granted it, until the removal takes. One exception: a reinstall that starts without telemetry leaves an earlier install's item in place for up to seven days, so an app that asks after it starts can still use it; if the user declines, or the seven days pass, the next launch removes it. It forgets who last signed in on a sign-out or when identity is withdrawn. Only builds signed by the Apple developer team that kept the item can reach it: if the app is transferred to another team, the new team's builds neither read nor remove the earlier item, which stays on the device unread, and withdrawing telemetry removes only the item kept after the move.