Skip to main content

tk_ota_push 0.2.2 copy "tk_ota_push: ^0.2.2" to clipboard
tk_ota_push: ^0.2.2 copied to clipboard

Sovereign OTA code push for Flutter with a 3-step Remote Config Gatekeeper, bank-grade security shield, and multiplatform deployment CLI.

tk_ota_push #

pub package License: MIT Platform Architecture

tk_ota_push est une solution 2-en-1 souveraine et on-premise pour Flutter, conçue pour les institutions financières, banques et entreprises (FinTech, Mobile Money, Télécoms). Elle allie :

  1. Un SDK Mobile (lib/) : Pilotage intelligent des mises à jour à chaud (Hot Code Push) avec Remote Config Gatekeeper en 3 étapes (0 appel réseau inutile), bouclier de sécurité mobile (Anti-Repackaging APK Xender, détection Root/Jailbreak, Pinning TLS) et gestionnaire d'assets dynamiques.
  2. Une CLI de Déploiement (bin/) : Outil natif en pur Dart pour compiler, verrouiller les Releases du Store, générer les différentiels binaires (bidiff + zstd) et publier les patchs signés cryptographiquement via HashiCorp Vault.

🏛️ Architecture Globale & Flux de Données #

flowchart TD
    subgraph Client["📱 Application Mobile Flutter"]
        App["App Métier (main.dart)"]
        RC["Remote Config Distant<br/>(Firebase / API Interne)"]
        GK{"🛡️ Gatekeeper<br/>3 Conditions"}
        Shield["🔒 Bouclier Sécurité<br/>(Certificat APK / Root / TLS)"]
        Updater["Moteur Natif Rust<br/>(libflutter / Updater)"]
    end

    subgraph Server["🏢 Serveur Souverain On-Premise"]
        API["Spring Boot API<br/>(Data Plane / Admin)"]
        Vault["🔑 HashiCorp Vault<br/>(Transit Engine / Clés RSA)"]
        Storage["📦 Coffre-Fort d'Artefacts<br/>(Releases & Différentiels)"]
        DB[("PostgreSQL 16<br/>Audit & Métadonnées")]
    end

    App -->|1. Initialisation| GK
    RC -->|2. Charge Payload JSON| GK
    GK -->|Condition échouée| Sleep["0 Appel Réseau (0 Mo Data)"]
    GK -->|3 Conditions réunies| Shield
    Shield -->|Appareil sain| Updater
    Updater -->|3. POST /api/v1/patches/check| API
    API -->|4. Renvoie URL & Signature| Updater
    API -.->|Signe condensat SHA-256| Vault
    Updater -->|5. Télécharge Micro-Patch (~100 Ko)| Storage
    Updater -->|6. Événements télémétrie| DB

⚡ Pourquoi le "Remote Config Gatekeeper" ? (0 Appel Inutile) #

Dans une architecture classique, le moteur natif appelle le serveur OTA à chaque lancement de l'application. Pour une banque ayant 5 millions d'utilisateurs actifs, cela engendre 10 à 15 millions de requêtes HTTP quotidiennes inutiles, saturant l'infrastructure et consommant le forfait data mobile (3G/4G) des clients.

En configurant auto_update: false dans shorebird.yaml, le SDK tk_ota_push prend le relais et évalue localement les 3 conditions cumulatives en mémoire en 0 milliseconde :

flowchart LR
    C1["1. Version Cible<br/>(target_release_version)"] -->|Match| C2["2. Validation Sécurité<br/>(patch_available == true)"]
    C2 -->|Match| C3["3. Monotonie Patch<br/>(current < latest)"]
    C3 -->|Match| OK["✅ Appel Serveur Autorisé"]
    C1 -.->|Différent| NO["🚫 0 Appel Réseau"]
    C2 -.->|False| NO
    C3 -.->|Déjà à jour| NO

Format du Payload Remote Config #

Que vous utilisiez Firebase Remote Config, une route API interne de la banque ou notre endpoint dédié, voici le JSON consommé par le SDK :

