soroq_flutter 0.3.0
soroq_flutter: ^0.3.0 copied to clipboard
App-facing Flutter package for Soroq hard OTA — Android code-push runtime and the experimental iOS engine-lane hard-OTA state machine (requires the Soroq iOS engine toolchain selected by `soroq setup`).
soroq_flutter #
soroq_flutter is the app-facing Flutter package for Soroq hard OTA.
- Android hard OTA is driven by this package: the native
startup/update runtime (
SoroqAutoUpdateCoordinator/SoroqPatchDownloadCoordinator/SoroqColdStartCoordinator/SoroqManifestTrust) + the Dart runtime bridge. - iOS hard OTA (EXPERIMENTAL) is driven by this package's pure-Dart engine-lane
state machine ([
Soroq] / [SoroqEngineLaneController]): fetch → Ed25519 verify → SHA-256 verify → transactional stage → hosted v0 rollback → crash-loop quarantine → client-id/rollout → tamper refusal. It requires the Soroq iOS experimental engine toolchain, installed by the Soroq CLI (soroq toolchain install ios), plus a
Which toolchain. Install it with
soroq setup, which reads the signed compatibility catalog and pins the frontend/toolchain pair the release lane was verified against. Do not pick a toolchain by name: the engine entrypoints this package depends on (soroqTransitionBatchByIdentity) exist only in the toolchain series the catalog selects, and choosing another one fails the iOS build withMethod not found: 'soroqTransitionBatchByIdentity'— which surfaces as a genericCommand PhaseScriptExecution failedand looks like a signing problem. thin app-supplied [SoroqEngineActivator] engine binding (bytes→module, index→redirect, rollback) — the one primitive that can't live in a pub.dev package because it uses the SDK-bundleddynamic_modules. This is package-driven hard OTA with a required iOS engine-binding activator — NOT "zero app-side glue". See the copy-paste activator + wiring in doc/ios_engine_activator.md.
- Also includes the lower-risk iOS config/data OTA snapshot lane (signed JSON config/data, not code-push).
Honest scope: iOS hard OTA is experimental (custom engine, experimental_profile,
Apple Guideline 2.5.2 distribution risk). This package does not claim App Store approval, and it is NOT a general
arbitrary-Dart hot-patch system — it is experimental custom-engine OTA only.
What It Includes #
Soroqfacade +SoroqEngineLaneController— the pure-Dart iOS engine-lane hard-OTA state machine, plus theSoroqEngineActivatorbinding interface- a reusable Dart API for the
soroq/engine_runtimemethod channel - runtime info, cold-start state, and auto-update state models
- Android startup wiring through a manifest-merged init provider
- native startup/update coordinators and a bundled arm64 Android JNI runtime
- iOS config/data OTA snapshot storage through the Flutter method channel
- re-exports of the pure Dart
soroq_sdkmodels and client
For the smallest public onboarding flow, start here:
- docs/public-beta-quickstart.md — the unified fresh-developer beta quickstart across all lanes
- docs/public-alpha-android-quickstart.md — the Android deep-dive
For Android build-stack readiness, use the repo preflight:
source ../../scripts/engine_env.sh
../../scripts/check_soroq_android_toolchain.sh
The current public-alpha lane is documented in docs/android-toolchain-readiness.md.
Installation #
dependencies:
soroq_flutter: ^0.3.0
This is the latest local package target in this checkout. App teams should use
flutter pub add soroq_flutter so pub.dev resolves the latest published
plugin until the staged train is published. Repository maintainers should run
make package-release-train-status and follow its next_step for the
immediate publish/proof action; use publish_plan only when the train is
staged for release.
Basic Setup #
Add a project-level soroq.yaml:
app_id: com.example.app
channel: stable
Import the package from Dart:
import 'package:soroq_flutter/soroq_flutter.dart';
Then use SoroqEngineRuntimeBridge to inspect runtime state, read startup
results, and configure auto-update behavior.
On Android, the zero-touch startup provider owns configuration generated by
soroq init. When a later Play Store build bundles changed update settings,
package-owned settings migrate automatically without clearing app data or
discarding staged/active patch state. A runtime call to configureAutoUpdate
marks the settings as developer-owned, so packaged defaults never overwrite an
explicit endpoint, client id, track, trust key, or enabled state.
Device Patch Status #
Apps and dashboards should avoid vague ready or live labels by themselves.
Read SoroqEngineRuntimeBridge.getAutoUpdateState() and display:
state.devicePatchLabelfor the lifecycle phasestate.devicePatchIdentityLabelfor the concrete patch number and idstate.devicePatchDetailLabelfor the operator-facing explanation
For example, an active patch can render as Active patch #13 on this device
with identity Patch #13 (patch-live-13). A staged patch can render as
Staged patch #14 - restart required with identity
Patch #14 (patch-staged-14).
For operator handoffs that reconcile hosted patch identity with device state, use docs/operator-patch-identity-handoff.md.
The public CLI flow around this package is now:
soroq init --app-id com.example.app
soroq status --check
soroq app create --name "Example App" --if-not-exists
soroq app list
soroq app status
soroq inspect android --artifact /absolute/path/to/app-release.aab
soroq release android
soroq release list --app-id com.example.app
soroq release status --release-id my-release
soroq patch android
soroq preview android
soroq patch config --config-file /absolute/path/to/config.json --release-id my-release
soroq patch list --app-id com.example.app --channel stable
soroq patch status --patch-id my-patch
soroq patch health --patch-id my-patch
soroq patch rollout --patch-id my-patch --percent 25
soroq patches set-track --patch-id my-patch --track stable
soroq rollback --patch-id my-patch --verify
soroq status should show release ready: yes and patch ready: yes before
you run the release or patch commands. Those commands use the same local project
preflight and will stop early if app_id, channel, or the package dependency
shape is invalid. Add --check to make that readiness check fail CI or local
scripts until the project is ready.
The short release command runs flutter build appbundle --release, discovers
the Android artifact from the usual Flutter output directories or
release-candidates/, then records an immutable copy under
.soroq/releases/.... The short patch command runs the same release build,
uses that recorded base, and discovers the newest compatible candidate
artifact. Explicit --artifact, --base-artifact, --candidate-artifact,
--release-id, and --build=false remain available for CI and unusual build
layouts. Flutter build flags can be passed after --, for example
soroq patch android -- --flavor internal --dart-define=API_ENV=prod.
soroq preview android gives you a release preflight: it resolves the
hosted Android release, verifies the downloaded release artifact metadata, runs
runtime patch-check, can download the signed patch artifacts, and can install
or launch the release on an explicitly selected Android device or emulator.
For staged rollout, use soroq patch rollout --percent N. For staged
tracks, publish or preview with --track staging or a custom track such as
--track beta, then promote with
soroq patches set-track --patch-id <patch-id> --track stable.
Config Patches #
Because this package re-exports soroq_sdk, Flutter apps can consume hosted
JSON config patches without manually reading the patch bundle. For apps that
want persistence and boot-report receipts, use SoroqConfigOtaController:
final client = SoroqControlPlaneClient(
baseUrl: Uri.parse('https://api.example.com'),
);
final controller = SoroqConfigOtaController(
client: client,
request: const SoroqPatchCheckRequest(
appId: 'com.example.app',
runtimeId: 'runtime-fingerprint',
currentPatchNumber: 0,
channel: 'stable',
clientId: 'example-client',
),
store: const SoroqMethodChannelConfigOtaStore(),
validator: (patch) {
return patch.config['enabled'] is bool ? null : 'enabled must be a bool';
},
);
final result = await controller.checkAndApply();
if (result.status == SoroqConfigOtaStatus.accepted) {
final config = result.snapshot!.config;
// Apply values through behavior already present in the app binary.
}
Config patches are download_only: Soroq delivers and parses the payload, then
your app chooses how to validate and apply it. On iOS, the public plugin's NATIVE
side is scoped to config/data OTA state and snapshot storage and does not bundle
or link an iOS runtime patching framework — the Android-style native executable-OTA
methods return ios_code_ota_unavailable on iOS by design. iOS hard OTA is
delivered differently: by the package's pure-Dart engine-lane state machine
(SoroqEngineLaneController / Soroq) driving the soroq-patched iOS engine through a
minimal app-supplied activator — no native runtime framework in this pod.
For a runnable iOS consumer, see examples/soroq_ios_config_harness.
What Is Proven Today #
- Android cold-start bootstrap through the shared plugin/runtime layer
- hosted patch-check and boot-report flows
- staged asset/config OTA on the Android public-alpha lane
- hosted release/AOT code patching for supported Soroq-compatible Android artifacts
- packaged Android runtime JNI libraries for
armeabi-v7a,arm64-v8a, andx86_64 - iOS config/data OTA consumer support through the public Flutter plugin
- iOS engine-lane HARD OTA (EXPERIMENTAL) driven by the package's pure-Dart
SoroqEngineLaneController/Soroqfacade — device-proven (base → apply → rollback → tamper-refused) on the Soroq iOS experimental engine toolchain; requires a minimal app-supplied engine activator. NOT a public iOS App Store approval claim. runtime_managed_dartprotocol/model support for the continuing research lane; this is not a public iOS App Store/TestFlight claim
Compatibility Truth #
This package is not a claim of universal stock-Flutter compatibility yet.
The current real OTA lanes are validated against a Soroq-compatible pinned Flutter toolchain, including tracked Flutter framework/engine patch seams in this repository. If a team is on a different Flutter version, they should use an explicitly supported Soroq toolchain release rather than assuming arbitrary cross-version compatibility.
In practical terms:
- the pure Dart control-plane layer is easier to share broadly
- the Android plugin/runtime layer is more version-sensitive
- the full OTA shipping path currently depends on Soroq-maintained Flutter fork/patch alignment
Status #
This package is real and proven on Android (asset/config OTA + supported
release/AOT code patching) AND drives the EXPERIMENTAL iOS engine-lane hard OTA
(pure-Dart state machine + minimal activator, on the Soroq iOS experimental engine
toolchain), plus the lower-risk iOS config/data OTA lane. iOS hard OTA is
experimental (experimental_profile, custom engine, Apple Guideline 2.5.2
distribution risk) — NOT an App Store approval claim, NOT a general arbitrary-Dart
hot-patch system. Arbitrary stock-Flutter version support is tracked through the
Soroq compatibility matrix rather than assumed for every Flutter toolchain.
Published Consumer Proof #
Repository maintainers can verify from the repository root that pub.dev consumers are not relying on local path dependencies:
make package-post-publish-proof
That target checks published package versions and archive freshness, then
creates fresh Dart and Flutter apps, installs soroq_sdk and soroq_flutter
from pub.dev, imports the public APIs, runs Flutter analysis, builds an Android
debug APK from the published plugin, and finishes the non-device public-alpha
readiness proof.
Because it installs from pub.dev and finishes the public-alpha readiness proof, it requires network access to pub.dev and the hosted Soroq public-alpha services after both packages are published.
Local Repo Development #
This repository may use a local pubspec_overrides.yaml while developing both
packages together. Published consumers should depend on the pub.dev package
versions, not the local override.
The published Android plugin bundles the arm64 Soroq runtime JNI library so a
fresh Flutter app does not need this repository's runtime/ checkout just to
build. Repository maintainers can force a local Rust rebuild with
SOROQ_BUILD_RUST_JNI=1. The Gradle rebuild path remaps local filesystem
prefixes out of Rust diagnostics so regenerated JNI artifacts do not leak
developer machine paths into the package.
Internal Proof Helpers #
For repository-local validation, the repo still includes the existing proof helpers:
source ../../scripts/engine_env.sh
../../scripts/verify_zero_touch_consumer_app.sh
source ../../scripts/engine_env.sh
../../scripts/verify_zero_touch_consumer_aot_code_patch.sh
source ../../scripts/engine_env.sh
../../scripts/verify_zero_touch_consumer_aot_rollback.sh