Engineering

React Native OTA Security: Signing, Integrity, and Key Rotation

How signed release metadata, artifact hashes, trusted public keys, and safe key rotation protect React Native OTA updates before activation.

13 min read

Written by Founder and maintainer of Bundle Drop

React Native over-the-air delivery changes code that an installed application may execute without another store build. That makes the update path part of the application's trust boundary. A production client should not activate an OTA artifact merely because a server returned it or because the download completed over HTTPS.

The useful security question is narrower and more testable:

Can this binary prove who authorized the release, verify the exact bytes it received, confirm that they belong to its native runtime, and refuse the update without losing its current working state?

Those are separate checks. A signature can authenticate release state without proving that the JavaScript is compatible. A hash can detect changed bytes without identifying who approved them. Runtime matching can reject an update for the wrong native binary without detecting a bug in otherwise valid application logic.

This guide explains how those boundaries fit together. It is not a general mobile-security checklist, a compliance claim, or a substitute for protecting application APIs and user data.

Trust is established before an OTA update can run

Authentication, integrity, compatibility, and health are separate checks. A production client should fail safely when any one of them is missing.

Authority

Publisher signs release state

Private signing material stays outside the app. The installed binary carries only the public trust needed for verification.

Authenticity

Client verifies origin and signature

The release description must come from an expected authority and use a key the binary already trusts.

Integrity

Client verifies the target bytes

Hashes and file metadata prove that the complete local result matches the selected artifact.

Runtime

Activate or refuse safely

Compatibility and application health still decide whether verified bytes may run and remain active.

Start with the OTA threat model

An OTA system moves information through several environments: a release machine or CI runner, a control service, artifact storage, a CDN, a network, and native client storage. A useful threat model assumes that any transport or cache can return stale, incomplete, or unexpected data even when no attacker is present.

The system should distinguish at least these failure classes:

QuestionEvidenceSafe failure
Did an expected publisher authorize this release state?A signature verified by a public key the binary already trustsReject the state or use a documented trusted fallback
Are these the exact selected bytes?Cryptographic hashes, file sizes, and a complete artifact manifestKeep the current verified bundle active
Can this native binary execute the update?Platform and runtime compatibility metadataReturn no update for this compatibility lane
Is the authenticated update healthy?Startup health, crashes, adoption, and product signalsPause expansion, roll back, or recover locally

Transport encryption still matters. TLS protects a connection from passive observation and ordinary tampering in transit. It does not by itself bind an update to an application-controlled publishing authority after the data passes through storage, cache, and service boundaries. End-to-end release verification gives the installed client evidence it can evaluate independently.

Hashes and signatures answer different questions

A cryptographic hash is a stable digest of bytes. If a bundle, asset, or manifest changes, its digest changes. That makes hashes useful for detecting corruption, addressing immutable artifacts, and checking the result of patch reconstruction.

A hash alone is not authorization. An attacker who can replace both an artifact and an unsigned hash can produce a matching pair. The client still needs a trusted statement that binds the selected release to the expected artifact identity, platform, runtime, and delivery metadata.

A digital signature supplies that authentication. The publisher signs a precise representation of release state with a private key. The application verifies it with a trusted public key. JSON Web Signature defines one common representation for signed content in RFC 7515, but the invariant is more important than the envelope: verification must cover the fields that determine what the client will execute.

The two mechanisms compose:

  1. verify that trusted release metadata selected a particular target;
  2. obtain the referenced content;
  3. verify that the complete local result matches the selected hashes and sizes;
  4. only then make the candidate available for activation.

This ordering also applies to differential delivery. A patch can reduce the bytes transferred, but the final reconstructed artifact must still match the authenticated target. Patch delivery and full-bundle fallback covers the transport decision in detail.

Keep private signing material outside the application

Public-key verification works because the two halves have different responsibilities:

  • the private key authorizes releases and belongs in a protected publisher environment such as an encrypted CI secret or managed key system;
  • the public key verifies releases and can be embedded in the application without becoming a signing capability.

Shipping a public key in the binary is expected. Shipping the private key in JavaScript, a mobile configuration file, a repository, or an artifact accessible to the application would destroy the authority boundary. Any party that extracts that private key could create release state the client accepts.

