ANTIGRAVITY LABEN
記事一覧/アプリ開発
アプリ開発/2026-05-02上級

Tauri 2 アプリを有料配布するまでに直した順番 — 署名・公証・自動更新・ライセンス検証

Tauri 2 のデスクトップアプリを有料配布するまでに私がつまずいた、署名の順番・公証の失敗条件・段階配信の判定漏れ・オフラインライセンス検証を、動く実装として書き直しました。

antigravity440tauridesktop2code-signingnotarization2monetization15license

プレミアム記事

Tauri 2 アプリを有料配布するまでに直した順番

Tauri 2 でアプリを完成させたあと、ほとんどの開発者が次に詰まるのは「コードを書く部分」ではありません。tauri build が通ったあとから本当の戦いが始まります。macOS の Notarization、Windows の SmartScreen 警告、自動アップデート、ライセンス認証、課金フロー — これらは公式チュートリアルがあえて深く触れない領域でありながら、有料アプリとして配布するなら避けて通れない関門でもあります。

私自身、個人で iPhone/Android アプリを長年運用してきましたが、デスクトップアプリは別の生き物です。ストアによる審査と配信に頼れない代わりに、自分でインフラを設計しなければなりません。ここではAntigravity のエージェントを活用しながら、本番運用に耐える Tauri 2 アプリのデプロイパイプラインをまるごと組み立てる方法を、実運用で得た落とし穴も含めて共有していきます。

なぜ Tauri 2 の「本番デプロイ」が独特の難所になるのか

Tauri が Electron に対して優位なのは、バイナリサイズと起動速度だけではありません。配布の自由度こそが本質です。Tauri 2 はネイティブインストーラを直接生成し、アップデート基盤も自由に選べる設計になっています。この自由度は強力ですが、同時に「すべて自分で設計しなければならない」責任を意味します。

Electron 系のフレームワークでは、electron-updaterelectron-builder の組み合わせで多くのことが自動化されます。Tauri 2 でも tauri-plugin-updater が用意されていますが、署名鍵の管理、配信エンドポイントの設計、段階的ロールアウトの仕組みは自前で組み上げる必要があります。Antigravity を使う最大の利点は、この「フレームワーク横断の設計判断」を AI エージェントと対話しながら詰められることです。具体的には、Antigravity の Manager Surface で「Rust バックエンドの署名検証」と「TypeScript フロントエンドのライセンス UI」を別エージェントに同時並行で書かせるパターンが本記事を貫く設計思想になります。

もう一つ、Web との決定的な違いがあります。Web の不具合はロールバックすれば数秒で消えますが、デスクトップアプリの不具合は利用者のマシンに残り続けます。次にアプリを起動してもらうまで、こちらから触れる手段がありません。

この非対称性が、更新サーバも、ライセンス形式も、インストーラも「保守的に倒す」理由になります。私はこの前提を理解するまで、配信の設計を Web の感覚で組んでいて、一度作った修正版が届かない期間の長さに肝を冷やしました。

Electron 側の設計と比べたい方は、Antigravity × Electron デスクトップAIアプリケーション開発 に更新とパッケージングの違いをまとめてあります。本記事は「ストアに頼らない配布を全部自前で持つ」側の話です。

macOS の Notarization を Antigravity で自動化する

macOS で配布する .dmg や .app は、単にコード署名するだけでは「開発元が未確認のため開けません」と表示されます。Apple のサーバーで Notarization(公証)を通し、その結果をバイナリに staple(貼付)するまでが必須の手順です。

私が最初に組んだスクリプトは、公証が通ったのに配布したアプリが警告を出す、という不可解な状態を作りました。原因は三つあり、どれも「書き方」ではなく「順番」の誤りでした。

一つ目は codesign --deep に頼っていたこと。Apple は配布用途での --deep を推奨しておらず、sidecar やフレームワークには意図した entitlements が付かないまま素通りします。二つ目は --timestamp の欠落で、これは公証側が secure timestamp なしの署名を拒否します。三つ目が一番厄介で、tauri build が出力した DMG には署名前の .app が入っているのに、その DMG を公証して staple していたことでした。

つまり中身は未署名のまま、外側だけが公証済みという状態です。以下は順番を組み直した版です。

#!/usr/bin/env bash
# notarize.sh - Tauri 2 macOS 公証パイプライン(署名 → DMG 再作成 → 公証 → staple)
# 前提: APPLE_ID / APPLE_TEAM_ID / APPLE_APP_PASSWORD / SIGNING_IDENTITY が環境変数に設定済み
# 前提: tauri build は --bundles app で実行し、DMG は本スクリプトが作る
set -euo pipefail
 
APP_NAME="MyApp"
APP_PATH="src-tauri/target/universal-apple-darwin/release/bundle/macos/${APP_NAME}.app"
DMG_PATH="dist/${APP_NAME}_universal.dmg"
mkdir -p dist
 
# 1. 内側から署名する。--deep は配布用途では使わない
#    (nested code に entitlements が引き継がれず、後から原因が追いにくい失敗になる)
while IFS= read -r -d '' NESTED; do
  codesign --force --options runtime --timestamp \
    --sign "$SIGNING_IDENTITY" "$NESTED"
done < <(find "$APP_PATH/Contents/MacOS" "$APP_PATH/Contents/Frameworks" \
           -type f -perm -u+x -print0 2>/dev/null)
 
