金曜の午後にチェックアウト機能のデグレが見つかりました。修正はすでに Capacitor の Web レイヤーに適用済みですが、App Store の審査にはあと3日かかります。Android ユーザーならすぐに新しいパッケージを受け取れますが、JavaScript の修正だけで全ユーザーに Native Build の再インストールを強いるのは非効率です。
これこそが、チームが CapacitorJS アプリの OTA アップデートを学ぶ実用的な理由です。制御された OTA リリースにより、署名済みの Web bundle を対象のバイナリに配信し、バックグラウンドでダウンロードして次回の起動時に適用できます。その間、ネイティブ層の変更は通常通りストアのプロセスに従います。難しいのはファイルのアップロードではありません。バージョン管理、署名、ロールアウト制御、オブザーバビリティ、そして稼働中のフリート全体でのリカバリをいかに整合させるかです。
目次
- CapacitorJS チームにとって OTA が重要な理由
- 前提条件と Capgo アップデーターのインストール
- 最初のOTA Bundleを安全にデプロイする方法
- Channel、Rollout、およびCI/CDの自動化
- テスト戦略と自動ロールバック
- 署名、セキュリティ、およびOTAの制限事項
- リリース前の運用チェックリスト
CapacitorJS開発チームにとってOTAが重要な理由
Capacitorアプリは通常、2つのリリース対象を持っています。ネイティブバイナリには、プラットフォームのシェル、権限、プラグイン、アイコン、entitlements、その他ストアの審査対象となる機能が含まれます。一方、ウェブレイヤーにはJavaScript、CSS、HTML、ルーティング、テキスト、アセットが含まれます。OTAアップデートは後者のレイヤーを対象とするため、変更内容がCapacitorのガイドラインに沿っている限り、ネイティブシェルを再ビルドすることなく、壊れた決済フローなどを修正できます。 CapgoのOTAガイドライン.

運用上のメリットはスピードだけではありません。インストール済みのバイナリは必ずしも同期していません。古いネイティブビルドを使用しているユーザーもいれば、最新のストア版を使っているユーザーもおり、バイナリごとにウェブバンドルが利用できるネイティブAPIが異なる場合があります。サポートされていないネイティブAPIに対してコンパイルされたバンドルは、JavaScriptの差分が正しくても失敗する可能性があります。そのため、OTAはリリースエンジニアリングの回避策ではなく、互換性を管理するためのシステムなのです。
アップデーターをデリバリーパイプラインとして扱う
本番環境のセットアップには、明確な手順が必要です:
- ネイティブブリッジのインストール: アップデーターパッケージを追加し、Capacitorを同期してiOSとAndroidに認識させます。
- バンドルの署名: 署名用マテリアルはリポジトリの外で管理し、検証をクライアントパスの一部として組み込みます。
- チャンネルの指定: staging、beta、production、または顧客ごとのコホートへ意図的にルーティングします。
- デリバリーの自動化: テスト合格後にのみ、CIでbuild、sign、upload、promoteを行うようにします。
- 採用状況の監視: channelやbinaryごとに、download、install、boot、crash、およびヘルス信号を追跡します。
- 迅速なロールバック: サーバー側で正常なbundleを復元し、クライアント側のリカバリ保護を維持します。
Capgo は、このワークフローを実現する一つの選択肢です。オープンソースの Capacitor updaterとクラウドサービスにより、署名済みweb bundleをターゲットのchannelへpublishし、次回の起動時に適用、さらにバージョンやデバイスレベルのデリバリー情報を公開します。可用性とリリースのレジリエンスを検討しているチームは、以下も確認できます。 Capacitor アプリケーションの可用性.
実践ルール: bundleがどのnative binaryをターゲットにしているか特定できない場合は、そのbundleをproductionへpromoteしないでください。
OTAの成功とは、web code とnative code の境界を隠すことなく、不要なストアリリースを減らせるかどうかです。重要なのは、チームがbundleをpushできるかではなく、「誰がそれを受け取り、なぜ対象となったのか、起動後に何が起きたのか、そしてbundleに問題が発生した際にどうサービスを復旧させるか」を説明できるかどうかです。
事前準備と Capgo Updater のインストール
ターミナルを開く前に、プロジェクトとリリース用アカウントの準備ができているか確認してください。Capacitor 5 または 6 のアプリケーションに、 @capacitor/cli 初期化済みの環境、Node 18以降、ターゲットバイナリ用の有効な Apple Developer および Google Play Console アカウント、そして Capgo クラウドアカウントが必要です。 appId API キーが必要です。
最初のインストールは、意図的に最小限の構成になっています:
npm install @capgo/capacitor-updater
npx cap sync
npx @capgo/cli init
npm install JavaScript パッケージとネイティブの依存関係を追加します。 npx cap sync 多くの人がスキップしがちなステップですが、これを怠るとネイティブブリッジがリンクされません。JavaScriptレイヤーはコンパイルできても、実行時にインストール済みバイナリ内に存在しないブリッジを呼び出そうとしてしまいます。インストール後、およびネイティブプラグインの設定を変更した際には、必ず同期を実行してください。
Initializer は、あなたの capacitor.config.ts を、アップデーターの設定、Webエンドポイント、およびデフォルトのチャンネルでパッチします。パッチをそのまま受け入れるのではなく、ファイルを開いてアプリケーションの識別情報とアップデートの契約内容を明示的に確認してください:
const config: CapacitorConfig = {
appId: 'com.example.app',
appName: 'Example',
version: '1.0.0',
autoUpdate: true,
updateUrl: '',
capgo: {
channel: 'staging'
}
}
生成される構造はプロジェクトや CLI のバージョンによって異なる場合がありますが、重要な値は共通です。 appId は、インストールされたバイナリと一致している必要があります。 version は、Native Build との関係性を記述している必要があります。 autoUpdate リリース方針に沿っている必要があります。 updateUrl バイナリが信頼するサービスを指している必要があり、また capgo ブロックで初期チャネルを特定する必要があります。
リリースビルド前の検証
XcodeやAndroid Studioを開く前に、診断コマンドを実行してください:
npx @capgo/cli doctor
正常な結果であれば、CLI が利用可能であること、プロジェクト設定が読み取り可能であること、updaterパッケージが検出されていること、必要なアプリ識別子が存在すること、および認証が設定済みのアカウントに到達できることが確認されます。native syncの欠如、アプリ識別子の不在、またはupdater設定の無効といったエラーが表示されないことを確認してください。

