soroq_dart_ota

iOS dart_eval patch-point OTA for Soroq. Run signed, downloaded Dart bytecode at designated patch points using a bundled dart_eval Runtime, riding on top of the public soroq_flutter config/data OTA transport.

Companion to soroq_flutter — kept separate so only opt-in consumers pull dart_eval (and its analyzer/compiler toolchain) into their dependency graph.

⚠️ Truth boundary (read this)

  • NOT Shorebird parity. It patches designated patch points only, not arbitrary AOT Dart.
  • NOT "App Store safe." Residual Apple Guideline 2.5.2 risk; an App Review pilot is required before any store claim.
  • No JIT, no downloaded dylib/Mach-O, no replacement engine.
  • The shipped app links the dart_eval Runtime only. The compiler/analyzer are never imported by this package's runtime code and tree-shake out of the AOT binary — verify with scripts/check-ios-binary-hygiene.sh <app|ipa> (analyzer = 0, compiler entrypoints = 0).

Install

flutter pub add soroq_dart_ota   # pulls soroq_flutter transitively

Requires Flutter with Dart >=3.9.0; the release gate is verified with Flutter 3.38.5 / Dart 3.10.4 or newer.

Consumer flow

flutter pub add soroq_dart_ota
soroq init
soroq release ios          # register the baseline; ship its IPA to TestFlight/App Store
# …edit your patch-point bytecode…
soroq patch ios --config-file config.json   # publish the signed .evc patch

Usage

Ship a baseline .evc as a Flutter asset and wire a patch point:

import 'package:soroq_dart_ota/soroq_dart_ota.dart';

final patchPoint = SoroqDartPatchPoint(
  apiBases: ['https://your-control-plane'],   // operator-configured; no local default
  appId: 'com.example.app',
  runtimeId: 'ios-runtime-1',
  channel: 'stable',
  releaseId: 'ios-release-1',
  bundledBytecodeAsset: 'assets/dartcode/classify.evc', // shipped baseline
  library: 'package:yourapp/logic.dart',
  function: 'classify',
  manifestTrust: trust, // REQUIRED — without it, checkAndApply fails CLOSED
);

await patchPoint.warmUpNetworkPermission();   // iOS Local-Network prompt (LAN demos)
final out = await patchPoint.run([1, 2, 3]);  // current behavior (bundled or active patch)
final result = await patchPoint.checkAndApply(); // download + verify + activate a signed patch
await patchPoint.rollback();                  // back to the bundled baseline

Hardening (built in)

  • Fail-closed manifest trustcheckAndApply refuses to download/apply without a trusted manifest key (no fail-open).
  • Transactional apply — verify → activate; a failed/bad re-check never wipes a prior good patch (no half-active state).
  • Cold-start activation — a downloaded+verified patch stays active across launches (loaded from the durable store).
  • Rollback — restores the bundled baseline; never leaves a half-active patch.
  • GuardrailsmaxPayloadBytes (default 1 MiB; oversized payloads are rejected before activation, store unchanged) and execTimeBudget (default 100 ms/call; an over-budget interpreter call throws SoroqExecBudgetExceeded).

Public API

Symbol Purpose
SoroqDartPatchPoint the lifecycle: run / checkAndApply / rollback (+ warmUpNetworkPermission, isPatched, store)
PatchApplyResult {applied, endpoint, patchNumber, bytecodeBytes, attempts}
SoroqExecBudgetExceeded thrown when a patch-point call exceeds execTimeBudget

Performance / footprint

See docs/ios-ota-benchmarks.md in the monorepo for the budgeted benchmark table and the binary-hygiene gate. Patch-point what is small, pure, and changes often; keep .evc well under maxPayloadBytes and per-call work under execTimeBudget.

Libraries

soroq_dart_ota
Soroq iOS dart_eval patch-point OTA — run SIGNED downloaded Dart bytecode at designated patch points using a bundled dart_eval Runtime, on top of the public soroq_flutter config/data OTA transport.