applyKbcPatchFromBytes method

Future<KbcPatchResult> applyKbcPatchFromBytes(
  1. Uint8List envelopeBytes, {
  2. required KbcLoaderFn loader,
})

β.3 + ε + β.4 + η: apply a Sankofa Deploy: Flutter Code KBC patch.

Pass a SANKOFA_KBC_ENVELOPE (.skdp) buffer plus a loader callback that calls the Dart VM's loadDynamicModule. The canonical wiring (from a host app's pubspec with dynamic_modules as a path-dep on pkg/dynamic_modules from our Dart SDK fork) is:

import 'package:dynamic_modules/dynamic_modules.dart';

final result = await Sankofa.deploy!.applyKbcPatchFromBytes(
  envelopeBytes,
  loader: loadModuleFromBytes,
);

The SDK:

  • parses the envelope (magic SKDP, version 1, length-prefixed header + JSON metadata + KBC payload + signature trailer)
  • verifies the payload SHA-256 against the carried digest — fail-closed on mismatch, the loader is never called
  • sanity-checks the inner KBC magic (DBC3 = 33 43 42 44)
  • hands the verified KBC bytes to loader
  • returns the dyn-module entry-point's return value plus the envelope metadata in a KbcPatchResult

Throws KbcApplyException on any pre-load failure. Throws via the loader on engine-side issues (e.g. running a stock Flutter engine that wasn't built with dart_dynamic_modules=true → "Loading of dynamic modules is not supported"). The exception message names the most likely cause to keep debugging short.

This is the iOS Path C ship surface (β.3 proved on iPhone 2026-05-24). Android baseline OTA still uses the Phase 5 libapp.so binary-diff route — see checkForUpdate.

Implementation

Future<KbcPatchResult> applyKbcPatchFromBytes(
  Uint8List envelopeBytes, {
  required KbcLoaderFn loader,
}) {
  // Path C is pure Dart — no platform plugin needed. Do NOT call
  // _assertReady(): on iOS the platform plugin's libapp.so updater
  // doesn't exist, so _ready stays false even though Path C works.
  return kbc_loader.applyKbcEnvelope(
    envelopeBytes,
    loader: loader,
    signingPubkeysB64: _effectiveSigningPubkeys,
  );
}