Native Fingerprinting
Native Compatibility for OTA Updates
Native fingerprinting derives a runtime identity from your project's native inputs. Bundle Drop embeds that identity in the native binary and independently calculates it again when you upload an OTA bundle. Delivery requires the two identities to match.
This works with both Expo and bare React Native. A compatible JavaScript update can stay on the same runtime, while a detected native change creates a different identity and requires a new binary.
Native fingerprinting is currently in Beta and is opt-in. It is intended to become the default in a future release. Manual runtime versions remain the current default. Existing projects keep their configured behavior unless they explicitly opt in.
Enable Fingerprinting
After completing Installation, replace the runtimeVersion section in your existing configuration:
runtimeVersion: {
compatibilitySource: "fingerprint",
},Keep your existing server, organization, project, and channel settings. The CLI detects the project workflow automatically; no additional fingerprint options are required for the normal setup.
Create a new Release native build after enabling fingerprinting, then upload from the matching project configuration. Changing the config in an OTA cannot change an older binary's embedded runtime identity.
Normal upload commands calculate the fingerprint automatically. You do not need to run a separate fingerprint command first or pass the hash to upload. A calculation failure stops the operation instead of silently falling back to a manual runtime.
Hybrid: Fingerprints and Explicit Runtime Versions
You can combine automatic native detection with a version boundary that your team controls:
runtimeVersion: {
compatibilitySource: "fingerprint",
ios: "1.0.0",
android: "1.0.0",
},Both the native fingerprint and the platform's explicit value contribute to the final compatibility identity. A detected native change separates incompatible bundles even when 1.0.0 stays unchanged. Bumping that value deliberately starts a new compatibility line, even if the native inputs are identical.
Use the hybrid approach when you want automatic protection and an explicit release boundary. Keep the values unchanged for compatible OTA updates. Rebuild the affected platform whenever you change its explicit boundary.
In the dashboard, Runtime shows the explicit value, or Automatic when none was supplied. Fingerprint appears separately as a compact value. The complete derived identity remains authoritative for delivery.
When a New Binary Is Required
| Change | Expected behavior |
|---|---|
| Edit application JavaScript, styles, or copy | Keep the identity and deliver a compatible OTA |
| Add a pure JavaScript dependency | Keep the identity when existing native inputs remain unchanged |
| Add or remove a native module, or change a bundled native font | Calculate a new identity and build a new native binary |
| Change an explicit runtime boundary | Calculate a new identity and build the affected platform |
| Update files inside an autolinked native dependency | The package may produce a new identity, including for tooling or JavaScript changes |
Fingerprinting detects compatibility changes; it does not deliver native code over the air. An older binary continues receiving updates for its own runtime line. A new binary receives bundles matching its new identity. Both lines can share a channel.
Check and Compare Fingerprints
Inspect the current identity without building or uploading:
npx bundle-drop fingerprint --platform ios
npx bundle-drop fingerprint --platform androidFor a before-and-after comparison, optionally save a report before making a change:
npx bundle-drop fingerprint --platform ios --output fingerprint-ios.json
# Make the change, then compare its inputs:
npx bundle-drop fingerprint --platform ios --compare fingerprint-ios.json --jsonReports are diagnostic snapshots, not runtime configuration or an upload cache. They are written only when you request an output file. Upload calculates the identity independently from the current project.
Keep Builds and Uploads Aligned
Use the same native dependencies, dependency lockfile, configuration, and relevant build environment for the binary and its OTA uploads. Keep these inputs in source control and install dependencies consistently in CI/CD.
Autolinked native dependencies can contribute their whole package contents. A tooling or JavaScript change inside one of those packages may therefore require a new runtime even when its native source did not change. Custom build inputs also need deliberate compatibility control; use an explicit boundary when your release process introduces a change that the collected inputs do not represent.
App version, iOS build number, and Android version code describe the native release. They do not override a fingerprint or make incompatible bundles eligible. See Runtime Version for the distinction between compatibility and release labels.
Keep Your Update Policy
Fingerprinting controls which bundles are compatible. Your update policy still controls when downloads and activation happen, including manual API control. Managed publishing, rollout eligibility, and native recovery keep their existing roles.
Patch delivery works within the matching runtime line. Fingerprinting does not permit patches between incompatible runtimes.
Keep or Return to Manual Runtime Versions
Manual runtime control remains available:
runtimeVersion: {
compatibilitySource: "manual",
ios: "1.0.0",
android: "1.0.0",
},Omitting compatibilitySource also preserves manual behavior. Switching between manual and fingerprint compatibility requires a new native build. It does not migrate installed binaries through an OTA.
Related Docs
- Runtime Version compares manual, automatic, and hybrid compatibility.
- Installation covers initial setup and native integration.
- Expo also covers the separate Expo-owned runtime authority option.
- Uploading provides platform upload commands.
- Troubleshooting covers unexpected identity changes.