最初のNative Buildは、あえて機能を最小限に抑えておきましょう。実機のiOSおよびAndroidデバイスにインストールし、ネットワーク接続状態で起動、一度終了してから再起動し、通常の起動プロセスに影響を与えずにupdaterがチェックを行えることを確認してください。 Capacitor updaterのインストールワークフロー プロジェクトの設定が期待通りのプラグイン構成になっているか比較したい場合に便利です。
最初のOTA Bundleを安全にリリースする方法
最初のbundleは新機能のリリースではなく、リリースパスのテストとして扱ってください。アーティファクトを準備する前に、署名キーの作成またはローテーションを行ってください:
npx @capgo/cli key create
秘密鍵は必ずシークレットマネージャーで管理してください。リポジトリへのコミット、プロジェクトアーカイブへの保存、CIのログへの露出は厳禁です。ネイティブ側の信頼パスには公開検証用データのみが必要です。秘密鍵にアクセスできるのは、署名ジョブのみに限定してください。
JavaScript側のバージョンを以下で変更します: package.json または、Webリリースのメタデータを変更します。Webのみの修正であれば、ネイティブのバージョンフィールドは変更しないでください。OTA bundleではネイティブの code を追加したり、インストール済みのバイナリで宣言された機能を変更したりすることはできません。ネイティブのバージョンを変更してしまうと、互換性が向上するどころか、かえって不明確になってしまいます。
署名済みアーティファクトを対象のチャンネルにアップロードします:
npx @capgo/cli bundle upload --channel production
アップロードの成功は、サーバーがリクエストを受け付けたことを確認するに過ぎません。必ずコマンドの出力を読み、bundle identifier、対象となるnative-versionの制約、署名済みチェックサム、およびチャンネルの割り当てを確認してください。これらのフィールドがアーティファクトを特定し、インストール済みのバイナリがその更新を受け取れる状態にあるかを示します。
リリースゲートとしてダッシュボードを活用する
bundleの詳細ビューでは、リリースの成否を判断するために以下の4つの項目を確認してください:
- 最小ネイティブバージョン: このbundleを実行できる最も古いバイナリはどれか?
- 最大ネイティブバージョン: 意図的に除外されている新しいバイナリはどれか?
- チャンネル: どのユーザー層にアーティファクトを公開しますか?
- ロールアウト率: 対象ユーザーのうち、何%に配信しますか?
本番環境への公開開始日時: 5%, 次に展開に必要な判定基準を定義します。ダウンロードとインストールの成功、正常なコールドスタート、クラッシュのないセッション、および変更後のフローにおけるJavaScriptエラーを確認します。Capgo ダッシュボード上で、アーティファクトの識別情報と配信コントロールをまとめて確認できます。