Expo's end-to-end code-signing guide follows the same model: publication signs locally with a private key, while the native build carries the certificate needed to verify an update before it is applied. The archived Microsoft CodePush client also documents signed package verification as a client trust decision rather than a property of TLS alone.

Authentication tokens are different. A project identifier, deployment key, or public API key may route an update request without authorizing arbitrary executable content. Teams should document which values are public application configuration, which credentials permit publication, and which key is the cryptographic release authority.

The native binary establishes the trust root

An OTA update cannot safely replace the rule that decides whether that same update is trusted. The public verification material and its expected authority therefore belong to native build input.

That creates an important operational constraint: an installed binary can verify only the authorities it already knows. Changing a trusted public key, signature algorithm, or verification policy may require a new store binary even when the application code being delivered remains JavaScript.

This is closely related to runtime compatibility. Both trust and native capability originate in the installed build, but they answer different questions:

  • the trust root identifies which publisher statements the binary accepts;
  • the runtime version identifies which JavaScript and assets the binary can execute.

Do not merge them into one version string or assume passing one check implies the other. Runtime versions as compatibility boundaries explains the native capability side of that model.

Plan key rotation across an installed fleet

Key rotation is not just generating a new key pair. The application fleet may include several native binaries, each with a different trusted-key set and a different store-adoption timeline.

A safe rotation plan answers:

  1. Which installed binaries trust the old key, the new key, or both?
  2. How does signed metadata identify the key used for verification?
  3. When will a binary containing the new public key reach enough devices?
  4. Which key signs updates during the transition?
  5. What happens to an old binary after the old key is retired?
  6. How is an emergency compromise handled differently from routine rotation?

Key identifiers let signed metadata select among already trusted public keys. They do not deliver trust by themselves. A manifest cannot safely introduce the public key that verifies its own signature unless an earlier trust mechanism authenticates that change.

Expo's code-signing documentation makes the consequence explicit: its certificate is part of the runtime, and rotation creates a new runtime line and native build. Other systems may support an overlap period with more than one embedded public key, but the transition still has to respect what each installed binary can verify.

Test rotation before an emergency. Publish a harmless update with the transition configuration, exercise old and new binaries, reject an unknown key deliberately, and confirm the safe fallback path. A rotation process that exists only in a runbook has not yet proven that it preserves update availability.

Authenticate fresh release state, not only cached bytes

A valid signature proves that a trusted key signed particular content. It does not automatically prove that the content is current. A previously valid release statement can remain cryptographically valid after an operator pauses delivery, revokes a release, or changes the eligible target.

Production designs therefore need a freshness or revocation model in addition to signature verification. Depending on the system, that can involve expirations, monotonically increasing generations, short-lived signed authority state, or another authenticated mechanism that prevents indefinite replay of stale selection data.

The client must define safe behavior for:

  • an expired statement;
  • an unknown signing key;
  • a generation older than one it has already accepted;
  • two different signed statements claiming the same generation;
  • an unavailable freshness check;
  • a locally stored update that is later revoked.

Failing safely does not always mean blocking application launch on a network request. A mobile app may need to start offline. The design should preserve a locally verified known-good state while refusing to promote unauthenticated or stale candidates.

Verify patches as a route to a complete target

Patch delivery adds state to the verification problem. The patch is useful only for the expected source artifact, and reconstruction may fail because the base is missing, local files changed, storage ran out, or the process was interrupted.

The important invariant is the result:

  • verify the expected source identity before applying a patch;
  • reject paths, sizes, or hashes outside the authenticated artifact description;
  • reconstruct into a staging location rather than over the active bundle;
  • verify the complete target file tree after reconstruction;
  • promote it atomically only after verification succeeds;
  • retain a verified full-bundle path when patch prerequisites are not satisfied.

The application-facing API should not need a “trust this patch anyway” branch. Whether bytes arrived through a delta or complete download, activation should see the same verified target identity.

Signing cannot make a faulty release healthy

