soroq_dart_ota 0.1.0
soroq_dart_ota: ^0.1.0 copied to clipboard
iOS dart_eval patch-point OTA for Soroq. Runs SIGNED downloaded Dart bytecode at designated patch points via a bundled dart_eval Runtime. Companion to soroq_flutter; designated points only (not arbitr [...]
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 trust —
checkAndApplyrefuses 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.
- Guardrails —
maxPayloadBytes(default 1 MiB; oversized payloads are rejected before activation, store unchanged) andexecTimeBudget(default 100 ms/call; an over-budget interpreter call throwsSoroqExecBudgetExceeded).
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.