Publish a release
Submit a release through the dashboard, understand each field in the publish form, and operate the validation lifecycle with confidence.
Publishing in Otalan means creating one release for one exact tuple and asking Otalan to validate the uploaded archive before activation.
On Otalan Cloud, validation is handled by Otalan.
The dashboard is designed for the first operational phase of a product: a release engineer or product engineer can publish deliberately, inspect the result, and roll back quickly if needed.
Use this page when you are operating the dashboard publish form. For the first end-to-end release path, start with
Before you publish
Make sure all of the following are already true:
- you selected the correct organization and project
- the target app is registered and active on the Apps page
- you ran
otalan bundlein the app repository - the generated
.otalan/bundle/bundle-<bundle-id>.zipfile is ready for dashboard upload - you know whether this publish is for Capacitor or Expo, plus the target
platform,channel,runtimeVersion, andbundleId - no product images, videos, large documents, or other externally hosted media are embedded in the bundle
Field-by-field guide
App
Choose the registered app that owns the release. App registration is project-scoped, so the publish form only lists apps for the selected project.
Archived apps are not valid publish targets until restored.
App runtime
After selecting the app, choose whether this release is for a Capacitor app or an Expo app using expo-updates.
This choice controls the publish form. Expo releases must attach the generated Otalan manifest JSON for validation. Capacitor releases do not show that field.
The runtime choice does not block older versions by version number. Other runtimes and older Expo or Capacitor versions might work or might not, but they are not supported for the moment.
Platform
Choose the client platform that should receive the bundle. Keep iOS and Android releases separate even if the web code is similar.
Channel
Use the channel to separate audience slices such as:
productionbetainternal
Channels are independent release lines.
Runtime version
This is the compatibility boundary. A client only receives bundles that match the exact runtime version line it reports.
For Capacitor, this is the installed runtime version. For Expo, this field maps to the runtime version used for compatibility.
Bundle ID
This is the identifier for the individual web release. It should be unique for the target tuple and understandable to operators scanning the bundle history later.
ZIP archive
When publishing from the dashboard UI, upload the .otalan/bundle/bundle-<bundle-id>.zip file generated by otalan bundle. The archive is validated before activation.
Keep this ZIP minimal. Content-heavy media should live behind your external hosting and be referenced by URL, not embedded in JavaScript, CSS, base64 strings, or the release ZIP.
Expo manifest JSON
This field is required after choosing the Expo runtime. Paste the generated Otalan .otalan/bundle/manifest.json content, or drop the manifest.json file directly onto the field so Otalan can validate the Expo release metadata.
Otalan uses the generated manifest to serve Expo clients correctly.
Mandatory update
Use this flag when the client should treat the update as mandatory according to the runtime behavior implemented on your side.
Rollout percent
Use rollout to limit exposure:
100means full rollout- smaller values create a staged rollout
Partial rollout cohorts are seeded by bundle ID and stable device identifier, so each bundle gets its own deterministic cohort. Partial rollout works best when the client sends the same stable device identifier on every update check.
Release notes
This is optional, but it is useful for auditability and operator context. Good release notes make future rollback decisions easier.
What happens after you submit
The publish flow has two phases:
Phase 1: Upload
The dashboard and otalan publish handle upload details automatically.
Phase 2: Validation and activation
Otalan then:
- validates the archive
- rejects unsafe or invalid content if necessary
- for Expo releases, validates the generated Expo metadata
- activates the release for the selected tuple when validation succeeds
Storage shown in the dashboard is the stored release footprint, not only the uploaded ZIP. Capacitor releases count the promoted archive. Expo releases count the promoted archive plus the extracted launch asset and listed asset objects persisted from the generated manifest.
Publish statuses
The dashboard reports publish status so operators can see whether they should wait, retry, or investigate. The Publish page shows ongoing publishes and recent failures, including the stored failure reason. While validation is still active, the Bundles and Rollback pages also show the submitted bundle as verification in progress.
Use Clear on the Publish page to remove failed activity entries after cleanup is finished. Active validations stay visible until they succeed or fail.
pending: the publish is waiting for validationprocessing: Otalan is validating the archiveready: validation succeeded and the release is activefailed: validation failed and the release was not activated
If validation fails, the previously active bundle remains unchanged.
For Expo releases, the generated manifest tells Expo where to fetch the active release assets.
Verify the release in the app
Publishing makes the bundle active on the Otalan side. The installed app still has to perform an update check before it can receive the new release.
For Capacitor clients, trigger whatever path in your app calls POST /capacitor/check. In many apps that means fully closing and reopening the app, because the update check runs on startup. If your app exposes a manual "check for update" action or a development trigger, use that instead.
When updateAvailable is true, the response includes the matched appId, platform, channel, and runtimeVersion alongside the bundle details.
For Expo clients, trigger the app flow that asks expo-updates to check the Otalan updates endpoint.
Before widening a rollout, run the complete update path in a local or staging environment and rehearse rollback to a known-good bundle for the same tuple. Otalan can validate and activate the archive, but it cannot prove your app-specific startup, plugin, data migration, or third-party runtime behavior is safe.
If the app does not receive the new bundle after the publish status is ready, verify these values before rebuilding the native app:
appIdplatformchannelruntimeVersion- rollout percent and device assignment
You only need a new native build when the change depends on native code, native plugins, permissions, or a different runtime version.
Production verification
Before widening a production rollout, verify the full client path:
- update A received by one real iOS device
- update A received by iOS and Android simulators
- update B received by the same device and simulators, proving clients are not stuck on the first bundle
- rollback to the previous known-good bundle received by the same device and simulators
- matching tuple values recorded for every check:
appId,platform,channel,runtimeVersion, andbundleId - status confirmed in Otalan after each publish or rollback
Keep the release artifact small and keep verification notes free of API keys, project secrets, and customer data.
Rollout and rollback guidance
Use staged rollout whenever the change has meaningful risk.
Examples:
10for a canary release25for a wider initial deployment100for a full release after confidence is established
If a release misbehaves, you can:
- pause the rollout
- resume it later
- roll back to a previously published bundle for the same tuple
Rollback is only safe within the same exact compatibility tuple. That is a feature, not a limitation.
Rollback can only reactivate a bundle that already exists in Otalan. If the target version is not listed on the Bundles page for that tuple, it was not registered there and cannot be used as a rollback target.
The Bundles page shows the original published timestamp for each bundle. Rollback keeps that displayed timestamp unchanged.
Recommended manual validation loop
For an operator-driven release, a practical checklist is:
- confirm the tuple values
- run
otalan bundleand upload.otalan/bundle/bundle-<bundle-id>.zipthrough the dashboard - watch the publish status until it reaches
ready - verify the bundle appears in the bundle inventory, using the runtime type filter to view Expo and Capacitor releases separately when needed
- restart the app or trigger its manual update check
- verify the client receives the expected release
- rehearse rollback to a known-good bundle for the same tuple
- increase rollout if you started with a staged percentage
The bundle inventory channel filter accepts glob-style * and ? patterns. For example, ota* includes channels such as ota, ota-beta, and ota-production.
That loop is fast enough for early-stage operations and strict enough to catch most mistakes before a release reaches the full target audience.