Claude CodeでElectronの公証とコード署名を設定する — 秘密をClaudeに渡さない構成
electron-builderの署名と公証の設定をClaude Codeに書かせ、証明書とApple IDの資格情報はmacOSのビルドジョブだけに置く構成と、確認コマンドをまとめます。
Electronアプリの署名と公証は、electron-builderなら資格情報が環境変数にあるだけで自動で走ります。Claude Codeに任せられるのは、その設定ファイルとワークフローの下書きまでです。証明書とApple IDの資格情報は、Claudeが動くジョブとは別のmacOSジョブにだけ置きます。
メイン/レンダラー構成の実装はClaude CodeでElectronアプリを開発する手順で扱っています。ここでは、作ったアプリを配布できる形にする工程を、秘密を渡さない前提で組みます。
署名と公証はどこまでが自動で、何が必要か
macOSとWindowsはどちらも署名のないアプリの実行を止めます。配布するElectronアプリは署名しておく前提です。macOSでは、署名に続いて公証(notarization)というApple側の自動検査が必要になります。
Electronの説明では、署名と公証を始める前に次の3つが条件です。
- Apple Developer Programへの登録(年額)
- macOSが動くコンピューターにXcodeを入れること
- 署名用の証明書を作ってインストールすること
electron-builderの説明では、配布用の証明書は「Developer ID Application」で、Mac App Store用の証明書とは別物です。公証の後にApple発行のチケットをアプリに貼り付ける「ステープル」まで、資格情報があれば自動で実行されます。
macOS配布の3工程
コード署名
アプリが自分のものだと示し、改ざんがないことを証明します。
公証
Appleがマルウェアの有無を自動で検査し、チケットを発行します。
ステープル
チケットをアプリに貼ります。オフラインでも検査が通ります。
Mac App Store向けの署名は、証明書の種類も手順も別です。ここでは直接配布(DMGやZIP)に絞ります。
Claudeに渡すのは設定ファイルまで
署名の設定で厄介なのは、設定を書く作業と、秘密を使う作業が同じ場所で起きやすいことです。ローカルでClaude Codeに「署名まで通して」と頼むと、キーチェーンや.p12、Apple IDのパスワードが手の届く範囲に入ります。
分け方はシンプルです。
2つのワークフローで責務を分ける
Claudeのワークフロー
@claudeの依頼で動き、electron-builder.ymlとワークフローの修正をPRにします。渡す認証情報はClaude用のキーだけです。
リリースのワークフロー
タグのpushで動くmacOSジョブです。署名と公証の資格情報をenvで受け取り、electron-builderを実行するだけです。Claudeは動きません。
Claudeのワークフローは、GitHub Actionsの公式サンプルと同じ形で書けます。@claudeのコメントに反応する対話モードです。
name: Claude Code
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
jobs:
claude:
if: contains(github.event.comment.body, '@claude')
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
id-token: write
actions: read
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 1
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
claude_args: "--max-turns 8"このファイルにはAPPLE_*やCSC_*を書きません。Claudeのジョブが受け取る秘密はANTHROPIC_API_KEYだけです。--max-turnsは1回の依頼の往復を制限するCLI引数で、署名の試行錯誤が長引いたときの暴走を抑えます。
electron-builder.ymlに書く設定は少ない
公証にはHardened Runtimeが必須です。electron-builderのv27では、この設定がmac.signの配下にあります。
mac:
sign:
hardenedRuntime: trueこの3行で足ります。素のElectronアプリが公証で必要とする例外はcom.apple.security.cs.allow-jitだけで、electron-builderが既定で付けます。entitlementsファイルを用意しなくても公証できます。
自前のentitlementsファイルを置くと、既定を拡張せず置き換えます。置くときはallow-jitを残してください。
mac.notarizeで挙動を選べます。
| 値 | 資格情報がないとき |
|---|---|
| 未設定 | 資格情報がないとき警告を出して公証を飛ばす |
true | 資格情報がないときビルド開始時に失敗する |
false | 資格情報がないとき常に公証しない |
PRの確認用ビルドは署名なしで通したいので、未設定のままが扱いやすい構成です。リリース側で「公証が飛んだ」ことに気づきたければ、mac.notarize: trueにしておくと資格情報の設定漏れでビルドが落ちます。ただし署名なしのPRビルドでも失敗するようになるので、その場合はPR側のビルドを別に設計します。
v27より前のelectron-builderは、同じ項目をmacの直下に置いていました。使っている版を確かめてからClaudeに渡してください。
Claudeに守らせる規約をCLAUDE.mdに置く
Claudeは設定を足すとき、習慣で古い書き方を持ち込むことがあります。electron-builderの説明は、com.apple.security.cs.allow-unsigned-executable-memoryを「習慣で足さない」よう明記しています。2019年ごろのElectronで必要だった設定で、現行のV8では要らず、Hardened Runtimeを弱めるだけだからです。
CLAUDE.mdに署名まわりの規約を書いておくと、同じ指摘を毎回しなくて済みます。
## 署名とリリース
- 署名と公証の設定は electron-builder.yml の mac 節だけを変える
- entitlements は allow-jit のみ。足す場合は理由をPRに書く
- 証明書(.p12 .p8)、Apple ID、パスワードはリポジトリにも会話にも置かない
- 値が必要な作業は止めて、人に頼む
- .github/workflows/ で secrets.* の参照を増やす変更は、PRの説明で明示する資格情報の置き場所とリリースワークフロー
資格情報の方式は3つあります。CI向けはApp Store Connect APIキーです。有効期限がなく、2要素認証のプロンプトも出ません。
| 方式 | 環境変数 | 向く場面 |
|---|---|---|
| APIキー | 環境変数APPLE_API_KEY APPLE_API_KEY_ID APPLE_API_ISSUER | 向く場面CI |
| Apple ID | 環境変数APPLE_ID APPLE_APP_SPECIFIC_PASSWORD APPLE_TEAM_ID | 向く場面手元での確認 |
| キーチェーンプロファイル | 環境変数APPLE_KEYCHAIN APPLE_KEYCHAIN_PROFILE APPLE_TEAM_ID | 向く場面ローカル開発 |
APIキーの.p8ファイルは、パスのほかに「内容をbase64で1行にした文字列」でも渡せます。GitHubのsecretに入れるには、この形を使います。Apple IDで渡す場合も、通常のパスワードではなく、appleid.apple.comで作るアプリ専用パスワードを使います。
base64 < AuthKey_KEYID.p8 | tr -d '\n'
base64 < developer-id.p12 | tr -d '\n'出力をそのままGitHubのRepository secretsに貼ります。electron-builderは、base64の値を公証のたびに権限0600の一時ファイルに書き出し、notarytoolが終わると消します。
リリース用のワークフローは次のとおりです。CSC_LINKとCSC_KEY_PASSWORDは証明書用の変数です。
name: Release mac
on:
push:
tags: ["v*"]
jobs:
build-mac:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- name: Build, sign, and notarize
run: npx electron-builder --mac
env:
CSC_LINK: ${{ secrets.MAC_CSC_LINK }}
CSC_KEY_PASSWORD: ${{ secrets.MAC_CSC_KEY_PASSWORD }}
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}electron-builderの公証手順にあるGitHub Actionsの例を土台にしています。例はApple ID方式なので、ここでAPIキー方式に差し替えました。APIキー方式ではAPPLE_TEAM_IDは使いません。
Electronの公式チュートリアルにはPackager向けのAPIの例もあり、osxNotarizeにappleIdとappleIdPasswordを直接書く形をとっています。設定ファイルに値を書く書き方は、Claudeに設定を触らせる構成とは相性が悪いので、環境変数で渡す方式を選びます。
Claudeのジョブから秘密を遠ざける
ワークフローを分けても、Claudeが書き換えられる範囲は残ります。GitHub Appの権限にはWorkflows: Read and writeが入っているので、PRの中でリリース用のワークフローも修正できます。インストール時に権限の一部だけを受け入れることはできません。
ここで効くのは設定より、PRの読み方です。
落とし穴
Claudeが書いたPRで秘密を新たに参照していないか
.github/workflows/の差分に、secrets.の新しい参照や、envへのAppleの変数の追加が混ざっていないかをマージ前に読みます。署名の設定を直すだけのPRなら、electron-builder.ymlだけが変わります。ワークフローが変わったPRは、署名と関係ない変更でも人が読み直してください。
権限の面では、Claude用のワークフローにpermissions.denyを渡して、秘密になりうるファイルを読ませない手があります。settings入力にJSONを渡せます。
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
settings: |
{
"permissions": {
"deny": [
"Read(./.env)",
"Read(./**/*.p12)",
"Read(./**/*.p8)",
"Read(./certs/**)"
]
}
}ReadのdenyはEditとWriteも同じパスで止めます。ただし、ファイル名を渡さずに読むコマンドや、PythonやNodeのスクリプトが自分で開くファイルには効きません。OS単位でパスを塞ぐなら、サンドボックスを併用します。Claude Codeのサンドボックスの選び方が隔離範囲の比較になります。
.envの扱いはClaude Codeで.envを読ませないRead denyの書き方に詳しくあります。
守りの中心は「Claudeのジョブにそもそも秘密がない」ことです。denyルールは、うっかりファイルをリポジトリに置いたときの保険として足します。
署名できたかをコマンドで確かめる
ビルドが成功しても、公証が飛ばされていることがあります。資格情報が空だと、警告だけで公証をスキップするからです。ビルド後の成果物は、次のコマンドで確かめます。
spctl --assess --verbose --type exec dist/mac/MyApp.app
xcrun stapler validate dist/mac/MyApp.app
codesign --verify --deep --strict --verbose=2 dist/mac/MyApp.appspctlはGatekeeperの判定を再現し、公証済みならsource=Notarized Developer IDと出ます。stapler validateはチケットが貼られているかを見ます。codesignは署名の整合性です。
手元でClaudeにこの3行を実行させ、出力を読ませる使い方もできます。electron-builderにはelectron-builder verifyというコマンドもあり、出力ディレクトリの全アプリの署名とステープルを検査し、失敗すると終了コード1で止まります。
ワークフローの末尾に次の1行を足す形にすると、リリースの成否そのものが検査になります。
- run: npx electron-builder verify失敗したときの切り分け
Claudeにログを渡して原因を絞らせるときも、エラー文言から先に引くと速く済みます。electron-builderの説明にある代表例です。
| 症状 | 原因 | 直し方 |
|---|---|---|
| The software certificate used to sign this application is not trusted | 原因Developer ID証明書がキーチェーンにない、または信頼されていない | 直し方CI上のキーチェーン設定が完了しているかを確認する |
| Package Invalid: the binary is not signed with a valid Developer ID certificate | 原因Mac App Store用の証明書で直接配布した | 直し方Developer ID Applicationの証明書を使う |
| The executable does not have the Hardened Runtime enabled | 原因Hardened Runtimeが無効 | 直し方mac.sign.hardenedRuntime: trueにする |
| Invalid Info.plist | 原因同梱バイナリの一部が未署名 | 直し方追加のバイナリはafterSignフックで署名する |
| APPLE_API_KEYのファイルが開けない | 原因相対パスがビルドの実行場所からずれている | 直し方パスをやめ、base64の値で渡す |
「公証が終わらない」ときは、Appleの公証サービス側が遅い場合があります。electron-builderは完了までポーリングを続けるので、続くようなら開発者向けのシステムステータスも見ます。
署名が要らない段階と必須になる段階
開発中は署名なしで進めても構いません。署名の設定を空にするには、CSC_*を未設定にしてCSC_IDENTITY_AUTO_DISCOVERY=falseを渡します。設定側でmac.sign.identityをnullにしても同じです。
署名が効いてくるのは、macOSのAPIに触れるときです。
autoUpdater: Squirrel.Macは署名がないと自動更新自体が動かないsafeStorage: 署名が一貫しないと、更新のたびにキーチェーンの許可を再要求されるapp.setLoginItemSettings(): パッケージ化、署名、公証がないと登録に失敗することがある
これらを触るアプリでは、Electron本体のバグに見える不具合が署名で直ることがあります。署名を後回しにする場合は、この3つの動作確認も後回しになります。
Windowsの署名は別の仕組みです。Azure Artifact Signingや従来の証明書といった方式があり、この記事の範囲には入れていません。
まとめ
署名の設定ファイルは短く、難しいのはどこに秘密を置くかです。Claudeにはelectron-builder.ymlとPRの下書きを任せ、資格情報はタグで動くmacOSジョブのenvだけに置きます。マージ前のレビューでは、secrets.の増減を見るのが最短の確認です。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。