A correctly signed update can still contain a JavaScript exception, a destructive data migration, a bad API assumption, or a call to a native method absent from the installed binary. Signing establishes publisher authenticity. It does not perform code review, compatibility analysis, or runtime health assessment.

That is why OTA safety continues after verification:

  • runtime versions constrain native compatibility;
  • staged rollout limits initial exposure;
  • observability identifies the active OTA artifact;
  • server rollback changes future selection;
  • native startup recovery can leave a candidate that repeatedly fails before JavaScript becomes healthy.

React Native OTA Updates: How Production Delivery Works connects those stages as one release system. Security is strongest when each stage produces evidence for the next instead of asking one signature to stand in for the entire release process.

How Bundle Drop establishes runtime trust

Disclosure: I am the founder of Bundle Drop.

Bundle Drop setup writes .bundle-drop/runtime-delivery.lock.json as pinned native build input. The public installation documentation describes the file as containing project identity, an expected manifest origin, a manifest access identifier, and public P-256 verification keys. It contains no private signing key, Personal Access Token, or temporary artifact capability, so it is intended to be committed with the application.

The documented bundle-drop sync workflow authenticates the project, validates the returned public trust data, refreshes the lockfile, and repairs its source-control rules. When trusted keys or identity change, teams commit that result and create a new native binary so installed clients receive the new trust root.

That bootstrap does not widen runtime compatibility or prevent a valid publisher from releasing a bug. Bundle Drop documents compatibility, artifact delivery, rollout, and recovery as separate boundaries under Installation, Uploading, and Rollback.

Review an OTA provider as a trust system

Before depending on an OTA service, exercise these questions with a release build:

  1. Where is the private signing key stored, and who can use it?
  2. Which exact fields are authenticated by the signature?
  3. How does the client choose among trusted keys?
  4. What native release is required to rotate or revoke a key?
  5. How are artifact bytes, assets, and reconstructed patches verified?
  6. What happens on an unknown key, expired statement, corrupted download, or replayed generation?
  7. Can the app still start offline from a known-good verified state?
  8. How are publication credentials separated from public client configuration?
  9. Can operators identify which signed artifact is actually running?
  10. Does recovery still work when the candidate JavaScript never reaches application startup?

The strongest answer is a tested failure path, not a feature-matrix checkmark.

Make safe refusal part of the release contract

OTA security is not one cryptographic feature. It is a chain of explicit decisions: the native binary pins a trust root, signed state authenticates the selected release, hashes verify the complete local artifact, runtime metadata enforces compatibility, and health signals decide whether the verified candidate should remain active.

When any link is missing, the safe result is not “try to run it and see.” Keep the current verified bundle, use a documented trusted fallback, and preserve enough identity to diagnose why the candidate was refused. That behavior makes signing, key rotation, and integrity verification operational controls rather than labels on a feature page.

Common questions

Is HTTPS enough for React Native OTA updates?

HTTPS protects the network connection and remains required. Publisher signatures and artifact verification add end-to-end evidence that the release was authorized and that the bytes match the selected target after passing through the rest of the delivery path.

Is it safe to commit an OTA public key?

Yes. A public verification key is designed to be distributed. The private signing key must remain outside the app and source repository. Public project identifiers should still be documented separately from credentials that authorize publication.

Does rotating an OTA signing key require a store release?

Usually, when the trusted public key is embedded in native configuration, the application needs a new native binary to trust the new key. Some systems permit an overlap period with multiple pre-embedded keys, but a signed OTA manifest cannot establish its own previously unknown trust root.

Does signing prevent a bad OTA update?

No. Signing proves publisher authenticity. Runtime compatibility, testing, staged rollout, monitoring, rollback, and local recovery address different ways an authentic release can still be unsafe.

Should a patch or the final bundle be verified?

The client should authenticate the selected target and verify the complete reconstructed result before activation. Patch-specific checks are useful, but a successful patch download alone does not prove that the final local artifact is correct.

About the author

Founder and maintainer of Bundle Drop

George builds Bundle Drop and writes about the compatibility, delivery, rollout, and recovery boundaries behind React Native OTA updates.

GitHub profile

Interested in safer OTA deployments?

Read the implementation guides or connect a React Native project when you are ready to test the release workflow.