Guides
Environments & release tracks
Every way your app reaches a device already has a name — Xcode, an install link, TestFlight, the App Store. Pharen names who each build went to and how far it has reached, maps each one to an environment for you, keeps test traffic out of your customer numbers by default, and flags the one mistake that ships silently: a test key baked into a store build.
The mapping
You never assign any of this. The environment follows the ingest key baked into each build configuration — wiring your tooling generates — and Pharen reads the distribution from the build itself at runtime.
The distribution answers one question: who was this build distributed to? debug — developers, as a debug, simulator or development-signed build; tester — your testers, whether a handful of teammates or a public TestFlight link; store — the public, through a storefront; enterprise — your organization's own people, through an in-house profile; sideload — whoever installed it directly, with no storefront behind it, such as from your website. Alongside it, the distributor says who supplied the build — app_store for the App Store, testflight, ad_hoc for a build installed under an ad-hoc provisioning profile, or other for an alternative marketplace. Both are dimensions you can group and filter by, and a third, release track, says how far the build has reached: developer, internal_testing, testing, released or unconfirmed.
| How the build reaches a device | Environment | Distribution — who it went to | Release track | In your metrics |
|---|---|---|---|---|
| Run from Xcode — you, on a simulator or a cable | development | debug | developer | Kept out of customer numbers automatically |
| Install link — teammates and test devices, many builds a day | staging | tester, supplied by ad_hoc, for an ad-hoc build; tester with no supplier when its provisioning profile cannot be read; debug for a development-signed one | internal_testing for the ad-hoc build, testing when the profile cannot be read, developer for a development-signed one | Never mixed into live; visible to your team in staging |
| TestFlight — your beta testers, release candidates | live | tester, supplied by testflight | testing | In live, but excluded from customer-facing numbers by default — one filter away when you want testers included |
| App Store — your customers | live | store, supplied by app_store | released | Your customer-facing numbers; the default everywhere |
TestFlight and the App Store ship the same Release artifact, so they share the same live key on purpose: a TestFlight crash is a live-code crash you get to see before the public does. What separates testers from customers in your metrics is the release track, not the environment — customer-facing metrics default to release_track=released, so TestFlight sessions never inflate them, and a single group-by on release_track shows testers when you want them. The server works the track out when you ask, from the distribution, the supplier and the release you registered; it is never stored on an event and the SDK never claims it. A build you declare with pharen releases create --release-track released reads as released, and so do App Store installs, which Apple's own evidence places there.
Two witnesses, one guard
The environment and the distribution come from different places, deliberately:
- The key asserts the environment. It's compiled into the build and resolved on the server — a build cannot claim its way into your live data at runtime, and a leaked test key can be revoked without touching live.
- The build confesses its distribution. At runtime the SDK reads how the binary was actually distributed — a debug build, an ad-hoc profile, a TestFlight install, the App Store — and reports it with every event.
Because the two are independent, Pharen can check them against each other. That's the guard:
Honest edges
- Web has no install distribution. A browser build carries no receipt and no profile, so your key's environment is the whole story there.
- Internal vs. external TestFlight isn't knowable on-device. Both install identical builds, so Pharen reports a
testerbuild supplied bytestflight— never which tester group. Nothing on this page promises that split, because no app can detect it. - A distribution can be unknown, and a track can be unconfirmed. When a build carries no distribution signal at all, Pharen says so rather than guessing — an unknown distribution never trips the guard and never masquerades as customers. A build nothing places on a track reads
unconfirmed: a released count of 0 beside a large unconfirmed count means “cannot tell”, never “no customers”, and declaring the build released moves its rows.