soroq_flutter 0.3.1
soroq_flutter: ^0.3.1 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.flutter-io.cn 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.1
This is the latest local package target in this checkout. App teams should use
flutter pub add soroq_flutter so pub.flutter-io.cn 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.flutter-io.cn 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.flutter-io.cn, 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.flutter-io.cn and finishes the public-alpha readiness proof, it requires network access to pub.flutter-io.cn 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.flutter-io.cn 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