{
  "target_release_version": "1.0.0+3",
  "patch_available": true,
  "latest_patch_number": 2,
  "force_update": false,
  "release_notes": "Optimisation des virements instantanés et correctif UI."
}
Champ Type Description
target_release_version String Version installée ciblée (ex: 1.0.0+3). Si l'utilisateur est en 1.0.0+2, l'appel est ignoré.
patch_available bool Interrupteur maître. Si false, les serveurs OTA ne sont pas interrogés.
latest_patch_number int Dernier numéro de patch disponible. Si le client a déjà ce patch, aucun appel n'est fait.
force_update bool false = mode silencieux éco-data ; true = modale bloquante de sécurité.
release_notes String? Message affiché à l'utilisateur lors d'une mise à jour critique.

📦 Installation & Configuration #

1. Dépendance Flutter #

Ajoutez tk_ota_push à votre pubspec.yaml :

dependencies:
  flutter:
    sdk: flutter
  tk_ota_push: ^0.2.0

2. Configuration Moteur (shorebird.yaml) #

À la racine de votre application Flutter :

app_id: votre-app-id
base_url: https://tks-cloud.duckdns.org
patch_public_key: <VOTRE_CLE_PUBLIQUE_RSA_FOURNIE_PAR_LE_SERVEUR>
auto_update: false # INDISPENSABLE : délègue la décision réseau au Gatekeeper

Déclarez le fichier dans pubspec.yaml :

flutter:
  assets:
    - shorebird.yaml

💻 Intégration dans le Code Dart (main.dart) #

import 'package:flutter/material.dart';
import 'package:tk_ota_push/tk_ota_push.dart';

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

  // 1. Initialisation du SDK avec Bouclier de Sécurité Bancaire
  final ota = TkOtaPush.init(
    appReleaseVersion: '1.0.0+3',
    requireWifiForSilentUpdate: false, // true = télécharge uniquement en Wi-Fi
    minBatteryPercentageForSilentUpdate: 20,
    securityPolicy: const OtaSecurityPolicy(
      blockRootedOrJailbrokenDevices: true, // Bloque les appareils compromis
      expectedApkSignaturesSha256: [
        '6b6929c8ff345cf008496328d7bff081a8ccfb89498bf9916ed668be3c3dedcf', // Empreinte certificat APK officiel
      ],
      pinnedServerCertSha256Fingerprints: [
        '42d889f8f4b6bf9d3abb767d3b5f215e0009e4d26cb7b958497e8b291e1b96dc', // Pinning TLS
      ],
    ),
    // Hook de résilience post-rollback : déclenché si un patch défectueux est révoqué
    onRollbackDetected: (event) async {
      debugPrint('⚠️ ROLLBACK DÉTECTÉ : #${event.previousPatchNumber} -> #${event.currentPatchNumber}');
      // Purger le cache SQLite / SharedPreferences pour éviter tout conflit de schéma
    },
    // Déclenché si force_update == true dans le Remote Config
    onCriticalUpdateRequired: (config, triggerImmediateDownload) async {
      debugPrint('🚨 MISE À JOUR CRITIQUE OBLIGATOIRE : ${config.releaseNotes}');
      // Afficher une boîte de dialogue bloquante UI
      await triggerImmediateDownload();
    },
    onSecurityViolation: (verdict) async {
      debugPrint('⛔ VIOLATION DE SÉCURITÉ : ${verdict.reason}');
    },
  );

  // 2. Synchronisation pilotée par le Remote Config
  final remoteConfigPayload = {
    'target_release_version': '1.0.0+3',
    'patch_available': true,
    'latest_patch_number': 2,
    'force_update': false,
    'release_notes': 'Correction calcul des intérêts de compte épargne.',
  };

  final outcome = await ota.syncWithRemoteConfig(
    OtaRemoteConfig.fromJson(remoteConfigPayload),
  );

  debugPrint('Résultat Gatekeeper : ${outcome.message}');
  debugPrint('Appel réseau effectué ? ${outcome.networkCallPerformed}');

  runApp(const MonApplicationBancaire());
}

🎨 Politiques d'Exécution & Exemples Visuels #

