3-Step Remote Config Gatekeeper
In a standard naive OTA setup (auto_update: true), every cold start of the mobile application initiates a POST /api/v1/patches/check HTTP request toward the OTA server. Across an active fleet of millions of devices, this produces millions of redundant daily requests when no patch has actually been released, consuming backend bandwidth and draining users' cellular data plans.
The RemoteConfigGatekeeper (lib/src/gatekeeper/remote_config_gatekeeper.dart) solves this inefficiency by evaluating 3 cumulative conditions locally in memory against a lightweight JSON payload (delivered via your standard startup config channel: Firebase Remote Config, launch API endpoint, etc.).
Architecture and Decision Flow
- Startup Health Check: Validates previous launch state and processes active revocations.
- Security Compliance: Evaluates local device security and runtime policies.
- Update Query: Checks in with the TkushPatch server with environment parameters.
- Download & Activation: Downloads and seals the patch if all conditions are satisfied.
The 3 Cumulative Conditions
The RemoteConfigGatekeeper.evaluate(...) method assesses the following rules in strict order:
-
Condition 1 — Release Version Match (
targetReleaseVersion):- Compares
installedReleaseVersion.trim()withremoteConfig.targetReleaseVersion.trim(). - If either string is empty or if
installed != target, the Gatekeeper immediately returnsGatekeeperVerdict.skippedVersionMismatch(shouldCallOtaServer: false). - Purpose: an end-user still running version
1.0.0+2(or newly upgraded to1.0.0+4) never queries the server for a hotfix compiled specifically for1.0.0+3.
- Compares
-
Condition 2 — Availability Kill-Switch (
patchAvailable):- Asserts that
remoteConfig.patchAvailable == true. - If
false, returnsGatekeeperVerdict.skippedNoPatchAvailable(shouldCallOtaServer: false). - Purpose: enables operators to immediately silence all OTA polling across the fleet with a single toggle in Remote Config without republishing the mobile app.
- Asserts that
-
Condition 3 — Strict Patch Monotonicity (
latestPatchNumber):- Reads the patch number currently running in the Rust engine (
currentPatchNumber ?? 0) and compares it againstremoteConfig.latestPatchNumber. - If
currentPatch >= latestPatch, returnsGatekeeperVerdict.skippedAlreadyUpToDate(shouldCallOtaServer: false). - If
currentPatch < latestPatch, all 3 conditions are fulfilled: returnsGatekeeperVerdict.allowed(shouldCallOtaServer: true).
- Reads the patch number currently running in the Rust engine (
The OtaRemoteConfig Model
The OtaRemoteConfig class (lib/src/models/ota_remote_config.dart) supports both snake_case and camelCase keys when instantiated via OtaRemoteConfig.fromJson(Map<String, dynamic> json):
| Dart Property | JSON Keys Accepted by fromJson | Type | Default | Role |
|---|---|---|---|---|
targetReleaseVersion | target_release_version, targetReleaseVersion | String | '' | Target application release version (x.y.z+build). |
patchAvailable | patch_available, patchAvailable | bool | false | Enables or closes patch checks for this release version. |
latestPatchNumber | latest_patch_number, latestPatchNumber | int | 0 | Latest published patch number (int, num, or numeric String). |
forceUpdate | force_update, forceUpdate, force_critical_update | bool | false | false = silent background download; true = blocking critical update modal. |
releaseNotes | release_notes, releaseNotes | String? | null | Optional descriptive message passed to onCriticalUpdateRequired. |
Remote Config JSON Payloads Examples
Scenario A: No Patch Published for Current Release (0 Network Calls)
{
"target_release_version": "1.0.0+3",
"patch_available": false,
"latest_patch_number": 0,
"force_update": false
}
Scenario B: Silent Background Update Available (Patch #2)
{
"target_release_version": "1.0.0+3",
"patch_available": true,
"latest_patch_number": 2,
"force_update": false,
"release_notes": "Performance optimizations for transaction history loading."
}
Scenario C: Critical Forced Update with Blocking Modal
{
"target_release_version": "1.0.0+3",
"patch_available": true,
"latest_patch_number": 3,
"force_update": true,
"release_notes": "Mandatory security update required to complete operations."
}
Verdicts Matrix (GatekeeperVerdict) and Execution Modes (OtaUpdateExecutionMode)
| Device State | OtaRemoteConfig Payload | GatekeeperVerdict | OtaUpdateExecutionMode | networkCallPerformed | restartRequired |
|---|---|---|---|---|---|
| Invalid APK certificate or Root detected | (Any) | null (halted earlier) | blockedBySecurityShield | false | false |
Version 1.0.0+2, Patch null | target: "1.0.0+3", available: true, latest: 1 | skippedVersionMismatch | blockedByGatekeeper | false | false |
Version 1.0.0+3, Patch null | target: "1.0.0+3", available: false, latest: 1 | skippedNoPatchAvailable | blockedByGatekeeper | false | false |
Version 1.0.0+3, Patch #2 | target: "1.0.0+3", available: true, latest: 2 | skippedAlreadyUpToDate | blockedByGatekeeper | false | false |
Version 1.0.0+3, Patch #1, on 4G (requireWifi: true) | target: "1.0.0+3", available: true, latest: 2, force: false | allowed | deferredToBackgroundTask | false | false |
Version 1.0.0+3, Patch #1, Wi-Fi & Battery OK | target: "1.0.0+3", available: true, latest: 2, force: false | allowed | silentBackgroundUpdate | true | false |
Version 1.0.0+3, Patch #1, on 4G | target: "1.0.0+3", available: true, latest: 2, force: true | allowed | criticalForcedUpdate | true | true (if downloaded) |
enableGatekeeper: false | force: false (Wi-Fi/Battery OK) | bypassedGatekeeperDisabled | silentBackgroundUpdate | true | false |
When forceUpdate: true, the SDK bypasses requireWifiForSilentUpdate and minBatteryPercentageForSilentUpdate so that high-priority security patches are downloaded without waiting for Wi-Fi or battery thresholds.