Native Integrity and Tampering Prevention
A core guarantee of TkushPatch is absolute runtime safety. Flutter Over-The-Air updates operate by hot-swapping AOT-compiled Dart machine code (application binary on Android, App.framework/App on iOS).
However, if a change touches native code (Java/Kotlin bytecode, C++ libraries, Swift/Obj-C frameworks, or OS manifest permissions), pushing an OTA patch will trigger severe runtime crashes (NoSuchMethodError, ClassNotFoundException, or SIGSEGV) because the store-installed binary on users' phones lacks those native symbols.
To eliminate this catastrophic risk, tk-ota-push incorporates a deterministic NativeIntegrityScanner (lib/src/cli/native_integrity.dart), verified by test/native_integrity_test.dart.
1. Why pubspec.lock Hashing Alone Is Insufficient
Historically, code push tools solely verified the SHA-256 hash of pubspec.lock. This approach suffers from two severe limitations:
- Frequent False Positives: Bumping a pure Dart package (e.g., from
intl ^0.18.0to^0.19.0) alterspubspec.lock, even though the code changes are 100% patchable via OTA. - Dangerous False Negatives: Manually modifying Java/Kotlin files in
android/app/src/main/or Swift files inios/Runner/does not changepubspec.lock, yet will break the application if patched over the air.
The NativeIntegrityScanner in tk-ota-push overcomes this by directly inspecting the compiled binary artifacts.
2. How the NativeIntegrityScanner Operates
During a release, the CLI computes and stores the baseline native footprint (native_code_hash) on the server. During a subsequent patch, it re-scans the newly compiled archive and compares both fingerprints.
- Archive Inspection: The CLI inspects the compiled application package (
.apk,.aab, or.ipa). - Native Code Audit: The OS manifest and native libraries are verified against the reference release.
- Integrity Verdict:
- If native code is untouched: The Dart binary patch generation proceeds.
- If native changes are detected: Build stops with a requirement to submit a full store release.
Android Inspection (scanAndroidArchive)
For each entry inside the APK or AAB bundle, the scanner evaluates:
| Component | Inspected Files | Integrity Role |
|---|---|---|
| Java / Kotlin Bytecode | classes.dex, classes2.dex... | Detects native Android code additions or modifications |
| Android Manifest | AndroidManifest.xml | Detects new permissions (CAMERA, LOCATION), services, or receivers |
| Native C++ Libraries | lib/<arch>/*.so (e.g., libflutter.so, SQLite) | Detects Flutter engine bumps or modified third-party C++ libs |
| Dart Snapshot | lib/<arch>/application binary | STRICTLY EXCLUDED from the native hash because this is the code OTA intends to update! |
| Flutter Assets | assets/flutter_assets/ | EXCLUDED (handled separately by the asset manifest scanner) |
iOS Inspection (scanIosApp)
On the compiled Runner.app bundle, the scanner inspects:
Info.plist: Detects new usage permissions (NSCameraUsageDescription, URL schemes, Bluetooth keys).Frameworks/: Verifies all dynamic native third-party frameworks, strictly excludingFrameworks/App.framework(which contains the Dart snapshot).- Mach-O Main Binary: Computes the SHA-256 of the native application executable.
3. Behavior When Native Alteration Is Detected
When running tk-ota-push patch, if the nativeCodeHash of the newly compiled code does not match the baseline Release registered on the server:
==================================================================
Native Code Integrity Verification
==================================================================
Locked Release on TkushPatch Server : b7e4a18c...
Locally compiled Patch : 8f92cd30...
❌ CRITICAL NATIVE INTEGRITY ERROR:
Native code alterations have been detected between the baseline
Release and the patch you are attempting to publish:
- classes.dex: MODIFIED (Kotlin/Java bytecode altered)
- AndroidManifest.xml: MODIFIED (New permission android.permission.CAMERA added)
⚠️ An OTA patch CANNOT alter native binaries or OS permissions.
Distributing this patch would cause immediate fatal crashes on user devices.
REQUIRED ACTION: You must create a NEW Store Release (e.g., 1.0.0+2)
via 'tk-ota-push release' and submit the new build to Google Play / App Store.
The CLI terminates immediately with exit code 1, and no patch payload is uploaded or signed on the server.
4. Asset Manifest Hash (computeAssetsManifestHash)
To protect asset integrity, FlutterProject.computeAssetsManifestHash() computes a deterministic combined SHA-256 of all assets declared in pubspec.yaml:
declared:assets/images/,assets/i18n/
assets/images/logo.png=8a3f91...
assets/images/promo.png=c2d10e...
assets/i18n/fr.json=5b47a9...
Any added, deleted, or modified asset produces a distinct hash, recorded in Release and Patch metadata for validation by the server and the OtaAssetManager runtime SDK.
5. The --force (-f) Flag: When and How to Use It
The --force option on tk-ota-push patch is designed for situations where pubspec.lock has changed (such as updating pure Dart packages without native dependencies) while the nativeCodeHash remains strictly identical:
# Force publication when pure Dart dependencies have drifted
tk-ota-push patch 1.0.0+1 -p android --force -m "Update intl package and fix currency formatting"
If the NativeIntegrityScanner detects an actual change in classes.dex, AndroidManifest.xml, or an iOS Mach-O binary, the CLI strictly refuses publication, even when --force is provided. The stability of your mobile fleet always supersedes instant delivery.