デバッグビルドを使用して、デバイスのパスを検証してください。 getCurrent() アクティブなbundleをレポートし、 notifyAppReady() 新しいbundleが正常な状態に達したことを確認します:
import { CapacitorUpdater } from '@capgo/capacitor-updater'
const current = await CapacitorUpdater.getCurrent()
console.log(current)
await CapacitorUpdater.notifyAppReady()
アプリがヘルスチェックを通過できる程度に初期化されてから、readinessを呼び出してください。readinessが確認されない場合、次回のコールドスタート時に自動リカバリによってbundleが失敗と判定される可能性があります。このセーフガードは本番ユーザーを保護するためのものですが、不完全なテストが配信の問題として誤検知される原因にもなります。展開範囲を広げる前に、各テストデバイスのアクティブなbundleと起動結果を記録しておいてください。
Channel、Rollout、およびCI/CDの自動化
Channelは、公開されたbundleとインストールされたデバイスを繋ぐルーティングレイヤーであり、リリースのゲート(関門)でもあります。staging channelは既知のNative Buildを指すべきであり、beta channelは制御されたコホートに対して配信し、productionはそれ以前のchannelでスモークテストに合格した後にのみ移行させるべきです。
明示的な名前でステージングパスを作成します:
npx @capgo/cli channel create staging
npx @capgo/cli bundle assign <bundle-id> --channel staging
そのチャンネルをテスト端末で使用しているNative Buildのバージョンに固定してください。テスターは、追加のプラグインや異なる設定を含むローカルの開発用ビルドではなく、本番環境と同じバイナリを実行する必要があります。スモークテストに合格したら、少し異なる別のbundleをアップロードするのではなく、テスト済みのアーティファクトをそのまま昇格(promote)させてください。
チャンネルマップは以下に配置し、 capgo.config.json アプリケーションの code と同様にレビューを行う必要があります。チャンネル名は安定させ、対象となるNativeの互換性範囲を明確にし、本番への昇格を明示的なCIアクションとして定義してください。デリバリー(配信)と並行して機能の公開(feature exposure)を管理するチームにとって、これらの フィーチャーフラグ管理のヒントは、 デプロイ権限とユーザーへの機能有効化を分離するための有用な方法を提供します。
昇格(promotion)をCIに連携させる
実用的な GitHub Actionsの設計には、2つのパスがあります:
- プルリクエスト: Webレイヤーをビルドし、制限されたプレビュー用クレデンシャルで署名して、一時的なプレビューチャンネルに公開します。プルリクエストがクローズされたら、そのチャンネルを削除または期限切れにします。
- タグ付けされたmainリリース: ユニットテストを実行し、本番用bundleをビルドして、Nativeバージョンの制約を検証し、アップロードします。テストジョブが正常に終了した場合のみ、昇格(promote)を実行します。
プロモーションコマンドの例は以下の通りです:
npx @capgo/cli bundle promote <bundle-id> \
--to-channel production \
--percent 5
以下のように、段階的に公開範囲を広げます: 25%、50%、100%各ステップの間にCIでの承認や監視ジョブを挟みます。パーセンテージやコマンドはあくまで制御手段であり、安全性を保証するものではありません。ビルドが通ることは、bundleの構文が正しいことを示すだけで、特定のNative Build、永続化されたローカル状態、低速な接続、あるいは古いセッションから復帰したデバイス上での挙動まで保証するものではないからです。
リリースの境界線: Web bundleはバイナリの互換性ウィンドウに属します。channelが、インストール済みのアプリに含まれていないNative APIを参照するcodeを配信するための抜け穴になってはなりません。
各bundleを、ビルド対象となった正確なネイティブバージョンに紐付けることで、TestFlightやAndroidの内部テスト用ビルドとの整合性を保ちます。Native Buildが変更された場合は、互換性を考慮した新しいbundleを公開するか、新しいchannelマッピングを作成してください。 Capgo GitHub Actions 連携ガイド を活用することで、そのポリシーを再現可能なワークフローへと落とし込むことができます。

