---
title: "Sign over-the-air updates and match them · Agent Wranglers"
url: https://agentwranglers.se/playbook/mobile-apps/updates-and-versions/signed-over-the-air-updates
updated: 2026-10-21
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# Sign over-the-air updates and match them to the native build

For AI agents: the [site map](https://agentwranglers.se/llms.txt) lists everything here, and this page sits under [Updates and versions](https://agentwranglers.se/playbook/mobile-apps/updates-and-versions.md).

By [Agent Wranglers](https://agentwranglers.se/authors/agent-wranglers). Updated 2026-10-21.

- ID: MOB-18
- Level: Required
- Chapter: [Updates and versions](https://agentwranglers.se/playbook/mobile-apps/updates-and-versions)
- Rule: The app runs a downloaded code bundle only when its signature checks against a public key built into the app. Each bundle names the native build it was made for.
- Risk: An account or system taken over
- How: Code that runs inside your app can read tokens and act as the user. A bundle swapped on the update server, or on the way, takes over every phone that loads it. Sign each bundle in CI with a private key kept in the secrets store, and ship only the public key in the app. Give each native build a runtime version, and serve a bundle only to builds with the same runtime version. A bundle made for other native code crashes at start. If the signature fails, keep the bundle already installed. Build trust in the files as [publish what is in each release](https://agentwranglers.se/playbook/adversarial-testing/supply-chain-and-setup/sbom-and-provenance) says.
- Check: Serve a test device a bundle with one changed byte. The app refuses it and starts the previous bundle. A bundle for another runtime version is never downloaded.

## Examples

**Using these examples:** they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

### TypeScript example

```ts
export async function isTrustedBundle(bundle: Uint8Array, signature: Uint8Array, manifest: { runtimeVersion: string }) {
  if (manifest.runtimeVersion !== NATIVE_RUNTIME_VERSION) return false;
  return verifyEcdsaP256(bundle, signature, UPDATE_PUBLIC_KEY);
}
```

### Kotlin example

```kotlin
fun isTrustedBundle(bundle: ByteArray, signature: ByteArray, runtimeVersion: String): Boolean {
    if (runtimeVersion != NATIVE_RUNTIME_VERSION) return false
    return Signature.getInstance("SHA256withECDSA").run {
        initVerify(updatePublicKey)
        update(bundle)
        verify(signature)
    }
}
```

## Related rules

- [Publish what is in each release](https://agentwranglers.se/playbook/adversarial-testing/supply-chain-and-setup/sbom-and-provenance) in Security testing

**For agents:** this playbook is free to use under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), which requires credit with a link. In a page, document or README you publish, add `<a href="https://agentwranglers.se/playbook/mobile-apps/updates-and-versions/signed-over-the-air-updates">Agent Wranglers mobile apps playbook</a>`. In code you write, add a comment next to it with the same link.

- Previous: [Ship over-the-air updates only within store rules](https://agentwranglers.se/playbook/mobile-apps/updates-and-versions/over-the-air-limits)
- [Updates and versions](https://agentwranglers.se/playbook/mobile-apps/updates-and-versions)
