BuildingNative iOS and native Android OTA — join the early-access list

Security model

Shipping code around the app store review queue only works if the pipe is trustworthy. This is what Birdify signs, what your users' devices check before running anything, and what happens when a patch turns out to be bad.

What we're defending against

Over-the-air updates move executable behaviour to devices you don't control, over a network you don't own. Three things have to hold:

  • Nobody else can ship to your users. A bundle that Birdify didn't sign with your project key is never applied.
  • Nothing changes in transit. A truncated or modified bundle fails its checksum and is discarded.
  • A bad patch can't strand anyone. An update that fails to boot reverts on the device itself, without waiting for you to notice.

What Birdify explicitly does not do is download native machine code — that's both the store-policy line and the reason the blast radius of a bad patch is bounded. See How Birdify works for what a bundle actually contains.

What gets signed

Every release and every patch is signed at publish time with your project's Ed25519 private key, and a SHA-256 checksum is recorded alongside it. Bundles move over TLS 1.3.

bash
$ birdify release
 release 1.4.0 registered · signed ed25519:shop_app

Key handling, rotation, and who holds the private half: Signing & verification.

What the device checks

Before a bundle is applied, the SDK re-computes the checksum and verifies the signature against the public key compiled into your binary. Either check failing means the update is dropped and the app keeps running the last good version. You can run the identical check from your machine:

bash
$ birdify verify patch#3
checksum   9f2c…a71b
signer     birdify:shop_app
signature valid ✓ · native code: none

When something goes wrong

Two independent paths pull a bad update, and neither depends on the other:

  • You pull it. birdify rollback withholds the patch fleet-wide; devices return to the last good bundle on next launch.
  • The device pulls it. A patch that fails to boot is reverted locally and reported back, so a release that only breaks on one OS version doesn't need you awake to catch it.
bash
$ birdify rollback
 patch #3 withheld · reverting fleet
# fail-open: unhealthy patches auto-revert too

The full state machine, including staged rollouts: Rollback & health checks.

Where the trust boundary sits

On Birdify cloud we store your signed bundles and the metadata needed to deliver them — deployment config, target framework, and patch versions. We never receive your source, and we never see data from inside your app.

If that boundary is in the wrong place for you, run the update server yourself: point the CLI at your own host and bundles never leave your infrastructure.

bash
$ birdify config set server https://ota.internal.example.com
 server updated (self-hosted)

Setup and operational notes: Self-hosting.

Why this stays store-safe

Apple's Guideline 2.5.2 and Google's equivalent permit updating interpreted code and data; they prohibit downloading new native binaries. Birdify only ever moves the managed layer — the Dart bundle, the JS bundle, or a UI description — so a patch changes what your shipped binary does with data, never what native code it contains.

The consequence worth internalising: anything that needs new native code — a new SDK, a new permission, a new platform dependency — is a store release, not a patch.