Release & recovery
Signed patch engine
Deliver signed replacements for logic, surfaces and assets that your installed app already registered. The app verifies the artifact against its original binary contract, stages it, and activates it on a later launch. Keep a compiled fallback and use signed rollback when a patch needs to come off devices.
What can change
| Change | Patch support |
|---|---|
| Registered module logic | Supported within the engine's restricted language and the module's existing input/output contract. Flutter also supports explicitly opted-in @Patchable top-level functions in its supported pure int/bool/String subset. |
| Registered surfaces | Supported using components and actions already shipped in the app: Flutter, native SwiftUI, or native Android Compose. A patch cannot introduce arbitrary widgets or native views. |
| Registered assets | Supported when the application reads those registered asset IDs through the patch runtime. This does not replace arbitrary files throughout the app bundle. |
| New contracts, plugins, permissions or native code | Ship a new app binary. Unregistered Dart, Kotlin, Java, Swift, Objective-C, engine updates and irreversible data migrations are not general hot reload. |
An app that shipped without the patch runtime and matching registrations needs an ordinary app release first. A successful compile does not establish distribution eligibility: use the intended distribution profile and qualify the exact app version, channel and patch class. Store policies remain separate from technical patchability.
Install the published packages
Flutter on Android and iOS
dependencies:
bug_capsule: 0.4.1-beta.10
bug_capsule_patch: 0.1.0-dev.1
Run flutter pub get. The patch package includes its native runtime: Android arm64-v8a, armeabi-v7a and x86_64; iOS arm64 and Simulator arm64/x86_64. It requires Dart 3.9+, Flutter 3.35+, Android API 23+ or iOS 15+. Start with an isolated test application. See the published Flutter patch package and runnable Flutter patch lab for integration.
Native iOS
.package(
url: "https://github.com/BugCapsule/bug_capsule_ios_sdk.git",
exact: "0.4.1-beta.5"
)
// Select these products in your iOS target:
.product(name: "BugCapsule", package: "bug_capsule_ios_sdk"),
.product(name: "BugCapsulePatch", package: "bug_capsule_ios_sdk")
The canonical repository-root SwiftPM package includes both products and the required XCFramework; no nested local package is needed. Patch APIs require iOS 15+. Core macOS support does not imply macOS patch support. Follow the iOS SDK guide and its PatchLab example.
Native Android publication
The native Android core/patch/Compose release bundle is prepared, but the new native patch artifacts have not been published to Maven Central. Do not add unpublished native Maven coordinates to a customer build. This does not block Flutter Android: the published Flutter patch package already carries its JNI libraries.
CLI and compiler
npm install -g @bugcapsule/cli@0.4.0-beta.5
bugcapsule --version
bugcapsule patch doctor --json
The npm wrapper installs the standalone CLI 0.4.1-beta.10. Patch compilation uses its embedded compiler and needs Node.js 24+; you do not need a separate patch-engine checkout for the standard workflow. --engine is an advanced explicit override. The CLI guide covers installation and login; platform builds still need their normal Flutter, Android or Xcode tools.
Prepare project trust and the original app
Set up patch trust
Sign in to the dashboard, select your project, and open Emergency Recovery → Patch management → Setup. A person with project-update permission creates root trust keys and a backend channel-signing key. Pin at least two root public keys in the original app. Register the public half of your artifact-signing P-256 key as an external artifact public key; its private key stays in your signing environment.
Create a scoped delivery credential
Choose the exact application ID and environment. Save the one-time private configuration download securely. Delivery credentials only fetch patches and send receipts; they cannot upload artifacts, manage a rollout or approve one. Never embed a personal token or a signing private key in the app.
Register contracts and fallbacks in the app
Define the modules, surfaces, actions and asset IDs the original binary supports. Integrate the SDK's patch binding with
BugCapsuleConfig, using the immutable base build ID, application/environment, installation identity and pinned roots. Use the SDK'sPatchUpdaterwithHttpPatchTransportfor delivery. The Flutter README and iOS README are the API references; copy a matching platform example rather than inventing a second runtime integration.Report actual runtime health
Staging is not activation. Relaunch the same installed app to activate the staged generation, check that the app is usable, then call the SDK's
markHealthyand flush its receipts. Keep the runtime store across launches. Reinstalling or clearing that store is not a rollback test.
Register the immutable base
For CLI reads and uploads, issue a personal token in Settings → Keys → New personal token. Keep project.read and explicitly select patches.read and patches.upload. These patch scopes are unchecked by default and limited by your role. The one-time private CLI configuration download avoids sharing the token in chat. Normal browser-login defaults do not automatically grant patch upload rights.
bugcapsule patch init --platform flutter
bugcapsule patch doctor --online --json
bugcapsule patch base register base-manifest.json --json
For native apps, use --platform ios or --platform android. Initialization creates bugcapsule.patch.json, an empty patch/registered.json and patch/modules; it does not make an app patchable or overwrite an existing configuration.
Populate the inventory from the contracts actually compiled into the app. The base manifest identifies applicationId, platform (flutter_android, flutter_ios, android or ios), baseBuildId, appVersion and modules, with its registered surfaces/assets and other binary capabilities where applicable. Keep the base and its build provenance with the app release. A marketing version is not an immutable base build ID. See the patch CLI reference for the complete manifest/workflow contract.
The registered inventory, runtime inventory and signed artifact must agree. For example, a binary that registers a summary surface cannot accept a module-only base that omits that surface. Correct the app/base contract instead of weakening verification.
Classify, build and sign
Commit the intended changes and use a clean checkout. Replace the uppercase identifiers below with your project's values. Keep the signing key outside the checkout; author-key must match the external public key registered in Setup.
bugcapsule patch classify \
--registered patch/registered.json --repo . --base-rev BASE_COMMIT --json
bugcapsule patch build \
--base INSTALLED_BASE_ID --source patch/modules \
--repo . --base-rev BASE_COMMIT --project PROJECT_UUID \
--patch-id coupon-fix --sequence 1 --profile internal \
--key /secure/location/author.pem --key-id author-key \
--out /tmp/coupon.bcpatch --json
bugcapsule patch inspect /tmp/coupon.bcpatch --json
The classifier examines all changed paths between the base commit and the selected head. STORE_RELEASE_REQUIRED means ship an app release; UNKNOWN means patchability has not been established. Neither result is an approval to force a patch. The build verifies the immutable base's capabilities and host ABI and refuses omitted patchable files. Select surface documents and assets explicitly with repeatable --surface and --asset options.
The default internal profile binds the signed artifact to committed source and emits coupon.bcpatch.source-diff.json. Use the distribution profile appropriate to your actual app. Explicit development artifacts have different source provenance; do not present a development transport test as production qualification.
Upload and validate the exact artifact
bugcapsule patch upload /tmp/coupon.bcpatch \
--source-diff /tmp/coupon.bcpatch.source-diff.json --json
Retain the returned artifact ID and upload checkpoint. Repeating the same upload resumes with stable request identities; changing the bytes or source review requires a separate checkpoint. An archived successful upload does not prove the artifact is still eligible for deployment.
In Patch management → Artifacts, inspect the identity, classification and immutable source review. Create validation for that exact artifact and the relevant captured failures. Run baseline, fixed-artifact and regression replay on the real target application. Existing replay/verify commands accept --patch-artifact, --patch-keyring and --patch-keyring-signature to pin the fix. Keep the archived delegated-key metadata with the artifact. Follow replay and verification and the exact evidence workflow.
Creating validation returns PENDING, not a test pass. Submitting evidence records real replay results; it does not run tests or fabricate outcomes. A source diff, successful build or successful HTTP request is not replay evidence.
Review, approve and deploy
Use the dashboard to create a deployment plan for the exact artifact and environment, review validation and any required distribution qualification, and request delivery. Complete the project's approval workflow, including a second person where required. Personal/service upload tokens cannot approve or manage deployments.
The command equivalents below require an explicitly authorized person-session workflow; they are not authorized by the upload token above. For routine operation, use the signed-in dashboard. See the repository's authentication and command reference for your approved CLI setup.
bugcapsule patch validate ARTIFACT_UUID --capsule CAPSULE_UUID --json
bugcapsule patch validate VALIDATION_UUID --get --json
bugcapsule patch plan ARTIFACT_UUID \
--environment staging --stages 100,500,2500,10000 --json
bugcapsule patch deploy DEPLOYMENT_UUID \
--expected-version CURRENT_VERSION \
--reason 'Reviewed exact validation and source' --json
bugcapsule patch status DEPLOYMENT_UUID --json
Plan creation never publishes. deploy requests delivery and may return approval_required; it does not approve itself. Stages use basis points: the example is 1%, 5%, 25%, then 100%. Read the current deployment version before a mutation. A version conflict requires reviewing fresh state, not blindly substituting a newer number.
Watch actual activation, healthy receipts and monitoring evidence before expanding a cohort. Offline, stale and never-reporting installations limit what the dashboard can conclude. Zero reports is not a healthy fleet.
Publish and confirm rollback
Use the deployment's dashboard rollback action to return to the bundled base or a previously approved digest. A permitted person-session CLI workflow can request the same operation:
bugcapsule patch status DEPLOYMENT_UUID --json
bugcapsule patch rollback DEPLOYMENT_UUID \
--expected-version CURRENT_VERSION --to-base \
--reason 'Observed regression' --json
bugcapsule patch status DEPLOYMENT_UUID --json
Use exactly one of --to-base or --to-digest sha256:…. --revoke additionally requests revocation of the faulty artifact. A signed rollback publication is not confirmation that every device has applied it: let the same installation fetch it, relaunch without clearing its store, and confirm the resulting identity and receipts. Check pending rollback and stale-report counts. Zero pending among reporting installations does not cover devices that have never reported.
Read command outcomes
Patch commands emit JSON results. Exit 0 means the command was accepted or the read completed, not that devices activated a patch. Exit 2 means validation or approval is still pending; 3 includes store-release requirements, failed/inconclusive validation and conflicts; 4 is unknown patchability; 77 is an authentication or permission problem. Read the returned state and reason. Keep artifact IDs, immutable base identity, validation evidence and rollout receipts together so a later review can reproduce the decision.