テスト戦略と自動ロールバック
OTAリリースは、CIを通過してもアクティベーション後に失敗することがあります。その失敗は、bundle、Native Build、デバイスに保存された状態、あるいはネットワーク環境に依存する可能性があります。シミュレーターで画面のレンダリングを確認することはできても、すべてのインストール済みバイナリを網羅したり、起動失敗時に正常に復旧できるかまでを確認することはできません。
3つのテストフェーズを活用しましょう:
- CI bundleテスト: ユニットテスト、型チェック、Lint、および本番用Webビルドを実行します。単なるコンパイル確認に留まらず、変更したチェックアウト、認証、ナビゲーション、永続化パスまで動作確認を行います。
- プライベートデバイスチャネル: 以前のNative Buildが動作している実機を用いて、プライベートチャネルを構築します。両プラットフォーム、クリーンインストール、アップグレードインストール、および代表的な保存状態を網羅してください。
- プロダクション・カナリア: 少数の対象ユーザーから開始し、クラッシュレポートの連携とJavaScriptエラーのアラートを設定します。起動失敗は最優先事項として扱ってください。影響を受けたユーザーは、アプリ内エラーを報告するcodeに到達できない可能性があるためです。
この Capacitor OTAテストガイド テストの仕組みを解説します。運用ルールはシンプルです。各アップデートが保護対象とするNative Buildに対してテストを実施してください。
サーバー制御とクライアント復旧を分離する
サーバーサイドのロールバックは、新規ダウンロードを停止します:
npx @capgo/cli channel set production --bundle <previous-id>
これにより、対象デバイスが次に検出する内容が変更されます。すでにダウンロード済み、または各デバイスでアクティブなbundleが削除されるわけではないため、クライアント側にもリカバリ制御が必要です。起動失敗の条件を定義し、正常に動作することが確認されているフォールバックを保持して、自動的にロールバックするようにupdaterを設定してください。
信頼性の高いリカバリパスには以下が含まれます:
- ダウンロード失敗のトラッキング: 接続の問題と、無効なアーティファクトを区別します。
- インストール失敗のトラッキング: 解凍、検証、およびファイルシステムの不具合を検知します。
- 起動ヘルスチェックのトラッキング: アクティベーション後にアプリが使用可能な状態に到達したかを確認します。
- 自動フォールバック: 起動に繰り返し失敗する場合、以前の正常なbundleを復元します。
- 手動エスカレーション: サポートやエンジニアリングのために、デバイスおよびbundleの識別子を保持します。
大規模なフリート運用では、わずかな失敗率でも膨大なサポート負荷につながります。 OTAアップデートの成功率が99.95%であっても、100万台のデバイスにつき約1,000件の失敗が発生することを意味します。これは、 IoTフリート向けOTAテストのベンチマークによると、「30分以内のロールバック」「クラッシュ率0.1%未満」「サポート対象期間内のデバイス率98%」といったガードレールが示されています。これらはあくまでベンチマークの例であり、一律の合格基準ではありません。アプリのリスクとユーザーへの影響に応じて、エラーバジェットを設定してください。
署名、セキュリティ、そしてOTAで変更できないこと
署名のないアップデートは、配信エンドポイントをサプライチェーン攻撃の対象にしてしまいます。TLSは通信を保護しますが、クライアント側でも、ダウンロードしたアーティファクトが承認されたリリースプロセスから来たものであること、および転送中に改ざんされていないことを確認する必要があります。
アップデーターツールを使用してEd25519キーペアを生成します。秘密鍵はCIのシークレットとして保存し、公開検証鍵はアプリのビルド時にネイティブバイナリに埋め込みます。クライアントは、アクティベートする前に必ずすべてのbundleを検証する必要があります。CLIトークンは環境と権限スコープによって制限し、uploadおよびプロモーションのログを保持し、本番環境での操作には強力な認証を必須としてください。
キーのローテーションは、単なる設定変更ではなく、互換性のための移行プロセスとして捉えてください。置き換え用のキーを生成し、現在の公開鍵と置き換え用の公開鍵の両方を信頼するネイティブビルドを配信してから、新しい秘密鍵でリリースに署名します。古いキーは、互換性のあるネイティブバイナリが置き換え用のキーを採用した後にのみ削除してください。信頼を早すぎる段階で解除すると、古いバイナリが有効なアップデートを受け入れられなくなります。侵害されたキーを無期限に信頼したままにすると、ローテーションの意味がなくなります。
リリース計画で線引きする
OTA は Web レイヤーの変更を処理します。インストール済みバイナリにない機能に依存する変更には、ネイティブリリースが必要です:
| カテゴリ | OTA で配信 | ネイティブリリースが必要 |
|---|---|---|
| アプリの動作 | JavaScript のロジック、ルーティング、バリデーション、状態管理 | ネイティブプラグインの実装 |
| 表示 | CSS、HTML、テキスト、テーマトークン、互換性のある画像アセット | アプリアイコンとスプラッシュのリソース |
| プラットフォームへのアクセス | バイナリに既に含まれる機能への既存の Capacitor web-layer 呼び出し | 新しい権限、entitlements、native API |
| 設定 | 互換性のある web 設定とリモートコンテンツのルール | Info.plist, AndroidManifest.xml、署名 ID |
| ストアのメタデータ | ストア審査対象の動作やバージョンメタデータを変更するものはなし | ストアポリシーに影響するメタデータやバージョン番号の変更 |
この Capacitor updater の code 署名ドキュメント 検証モデルについて説明しています。改ざん検知可能な監査ログ、証明書検証、TLS、厳格なアクセス制御、およびバージョン偽装や鍵の漏洩に対する調査プロセスを運用に適用してください。
インストールされているネイティブ環境の構成は徐々に変化するため、互換性のターゲット設定は依然として重要です。Appleの報告によると、 iOS 26を利用しているアクティブデバイスの割合は66%で、 と 過去4年間にリリースされたデバイスに限るとiOS 26は74%に達しています。一方で、別のレポートではiOS 26の割合を サイクル後半の全デバイスの79%と予測しており、 モバイルOTAのキー管理に関する研究では、次のようにまとめられています。これらの数値は調査時期や対象デバイスによって異なりますが、実用的な結論は共通しています。成熟したプラットフォームであっても、すべてのデバイスが同時にアップデートされるわけではありません。単一のOTAアーティファクトですべてのデバイスをカバーしようとするのではなく、ネイティブのバージョンや機能に基づいてbundleを割り当ててください。
リリース前の運用チェックリスト
リリースチェックリストは1ページにまとめ、リポジトリやrunbookと一緒に保管してください。インシデントが発生するたびに更新しましょう。実際の障害発生後に導入された管理策は、通常、再発防止に直結します。
upload 前に
- 互換性の確認: bundleが対象のnative binaryに正確に対応し、利用できないplugin APIを使用していないことを確認します。
- アーティファクトの確認: クリーンなワークスペースからbuildし、生成されたファイルを確認して、想定したwebバージョンであることを検証します。
- 署名の保護: CIに想定どおりのsecretとkeyがあることを確認します。logsから機密情報が漏れないことも確認してください。
- チャネルの検証: bundleを割り当てる前に、staging、beta、productionのマッピングをレビューします。
- リカバリ手段の確保: 以前の正常動作が確認済みのbundleを記録し、フォールバックパスが引き続き動作することを確認します。
- 実機でのスモークテスト: 代表的なデバイスで、起動、ログイン、チェックアウト、ナビゲーション、永続化、updateの準備状況をテストします。
プロモーション期間中
まずは限定的なコホートにリリースし、ユーザーへの影響を示す以下の指標を監視します:
- クラッシュフリーセッション数: 前のbundleと比較し、リグレッションが発生していないか調査します。
- JavaScriptエラー率: bundle、native version、プラットフォーム、および機能パスごとにエラーをグループ化します。
- コールドスタートの挙動: アクティベーションによって起動時間が変わっていないか、またはユーザーが空白の画面で止まっていないかを確認します。
- 機能の適用範囲: リモートで有効化した機能が、バイナリ側と一致していることを確認します。
- 採用率と失敗率: アップデートを確認していない対象デバイスと、確認したものの失敗したデバイスを切り分けて分析します。
プロモート後の最初の観測期間は、 30分間 維持してから公開範囲を広げてください。クリティカルパスで障害が起きた場合は、本番チャネルを以前のbundleに戻し、logsを保存した上で、修正を別のOTA bundleに含めるか、新しいネイティブリリースとして出すかを判断してください。
リリース当日の心得: fallback bundleが役立つのは、チームがその識別子、互換性範囲、復元コマンドを把握している場合だけです。
このチェックリストを共通の運用 code として扱ってください。Capgo では、署名済みbundleの配信、チャネルターゲティング、ロールアウト制御、更新履歴、デバイスレベルのobservabilityを提供できます。互換性ポリシーとプロモートの判断は、引き続きチームが担います。
Capgo は、CapacitorJSチームに、チャネル、段階的配信、ロールバック保護、リリースのobservabilityを備え、署名済みJavaScript、CSS、設定、アセットの更新を制御できる手段を提供します。このチェックリストを次のデプロイに組み込み、ステージングバイナリでテストした上で、 Capgo 本番ワークフローに適用できるか評価してください。