1. Mode Silencieux / Éco-Data (force_update: false) #

Idéal pour les évolutions courantes. Le SDK inspecte l'état du réseau et de la batterie :

  • Si les conditions sont optimales, le micro-patch différentiel (~50 à 150 Ko) est téléchargé en arrière-plan sans interrompre l'utilisateur.
  • Il s'applique automatiquement au prochain démarrage à froid (Cold Start).
  • Si la batterie est faible ou que le Wi-Fi est requis, le téléchargement est différé via workmanager.

2. Mode Critique Forcé (force_update: true) #

Idéal pour les correctifs d'urgence (faille de sécurité, régulation bancaire). Une modale bloquante s'affiche immédiatement :

Future<void> showCriticalUpdateDialog(
  BuildContext context,
  OtaRemoteConfig config,
  Future<bool> Function() onDownload,
) {
  return showDialog(
    context: context,
    barrierDismissible: false,
    builder: (ctx) => AlertDialog(
      icon: const Icon(Icons.security_update_warning, size: 48, color: Colors.orange),
      title: const Text('Mise à Jour de Sécurité Obligatoire'),
      content: Text(config.releaseNotes ?? 'Veuillez patienter pendant la mise à jour.'),
      actions: [
        FilledButton(
          onPressed: () async {
            final success = await onDownload();
            if (success) {
              // Notifier l'utilisateur de redémarrer
            }
          },
          child: const Text('Mettre à jour maintenant'),
        ),
      ],
    ),
  );
}

🛡️ Le Bouclier de Sécurité Mobile (OtaSecurityPolicy) #

Protection Risque Éliminé Fonctionnement
Anti-Repackaging APK Faux APK modifiés ou infectés partagés via Xender / WhatsApp / Telegram Compare l'empreinte SHA-256 du certificat de signature de l'APK avec expectedApkSignaturesSha256. Si altéré $\rightarrow$ refus strict du patch.
Détection Root / Jailbreak Appareils compromis ou débogueurs malveillants Détecte les binaires su, Magisk, SuperSU, Cydia.
Certificate Pinning TLS Attaques Man-in-the-Middle (MitM) sur réseaux publics Valide l'empreinte SHA-256 du certificat HTTPS du serveur OTA souverain.

🖼️ Gestionnaire d'Assets Dynamiques (OtaAssetManager) #

Permet de mettre à jour des fichiers statiques (traductions JSON fr_CM.json, bannières promotionnelles, icônes SVG) sans aucune recompilation :

final assetManager = OtaAssetManager();

final descriptor = OtaRemoteAssetDescriptor(
  assetKey: 'assets/i18n/fr.json',
  downloadUrl: 'https://tks-cloud.duckdns.org/api/v1/assets/fr-v2.json',
  expectedSha256: '99e887f4f4b6bf9d3abb767d3b5f215e0009e4d26cb7b958497e8b291e1b96dc',
);

// Télécharge, vérifie l'intégrité SHA-256 et stocke dans le cache sécurisé
final file = await assetManager.fetchAndCacheAsset(descriptor);

🛠️ CLI de Déploiement (tk-ota-push) #

La CLI est incluse dans le package et peut être activée globalement sur n'importe quel poste développeur ou serveur CI/CD :

dart pub global activate tk_ota_push

1. Authentification Développeur (Passerelle Web & PKCE) #

tk-ota-push login --server https://tks-cloud.duckdns.org

Ouvre automatiquement votre navigateur pour l'authentification Keycloak / SSO et génère une session chiffrée locale (~/.tk_ota_push/credentials.json).

Vérifier la session active :

tk-ota-push whoami

2. Publication d'une Release du Store (Verrouillage du Binaire de Référence) #

À exécuter le jour où vous soumettez votre binaire sur Google Play ou Apple App Store :

tk-ota-push release 1.0.0+3 \
  --server https://tks-cloud.duckdns.org \
  --split-debug-info=./build/symbols

