# Pharen > Pharen is iOS app infrastructure — crash reporting, beta distribution, product analytics, and deep links behind one small SDK with no third-party dependencies — with a scoped connection that lets your AI agent work from what your app is actually doing in production. Pharen — iOS-first infrastructure for shipping apps. A Lucubra LLC product. One line of code and two Info.plist values — that's the whole install. Consent is stamped on every event and enforced at the point of collection. PII traits never touch the event stream or logs. With one read-only key, any MCP client can ask about your app; nothing is added to the app for that. ## What Pharen does - **Crash reporting**: fatal crashes are written to disk at crash time and delivered on the next launch, duplicate-safe (deterministic report ids; the server deduplicates). Symbolicated end-to-end on device builds and grouped into issues; uploading dSYMs late retroactively re-groups issues with real function names. Handled errors via `recordError` with stack, severity, and breadcrumbs. - **Reports from a browser**: a report page Pharen hosts under the app's name — the same three kinds as the in-app form, up to five pictures re-encoded server-side so no metadata survives, an optional reply address — and a one-tag widget that opens the same page in a modal frame on the customer's own site from their own control (`PharenReport.open()`; a `pharen:report-sent` event carries the number). Each report is an issue with a number beside the crash issues. The page is embedded only from the origins its publishable key names; a brand record (colours per scheme, radius, a Google Fonts family) dresses it, held to a contrast floor. Every report records which key admitted it (the class the platform witnessed and the key's id), the platform the submitter named and the browser origin; the console filters both feeds by kind of key and by key and counts each web key's reports over seven days on the instant each arrived, so a copied key is found and rotated from its rows. Guide: https://www.pharen.ai/docs/report-widget - **In-app feedback**: a device shake opens a white-label report flow (a problem, an improvement, a question; a screenshot of the frame at the shake and pictures the person adds), riding the SDK's `content` consent purpose; an app opens the same flow from its own trigger with `presentFeedbackSheet()` — a “Send feedback” row in a settings screen — or one kind directly with `presentFeedbackSheet(category:)`, whether or not shake is on. - **Beta distribution**: upload an .ipa and get an over-the-air install link served from edge object storage — a tester taps it in Safari and the app installs. The same step registers the release and uploads every dSYM. One CLI command (`bash release/release-lane.sh`) or the MIT-licensed fastlane plugin (`fastlane add_plugin pharen`, then one `pharen_release` call). - **Sessions & analytics**: automatic sessions (15 minutes of inactivity or 4 hours of life end one; every start names the session before it; no code), `track()` for named events, `screen()` for screen views. Every event shares one strict envelope, so crashes, screens, and custom events sit in one timeline, roll up into session summaries, and filter the same way. - **Deep links**: inbound universal links on pharen.link and your custom domains, routed through one `onLink` hook. Routing fires regardless of consent — consent gates only the telemetry. The open event carries only the domain the link arrived on, plus a deferred flag; the path and query string never leave the device. Links are minted on the platform — the SDK is inbound-only. - **Identity & consent**: `identify()` connects activity to your own user id (sent as `user_id`, replaced by the platform's own reference before any event is stored; the raw id is kept only sealed, in the identity store); traits route to the identity store, a restricted store separate from events — never on the event stream, never in logs. Tenant and environment are stamped server-side from the ingest key; the client cannot spoof them. - **Installs, users & devices**: every count is in one of three nouns. An install is one copy of the app on one device, one id from its first run with the SDK until the app is deleted; `installs.unique` counts the installs that sent events in the window under the consent granted (not an install base), while `installs.churned` reads, as of now, every install that has gone quiet. On iOS a reinstall is a new install, offloading keeps the install (a return at the same build reports `binary_reinstalled_with_data` on its first launch back, the same build placed again with its data kept; a return that brings a newer build reads as an update and is not reported), and restoring a backup onto a new iPhone or iPad (or the same one after an erase) starts a new install, whose first launch reports it was restored; the SDK marks the install id to be left out of the backup. With `telemetry` granted, a Keychain marker that can survive the app's deletion lets a reinstall on the same device report `installed_before`, and the same user signing in again report `replaces_install`; a missing marker proves nothing, so neither is ever false. A user is an account the app names when someone signs in, and every user count says how many installs it rests on. Devices are not counted: Pharen reads no hardware, advertising or vendor id. Every Pharen count of who used the app (reach, new installs, retention, churn, config rollout) is named and served in installs or users. The App Store's own "active devices" is Apple's figure. The page: https://www.pharen.ai/docs/installs-users-devices - **Your agent, connected**: a read-only query key (`phq_live_…` / `phq_test_…`), scoped to the apps and the one environment it names, rate-limited and revocable, connects any MCP client to `https://mcp.pharen.ai/mcp` (Streamable HTTP; the key rides in the `Authorization: Bearer` header; one-line connect commands for Claude Code and Codex are on /docs/mcp). The host is a thin transport that adds no privilege; a missing key is refused before any tool runs. On this path Pharen runs no model of its own — the customer's agent does the reasoning. - **Store context**: connect an App Store Connect key once and the store's outside view arrives beside the SDK's inside view — ratings per territory, customer reviews as published, search and chart rank, downloads and engagement, the store's own launch and hang percentiles. Integrates store metrics into your operational picture without altering your store presence (nothing is written back). Public listing facts are public information, not Customer Data; what the customer's own credential retrieves is Customer Data under the Terms' Store Data clause. Proceeds are never served over a query key: read them in the console, or with your control credential. Apple today. - **Config visibility**: the configuration each reporting build actually enforces — the defaults it resolved at startup with the current remote document applied over them, computed once, server-side, per build. Read in the console or over a control credential; never over the query key an agent holds. A build absent from the read has not reported, and a build that has not polled since a change is still on the previous document — the read says so rather than guessing. ## What the agent connection returns, and what it never does - Returns: counts and sums grouped by version, build, platform, device model, OS, build provenance, channel (who the build was distributed to: debug, tester, store, enterprise, sideload) and distributor (who supplied it: apple, google_play, amazon, samsung, huawei, testflight, firebase_app_distribution, other) (sessions, crashes, reach, new and retained installs, adoption, thermal load, SDK health, config rollout, funnel volume and reach); crash issues grouped by cause, newest activity first, each with its title, exception, counts, when it was first and last seen, and the frames of one representative crash; the reports people filed, each as its number, status and the word it was asked as — and their words only on a key minted with the `report_text` grant or a person's own session, as a masked copy (addresses, phone numbers and bare runs of ten or more digits replaced, a link's query string removed) with a manifest of what was replaced, the original for an owner's or a developer's session on a request the issue's history records; store listing facts and what the customer's store credential retrieves; on a connection presenting the control credential, the configuration each build actually enforces and its revision history. - A user or an install, what the app said about a user, and the address a reporter left to be reached at cross only on a key minted with the grant that covers them (`users`; `identity_contact`, `identity_name`, `identity_traits` beside it; `report_contact` for a reporter's address) and a stated purpose, by an owner or by a developer whose identity read has not been withdrawn. Every such read is recorded: in the tenant's identity reads log, or on the issue's history for a reporter's address. - Never, under any grant: a raw event, a location a device shared, anything from the special-category store, anything under the `health` purpose. These are structurally absent from the metrics registry, not filtered out at request time. A trait the app supplied through identify() is not among them: it is served as supplied, under the identity grant that covers it (`identity_contact`, `identity_name` or `identity_traits`). Grouped or filtered cells below the small-cell threshold (five) are withheld for builds that reached customers, so an empty answer there means fewer than five, never zero; the customer's own development and tester builds answer exactly when the query names its build provenance on a metric that offers a provenance filter, while a metric without one can never prove it and is withheld the same way; a total narrowed by nothing is exact at any size, and a key minted with the small_cells grant reads every cell exactly. Issues carry no small-cell floor and no provenance filter. - Tools with a query key: `pharen_list_metrics` (the live catalog — the first call), `pharen_apps` (the apps the key may name, by name and id), `pharen_query`, `pharen_issues`, `pharen_issue` (one issue, with the report behind it), `pharen_user` and `pharen_user_lookup` (one user, and users found by an exact value — on a key with the `users` grant), `pharen_store_overview`, `pharen_store_reviews`, `pharen_store_series`. With a person's own session: `pharen_issue_transition` (one lifecycle verb, as that person). With the control credential: `pharen_get_effective_config`, and the two tools that read and write the remote configuration's prompt-policy sections (listed on /docs/mcp). - Two caveats: `environment` is the deployment ring, not a statement about the build (a developer's local install pointed at the production key emits environment=production); `provenance=unknown` means "cannot tell", never zero. ## The whole install (three steps) 1. Add the Swift package: `https://github.com/pharen-ai/sdk-ios.git` (Swift Package Manager; zero third-party dependencies — Foundation and Apple's system frameworks only; a compiled framework, its archive pinned by checksum in the manifest). Measured on 2026-09-15 at the package's iOS 15 floor (the framework built with Xcode 26.0.1; the app with Xcode 26.6 and, in the last sitting, 26.0.1, to the same app-side bytes): adding PharenSDK 1.0.0 to an empty app adds a 1.42 MB framework to the app bundle, before any call is written; the app's own executable grows by 4.7 KB when it starts the SDK and tracks an event. Download size after App Store thinning is not measurable locally. Per tracked event, measured on 2026-09-15 for 1.0.0 (the compiled framework) on an iPhone 17 Pro running iOS 26.6.1: 15–28 µs on the calling thread and 1–3 ms of CPU in the background to encode and durably write it — the ranges of the framework's three runs across two sittings the same day, because the phone's state moves both figures more than the code does. Each sitting carried its own control (the same code as a source package in one, the same sources built with the newer compiler in the other), within 1 µs of the framework on the calling thread and inside the run-to-run spread through the write, so the compiled framework costs nothing per call. The repository is private today; access comes with an approved application. 2. Set two Info.plist values: `PharenIngestKey` (your key — public by design, ships in the binary) and `PharenConsentGrantedAtBuild` (the consent purposes you grant at launch, e.g. `telemetry`). 3. Call `Pharen.start()` — one line, as early as possible in the app lifecycle. It installs crash handlers, starts a session, and delivers events in batches off the main thread. ## The five consent purposes Consent is opt-in; an event whose purpose is not granted is dropped at the boundary, before it is stored or sent. - `telemetry` — crashes, handled errors, performance - `behavioral` — sessions, screen views, `track()` events - `identity` — associating activity with a known user via `identify()` - `content` — user-submitted content, such as feedback - `health` — special-category / sensitive data; explicit, runtime-only (rejected in a plist, never part of a "grant all"); reserved in the 1.0 SDK — no API writes the special-category store, and a grant changes one thing about what the SDK collects, that diagnostic attachments default off (a default, not a prohibition: the app masks what must never appear in a picture, then turns attachments on with `config.diagnostics.attachments.enabled = true` in the start block or `diagnostics.attachments.enabled: true` in remote config — one spelling; `Pharen.shared?.diagnostics.attachmentAvailability` says which holds right now) Store context is not collected from anyone's device and is not gated by these purposes. Privacy posture, honestly framed: the architecture makes GDPR- and CCPA-style privacy obligations tractable — purpose-limited processing, opt-in collection, immediate grant()/revoke(), data minimisation on the event stream. Whether a particular use complies is a legal determination that stays with the customer and their counsel. ## Scope, stated plainly - iOS today, in production. An Android SDK, built on the same event contract, is on the roadmap. - The agent connection reads the app's data, and nothing about a person without a grant the customer chose by name (above: the words of a report, with identifiers replaced; a user or an install and what the app said about a user, each read recorded in the identity reads log; and the address a reporter left, each read recorded on its issue). - Deletion and export are on request, inside the windows the DPA sets. Data stays portable: versioned contracts and an export-ready event log. - The CLI is provided when an application is approved. - Deep links: full post-install attribution is roadmap (only a deferred flag exists today); acquisition-channel segmentation is roadmap. - The SDK ships as a compiled framework under Schedule A (Software License) of the Pharen Terms of Service (https://www.pharen.ai/legal/terms#schedule-a) — licensed, not sold; not open source. Its public interface, documentation, privacy manifest and symbols ship with it; source is not published. The integration glue (fastlane plugin, release-lane template) is MIT. ## How to get access Access is by application, at https://onboard.pharen.ai/start: choose a permanent tenant name, sign in with GitHub, accept the Terms, DPA and AUP on a version-pinned clickwrap. Applications are reviewed for approval, and the outcome arrives by email. On approval the applicant owns the tenant, signs in to the console at https://console.pharen.ai, and mints ingest and query keys with the CLI (`pharen setup ios`, `pharen query-keys create`). Individuals and organizations are both welcome. Larger teams: hello@pharen.ai — a DPA with SCCs and a security annex, a subprocessor register with 30 days' notice, server-stamped tenant isolation with row-level security on the sensitive tables, reader/developer/owner memberships with a recorded history, key rotation with grace windows for ingest and query keys and immediate revocation for every credential, scoped read-only agent keys, per-tenant rate limits and metering, versioned contracts and an export-ready event log, and least-privilege App Store Connect keys uploaded once over an encrypted connection and sealed at rest are all in place today. ## Legal The operative legal documents are versioned and dated under https://www.pharen.ai/legal — Terms of Service (/legal/terms), Privacy Notice (/legal/privacy), Data Processing Agreement (/legal/dpa, with SCCs incorporated by reference and the security annex), Acceptable Use Policy (/legal/aup, including the data classes that must never touch the event stream), and Subprocessor Register (/legal/subprocessors, currently Render and Cloudflare, with 30 days' advance notice before any addition). Customers accept the Terms, DPA, and AUP by version at onboarding. The platform is operated by Lucubra LLC, a Washington (USA) limited liability company; /privacy remains the architecture explainer, /legal/privacy is the legal notice. ## Documentation - Quickstart: https://www.pharen.ai/docs - Connect your agent (MCP): https://www.pharen.ai/docs/mcp - Crash reporting: https://www.pharen.ai/docs/crash-reporting - Report page & widget: https://www.pharen.ai/docs/report-widget - Distribution: https://www.pharen.ai/docs/distribution - Sessions & analytics: https://www.pharen.ai/docs/analytics - Installs, users & devices: https://www.pharen.ai/docs/installs-users-devices - Deep links: https://www.pharen.ai/docs/deep-links - Privacy & consent: https://www.pharen.ai/docs/privacy - Keys & credentials: https://www.pharen.ai/docs/keys - fastlane plugin: https://www.pharen.ai/docs/fastlane - Xcode run script: https://www.pharen.ai/docs/xcode-run-script - CLI reference: https://www.pharen.ai/docs/cli - Troubleshooting & FAQ: https://www.pharen.ai/docs/troubleshooting Site: https://www.pharen.ai · Product: https://www.pharen.ai/product · SDK: https://www.pharen.ai/sdk · Privacy: https://www.pharen.ai/privacy · Legal: https://www.pharen.ai/legal · Start: https://www.pharen.ai/get-started