Bundle ID Pinning
A pio_live_... token is shipped inside your app bundle — meaning it’s extractable by anyone who decompiles the IPA/APK. Pionne mitigates this risk with an automatic Bundle ID pinning mechanism.
The risk
Section titled “The risk”- An attacker extracts your token from the APK.
- They use it from their own app to spam your ingest.
- You burn through your 50k events/month quota on noise.
The protection
Section titled “The protection”Step 1 — first event
Section titled “Step 1 — first event”The very first event received on a project carries an app_id (the iOS Bundle ID / Android Application ID). Pionne pins this value into the project’s bundle_id field.
Project "Acme iOS" bundle_id: null → first event { app_id: "com.acme.app" } → bundle_id: "com.acme.app" (pinned)Step 2 — subsequent events
Section titled “Step 2 — subsequent events”For every event, the server compares the payload app_id to the project’s bundle_id:
app_id received | bundle_id project | Response |
|---|---|---|
com.acme.app | com.acme.app | 202 Accepted |
com.attacker.app | com.acme.app | 403 Forbidden |
The attacker can use your token all they want, their app has another Bundle ID — so it’s rejected.
Change the pinned bundle_id
Section titled “Change the pinned bundle_id”If you legitimately change Bundle ID (rebrand, app merger), go to Settings → Project → Bundle ID in the Pionne mobile app and update the value. The next event with the new Bundle ID will be accepted.
Expo Go / dev case
Section titled “Expo Go / dev case”Under Expo Go, the reported app_id is Expo’s (host.exp.Exponent or similar). If you test with Expo Go before releasing your app, set up a separate Pionne project for dev, otherwise you’ll pin on Expo’s Bundle ID.
Pionne.init({ token: __DEV__ ? process.env.EXPO_PUBLIC_PIONNE_TOKEN_DEV : process.env.EXPO_PUBLIC_PIONNE_TOKEN_PROD,});Backends without bundle_id
Section titled “Backends without bundle_id”On Web/Node/Laravel/Symfony, your token is never shipped to a client: it lives in .env (gitignored), a secrets manager (AWS Secrets, Vault, EAS env vars…), or the server process memory. Nobody can “decompile” a Docker container or your PHP-FPM to retrieve it — for that you’d already need root access on the server, and at that point the token is the least of your worries.
Result: leave the Bundle ID field empty for those projects. To distinguish deployments (prod-eu vs prod-us, staging…), use tags on the SDK side rather than a server-side pinned identifier:
// Laravel / SymfonyPionne::init([ 'token' => env('PIONNE_TOKEN'), 'tags' => [ 'deployment' => env('APP_DEPLOYMENT', 'prod'), 'region' => env('AWS_REGION', 'eu-west-3'), ],]);Pionne.init({ token: process.env.PIONNE_TOKEN, tags: { deployment: process.env.APP_DEPLOYMENT ?? 'prod' },});Tags arrive indexed on the event, so you can filter/group on them in the dashboard without blocking ingest.
In addition
Section titled “In addition”Bundle pinning is just one layer. Combine it with:
- Tokens → regenerate your token if you suspect a leak.
- Privacy → minimize what’s in the payload.
- Rate limits → server-side cap per token, token bucket SDK-side.