Reçu généré dans le terminal :

  ┌──────────────────────────────────────────────────────────────────────────
  │ 🔒 REÇU DE VERROUILLAGE RELEASE (RÉFÉRENCE STORE)
  ├──────────────────────────────────────────────────────────────────────────
  │ ID Release (UUID)   : a42e271a-31ab-4476-ae69-24e5d2b8448f
  │ Application (Tenant): sara-money-demo (sara-money)
  │ Version & Cible     : v1.0.0+3 • android (aarch64)
  │ Binaire Référence   : libapp.so (Scellé)
  │ Garde-Fou Natif     : pubspec.lock (42d889f8... Scellé)
  │ Garde-Fou Assets    : flutter_assets/ (99e887f4... Scellé)
  │ Symboles (.symbols) : Archivés (app.android-arm64.symbols)
  │ Statut Release      : LIVE_ON_STORE
  └──────────────────────────────────────────────────────────────────────────

3. Publication d'un Patch à Chaud (Différentiel bidiff + zstd) #

Après avoir modifié du code Dart :

tk-ota-push patch 1.0.0+3 \
  --server https://tks-cloud.duckdns.org \
  --channel stable \
  --message "Correction calcul frais de virement" \
  --split-debug-info=./build/symbols

Reçu généré dans le terminal :

  ┌──────────────────────────────────────────────────────────────────────────
  │ 🚀 REÇU DE PUBLICATION PATCH OTA #1
  ├──────────────────────────────────────────────────────────────────────────
  │ ID Patch (UUID)     : 8b73f831-29cf-4cb6-8801-44ebda29910a
  │ Application & Version: sara-money-demo • v1.0.0+3
  │ Cible & Canal       : android (aarch64) • Canal 'stable'
  │ Statut & Rollout    : APPROVED_AND_SIGNED (Déploiement : 100%)
  │ Taille Différentiel : 142.0 KB (145408 octets)
  │ SHA-256 Binaire     : 112233445566778899aabbccddeeff001122334455...
  │ Signature Vault RSA : d7a81c4e2b0f... (Scellée dans Vault)
  │ Symboles (.symbols) : Archivés (app.android-arm64.symbols)
  │ Auteur (Git / Dev)  : dev@saramoney.cm
  └──────────────────────────────────────────────────────────────────────────

4. Workflow à 4 Yeux (--staged) & Promotion de Patch #

Pour soumettre un patch à validation sans le déployer immédiatement :

# 1. Le développeur soumet le patch sur staging
tk-ota-push patch 1.0.0+3 --channel staging --staged --server https://tks-cloud.duckdns.org

# 2. Le Release Manager / RSSI approuve et promeut vers la production (Re-signature Vault automatique)
tk-ota-push promote --patch-id <UUID_DU_PATCH> --to-channel stable --server https://tks-cloud.duckdns.org

📜 Options & Flags de la CLI #

Commande Option / Flag Description
release / patch --server <url> URL du serveur OTA (défaut : http://localhost:8080 ou session CLI).
release / patch --tenant-id <id> Identifiant du Tenant (outrepasse shorebird.yaml).
release / patch --split-debug-info=<dir> Répertoire des symboles Dart pour l'archivage automatique .symbols.
release / patch --obfuscate / --no-obfuscate Active ou désactive l'obfuscation de code Dart (activé par défaut).
patch --channel <nom> Canal de destination : stable, staging, beta (défaut : stable).
patch --rollout <1-100> Pourcentage de déploiement progressif Canary (défaut : 100).
patch --staged Soumet le patch à l'état STAGED pour approbation à 4 yeux (Maker-Checker).
patch --message <texte> Description métier ou traçabilité Git liée au correctif.
Toutes --json Affiche la réponse brute de l'API en JSON (pour intégrations CI/CD).

📄 Licence #

Ce package est distribué sous licence MIT. Consultez le fichier LICENSE pour plus d'informations.

1
likes
155
points
--
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Sovereign OTA code push for Flutter with a 3-step Remote Config Gatekeeper, bank-grade security shield, and multiplatform deployment CLI.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

args, crypto, http, path, shorebird_code_push, yaml

More

Packages that depend on tk_ota_push