# 2. 最後に .app 本体を署名する(--timestamp がないと公証が secure timestamp 欠落で落ちる)
codesign --force --options runtime --timestamp \
  --sign "$SIGNING_IDENTITY" \
  --entitlements src-tauri/entitlements.plist \
  "$APP_PATH"
codesign --verify --strict --verbose=2 "$APP_PATH"
 
# 3. 署名済みの .app から DMG を作り直す(ここを飛ばすと未署名の .app が同梱される)
STAGE="$(mktemp -d)"
cp -R "$APP_PATH" "$STAGE/"
ln -s /Applications "$STAGE/Applications"
rm -f "$DMG_PATH"
hdiutil create -volname "$APP_NAME" -srcfolder "$STAGE" -ov -format UDZO "$DMG_PATH"
codesign --force --timestamp --sign "$SIGNING_IDENTITY" "$DMG_PATH"
 
# 4. DMG を公証する。exit code だけに頼らず status を必ず読む
xcrun notarytool submit "$DMG_PATH" \
  --apple-id "$APPLE_ID" \
  --team-id "$APPLE_TEAM_ID" \
  --password "$APPLE_APP_PASSWORD" \
  --wait \
  --output-format json > notarize_result.json
 
STATUS=$(jq -r '.status' notarize_result.json)
SUBMISSION_ID=$(jq -r '.id' notarize_result.json)
if [ "$STATUS" != "Accepted" ]; then
  echo "公証が通りませんでした: status=${STATUS} id=${SUBMISSION_ID}"
  xcrun notarytool log "$SUBMISSION_ID" \
    --apple-id "$APPLE_ID" \
    --team-id "$APPLE_TEAM_ID" \
    --password "$APPLE_APP_PASSWORD"
  exit 1
fi
 
# 5. staple と最終検証(DMG は primary-signature を見る)
xcrun stapler staple "$DMG_PATH"
spctl --assess --type open --context context:primary-signature --verbose=4 "$DMG_PATH"
echo "公証完了: $DMG_PATH"

順番を直したあと、旧版と新版の差はコマンドの数ではなく、検証で落ちる位置に出ました。旧版は spctl.app を見ていたため、DMG 内の未署名バイナリに気づけません。新版は DMG そのものを --context context:primary-signature で評価するので、利用者がダウンロードした状態と同じものを検査できます。

notarytool submit --wait の終了コードだけで合否を判定していたのも危うい設計でした。ステータスが Invalid でも CI が緑になった回があり、そのビルドをそのまま配ってしまったことがあります。JSON の status を明示的に読む数行を足すだけで、この種の取りこぼしは消えます。

期待される出力は notarytoolstatusAccepted になること、staple 後の spctlsource=Notarized Developer ID を返すことの二点です。Unnotarized Developer ID のままなら staple が失敗しているか、公証にかけた成果物と配布する成果物が別物になっています。

失敗ログの読み解きは Antigravity の Sub-Agent に任せています。「notarytool log の JSON を受け取り、issues[].message を『entitlements 不足』『timestamp 欠落』『未署名の nested binary』に分類して、該当パスだけを出力して」と頼むと、原因のファイルまで一息で降りられます。公証のエラーは文面が抽象的なので、分類してくれる層が一枚あるだけで復旧時間が変わります。

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
tauri build が出力した DMG をそのまま公証に出すと、中の .app が未署名のまま通ってしまう順番の罠を、動くスクリプトごと回避できます
段階配信が 0% でも一部端末に配ってしまう判定漏れと、端末バケットが起動ごとに変わる非固定バグを、Worker の実装を差し替えて塞げます
Stripe Webhook の署名検証・冪等化・Ed25519 ライセンス発行までを 1 本の Worker に収めた実装を、そのまま自分のプロダクトへ移植できます
Stripe による安全な決済 · いつでもキャンセル可能

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Antigravity Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

アプリ開発2026-06-30
Antigravity に任せた App Open 広告が、課金画面やリワード動画から戻るたびに出てしまう
Antigravity のエージェントに App Open 広告を実装させると、自前のリワード動画や Google Play の課金シートから戻った瞬間にも広告が出ます。AdMob のポリシー違反にも触れるこの挙動を、前面復帰の理由を記録するフォアグラウンド調停役で抑える設計を、動く Kotlin と検証手順つきで解説します。
アプリ開発2026-06-25
「広告を見たら壁紙を解放」をエージェントがクライアント側だけで付与していた — リワード報酬をSSVで検証し直した記録
リワード広告を見たら壁紙を解放する機能を Antigravity のエージェントに頼んだら、解放フラグをクライアント側だけで書き込む実装が返ってきました。なぜそれでは足りないのか、AdMob のサーバーサイド検証(SSV)で報酬付与を検証し直し、二重付与まで止めた設計を記録します。
アプリ開発2026-06-19
ATT を取る前に広告SDKを初期化していた — 初回起動だけ eCPM が下がる順序の罠
iOS の AdMob メディエーションを4本のアプリへ広げたとき、初回セッションだけ広告収益が落ちる現象に気づきました。原因は ATT 許諾と MobileAds 初期化の順序です。順序が効く理由と、Antigravity にその順序を監査させた実装記録をまとめました。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →