Skip to content

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.

  1. An attacker extracts your token from the APK.
  2. They use it from their own app to spam your ingest.
  3. You burn through your 50k events/month quota on noise.

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)

For every event, the server compares the payload app_id to the project’s bundle_id:

app_id receivedbundle_id projectResponse
com.acme.appcom.acme.app202 Accepted
com.attacker.appcom.acme.app403 Forbidden

The attacker can use your token all they want, their app has another Bundle ID — so it’s rejected.

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.

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,
});

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 / Symfony
Pionne::init([
'token' => env('PIONNE_TOKEN'),
'tags' => [
'deployment' => env('APP_DEPLOYMENT', 'prod'),
'region' => env('AWS_REGION', 'eu-west-3'),
],
]);
Node.js
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.

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.