Skip to main content

5-Minute Quickstart

This step-by-step guide walks you through integrating the tk_ota_push SDK into your Flutter application, configuring the RemoteConfigGatekeeper filter, and deploying your first over-the-air hot patch.

Step 1: Create the shorebird.yaml File​

At the root of your Flutter project (alongside pubspec.yaml), create shorebird.yaml:

# Multi-Tenant Organization Identifier
tenant_id: sara-money

# Mobile Application Identifier (optional if detected from Gradle/Xcode)
app_id: com.saramoney.app

# Sovereign OTA Server Base URL (read by the CLI and mobile Rust engine)
base_url: https://ota.saramoney.internal

# Default distribution channel
channel: stable

# Delegates network calls to the 3-step Dart Gatekeeper
auto_update: false

# Enforce RSA-SHA256 signature verification at cold boot
patch_verification: strict

# RSA Public Key (automatically synchronized by tk-ota-push release/patch)
patch_public_key: MIIBCgKCAQEA...

Verify that shorebird.yaml is registered under assets in pubspec.yaml:

dependencies:
flutter:
sdk: flutter
tk_ota_push: ^1.0.0

flutter:
assets:
- shorebird.yaml

Step 2: Initialize TkOtaPush in lib/main.dart​

Here is a complete, working lib/main.dart demonstrating:

  • Initializing TkOtaPush.init(...) with the mobile security shield and rollback detection.
  • Evaluating a Remote Config payload with syncWithRemoteConfig(...).
  • Handling critical updates when force_update: true.
  • Inspecting the device identity (readDeviceIdentity()) for support diagnostics.
import 'package:flutter/material.dart';
import 'package:tk_ota_push/tk_ota_push.dart';

void main() async {
WidgetsFlutterBinding.ensureInitialized();

// 1. Initialize the tk_ota_push mobile SDK
final ota = TkOtaPush.init(
appReleaseVersion: '1.0.0+1',
enableGatekeeper: true,
requireWifiForSilentUpdate: false,
minBatteryPercentageForSilentUpdate: 20,
securityPolicy: const OtaSecurityPolicy(
blockRootedOrJailbrokenDevices: true,
expectedApkSignaturesSha256: {
// Replace with your production release keystore SHA-256 fingerprint
'6b6929c8ff345cf008496328d7bff081a8ccfb89498bf9916ed668be3c3dedcf',
},
),
onRollbackDetected: (event) async {
debugPrint(
'Rollback detected on ${event.releaseVersion}: '
'#${event.previousPatchNumber} -> #${event.restoredPatchNumber}',
);
// Flush incompatible local caches (SQLite / SharedPreferences / Hive) if needed
},
onSecurityViolation: (verdict) async {
debugPrint('OTA security violation: ${verdict.reason}');
},
);

// 2. Fetch the Remote Config payload (Firebase, custom REST API, etc.)
final remoteConfigJson = <String, dynamic>{
'target_release_version': '1.0.0+1',
'patch_available': true,
'latest_patch_number': 1,
'force_update': false,
'release_notes': 'Performance improvements on money transfer flow.',
};

// 3. 3-step intelligent sync (0 network calls when already up to date)
final outcome = await ota.syncWithRemoteConfig(
OtaRemoteConfig.fromJson(remoteConfigJson),
);
debugPrint('Execution mode: ${outcome.executionMode.name} (${outcome.message})');

runApp(const MyApp());
}

class MyApp extends StatelessWidget {
const MyApp({super.key});


Widget build(BuildContext context) {
return MaterialApp(
title: 'TkushPatch Quickstart',
home: Scaffold(
appBar: AppBar(title: const Text('TkushPatch OTA Status')),
body: FutureBuilder<OtaDeviceIdentity?>(
future: TkOtaPush.instance.readDeviceIdentity(),
builder: (context, snapshot) {
final identity = snapshot.data;
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Release Version: ${TkOtaPush.instance.appReleaseVersion}'),
const SizedBox(height: 8),
Text('Active Patch: #${identity?.currentPatchNumber ?? 0}'),
const SizedBox(height: 8),
Text('Tier-3 Support Code: ${identity?.shortSupportCode ?? "Pending"}'),
const SizedBox(height: 8),
Text('Channel: ${TkOtaPush.instance.track.name}'),
],
),
);
},
),
),
);
}
}

Step 3: Publish the Reference Release​

Before deploying differential patches, you must compile and seal the baseline Release matching the version distributed to users (1.0.0+1 in pubspec.yaml):

# 1. Authenticate with the TkushPatch server
tk-ota-push login

# 2. Build and lock the baseline release
tk-ota-push release -p android

During this command, the CLI:

  1. Automatically retrieves the server RSA public key (GET /api/admin/v1/public-key) and updates patch_public_key in shorebird.yaml.
  2. Compiles the app in release mode using the Shorebird toolchain (--obfuscate enabled by default).
  3. Extracts application binary directly from the resulting APK/AAB archive and registers its SHA-256 hash as well as native code integrity hashes (NativeIntegrityScanner) on the server.

Step 4: Deploy Your First OTA Patch​

Modify a string or Dart business logic inside lib/main.dart without bumping the version in pubspec.yaml, then run:

tk-ota-push patch -p android -m "First hot patch"

The server calculates the binary differential (binary diff + optimized compression), signs the SHA-256 digest of the target binary using the private RSA key hosted in cryptographic signing engine, and publishes Patch #1.

Upon next execution of syncWithRemoteConfig with patch_available: true and latest_patch_number: 1:

  1. The 3 conditions of RemoteConfigGatekeeper evaluate to green (GatekeeperVerdict.allowed).
  2. The SDK executes checkAndUpdate(), downloads the differential micro-patch, and validates its RSA-SHA256 signature.
  3. On the next application restart (Cold Start), Patch #1 is immediately active.