Murasaki
ビルド & 配布

配布

本物のネイティブインストーラーを出荷する — クロスアーキテクチャビルド、署名、公証、CI。

murasaki bundle / murasaki installer は、macOS(.app / .dmg)、Windows(ポータブル .zip、NSIS .exe、MSI .msi)、Linux(AppDir + .AppImage.deb)向けに、本物のインストーラーを出力します。いずれもクロスアーキテクチャ(arm64 / x64)に対応しています。Linux の .AppImage は macOS/Windows の成果物と同じように起動・自己更新できます。更新の仕組み、.deb におけるシステムのパッケージマネージャー上の制約、コンテナ/CI で AppImage を動かす際の FUSE に関する注意点については、下記の「Linux: AppImage と .deb」セクションを参照してください。

ターゲットバンドルインストーラー公開コード署名
macOS arm64 / x64.app + .app.zip.dmgDeveloper ID + 公証に標準対応
Windows x64 / arm64ポータブルフォルダー + .zipNSIS .exe、Windows 上で WiX .msiSignTool 経由の Authenticode に標準対応
Linux x64 / arm64AppDir + .AppImage.deb--sign による GPG 分離署名(distro リポジトリ/キーリングの信頼統合はなし)

ファイルが生成されたからといって、そのまま配布可能とは限りません。クリーンな環境でインストール済みの成果物をテストし、署名とすべての本番用依存関係・リソースを検証してください。Murasaki は 1.0 未満であり、Node の依存関係パッケージングは、動的なネイティブアドオンや実行時に検出されるアセットの配置すべてをまだ網羅していません。

クロスアーキテクチャビルド

バンドルには、ポータブルなターゲット固有の Node ランタイム(nodejs.org から取得し、~/.murasaki/node/ にキャッシュ)と、コンパイル済みのネイティブランチャーバイナリが同梱されます — CLI をたまたま実行している node ではありません。Murasaki は公式 Node のリリースキーのフィンガープリントを固定しており、クリア署名された SHASUMS256.txt.asc の署名を検証してから、選択したアーカイブのチェックサムを信頼します。つまり、Apple Silicon の開発マシンから Intel 向けビルドを生成することも、その逆もできます:

ランタイムのダウンロードは HTTPS 限定で、タイムアウトとサイズの上限を設け、非公開の一時ファイルへストリーミングし、署名とチェックサムの検証後にのみキャッシュします。キャッシュ済みの Node / AppImage ランタイムも使用のたびに再ハッシュし、部分的にしか揃っていない、または改変されたキャッシュエントリはパッケージングせずに破棄します。

murasaki bundle --arch x64             # Apple Silicon Mac 上で x64 の .app をビルド
murasaki bundle --arch arm64           # Intel Mac 上で arm64 の .app をビルド
murasaki bundle --target win32-arm64   # arm64 の Windows バンドル

murasaki installer も、同じ方法で --arch / --targetbundle に転送します。Windows の場合は win32-x64 / win32-arm64 です。

URL スキームとファイル関連付け

パッケージ済みアプリが扱うハンドラーは、murasaki.config.ts で宣言します。

murasaki.config.ts
import { defineConfig } from 'murasaki'

export default defineConfig({
  appId: 'com.example.notes',
  productName: 'Notes',
  protocols: [{ scheme: 'example-notes', name: 'Notes link' }],
  fileAssociations: [{
    extensions: ['enote'],
    name: 'Notes document',
    role: 'editor',
    mimeType: 'application/x-example-note',
  }],
})

ターゲットごとの登録結果は次のとおりです。

Artifact登録動作
macOS .app / .dmg署名前にアプリの Info.plistCFBundleURLTypesCFBundleDocumentTypes、エクスポートされたドキュメント UTI を書き込む。DMG にはそのアプリがそのまま入る
Windows NSIS .exeプロトコル、ProgID、Open With のエントリ、デフォルトのアプリのケイパビリティを、デフォルトではユーザー単位、installMode: 'perMachine' を指定するとマシン単位で登録
Windows WiX .msi同じハンドラーをマシン単位で登録
Windows ポータブルフォルダー / .zipレジストリを変更せず、自動登録も行わない
Linux AppDir / .AppImage / .deb.desktop ファイルに MimeType= 行(プロトコルごとに x-scheme-handler/<scheme>、ファイル関連付けの拡張子ごとに application/x-<extension>)を書き込む。.debusr/share/applications/ にインストールし、インストール/削除時にデスクトップデータベースを更新する。手動展開した AppDir/.AppImage には OS レベルの登録手順はない。コールドスタート時の argv(.desktopExec= 行が展開する %U/%F)と、2 回目起動時のアクティベーションは、どちらの場合も動作する — 下記参照

コールドスタート、2 回目の起動、macOS の open イベントのいずれでも、一致した URL / ファイルは ready() 完了後に Node Main の openRequested() フックへ渡されます。Windows のポータブルビルドでも、URL / ファイルをコマンドラインで直接渡せば受け取れますが、インストール不要の成果物が OS のデフォルトを自動で取得することはありません。

Windows への登録はアプリを選択可能なハンドラーにしますが、ユーザーが保護されたデフォルトアプリの選択を上書きすることはありません。新規インストールとアップグレードの両方をテストし、アンインストール時に自分のアプリのハンドラーエントリだけが削除されることを確認してください。

署名と公証

Murasaki が担うのは署名のオーケストレーションであり、発行者としての身元を証明するものではありません。Apple Developer ID または Windows の証明書 / Artifact Signing プロファイルを用意してください。--sign は未署名の成果物を黙って公開せず、署名できなければ失敗します。

デフォルトでは murasaki bundlead-hoc signed.app を生成し、murasaki installer はそのアプリを .dmg に格納します。これにより macOS はローカルのバンドル整合性を検証できますが、信頼された開発者としての身元を示すものではなく、成果物を公証することもできません。自分でダウンロードした開発用の成果物に限り、次のコマンドで検疫属性を外せます:

xattr -dr com.apple.quarantine "<path>"

これをエンドユーザー向けのインストール手順にしないでください。警告なしで公開配布するには、Developer ID 署名と公証が必要です。

警告なしで配布するには、あなた自身の Apple Developer ID で署名・公証してください — Murasaki は独自の証明書を一切同梱していません:

murasaki bundle --sign                 # .app を Developer ID で署名する
murasaki installer --sign --notarize   # + .dmg を Apple に提出し、チケットをステープルする
  • --sign.app を hardened runtime で署名します(Apple のドキュメント化されたフローどおり: まず内側のコード、その後外側のバンドルの順)。署名 ID は $MURASAKI_SIGN_IDENTITY、次に config.sign.identity、次にキーチェーン内で最初に見つかった "Developer ID Application" の ID の順で解決されます。メインアプリと同梱の Node ヘルパーは、別々のエンタイトルメントで署名されます。ホスト/システム権限はアプリ側に残し、JIT・未署名実行可能メモリ・ライブラリバリデーションの無効化は Node だけに付与します。App Sandbox は現在未対応で、sign.appSandbox: true を指定すると、無効な継承ヘルパー署名を生成する代わりにフェイルクローズで拒否します。hardened runtime 用の権限セットは config.sign.entitlementsconfig.sign.helperEntitlements で個別に上書きでき、指定したファイルが存在しない、または無効な場合はフェイルクローズで停止します。
  • --notarize には --sign が必須です(公証は Developer ID 署名済みのコードしか受け付けません)。認証情報は APPLE_IDAPPLE_TEAM_IDAPPLE_APP_PASSWORD(アプリ専用パスワード)から読み取られます — 設定やファイルからは決して読み取りません.dmg を Apple の notary サービスに提出し、結果を待ってから、Gatekeeper がオフラインで検証できるようチケットをステープルします。

どちらも、有料の Apple Developer Program メンバーシップが必要です。

macOS の成果物を検証する

署名後の最終 .app と、公証後の最終 .dmg に対して実行します。

codesign --verify --strict --verbose=2 "dist/bundle/My App.app"
codesign -dvvv --entitlements :- "dist/bundle/My App.app"
spctl -a -t open --context context:primary-signature -vv "dist/My App-1.0.0.dmg"
xcrun stapler validate "dist/My App-1.0.0.dmg"

codesign --verify はアプリのコード/リソースのシールを検査し、spctl は配布用 DMG を Gatekeeper ポリシーに照らして評価し、stapler validate は公証チケットが添付されているかを検査します。これらはそれぞれ別の観点を確認するものなので、公開リリースでは全部実行してください。

Windows のインストーラーと署名

murasaki bundle --target win32-x64 は、ポータブルフォルダーと .zip を生成します。murasaki installer は、それに加えてビルドホストにすでに存在するツールも呼び出します。

  • NSIS .exe: makensis。macOS / Windows で実行可能
  • .msi: WiX v4。Windows 上で実行

再現性のある CI ツールチェーンでは、MURASAKI_NSIS_PATH / MURASAKI_WIX_PATH に実行ファイルの正確なパスを指定できます。明示したパスは常に優先されます。そのパスが存在しない、または実行できない場合はそのインストーラー形式をスキップし、他のインストーラーも作れなければコマンドは失敗します。

NSIS は perUser(デフォルト、昇格なし)または perMachine、MSI は常にマシン単位です。ブランディングとアップグレード ID については設定を参照してください。

署名済みのリリースビルドは Windows 上で実行してください(未署名の Windows 成果物のクロスビルドは、引き続き他 OS でも可能です):

pnpm exec murasaki bundle --target win32-x64 --sign
pnpm exec murasaki installer --target win32-x64 --sign

bundle コマンドは、ポータブル ZIP を作る前に <productName>.exe を署名します。installer コマンドはその署名済みペイロードを NSIS / MSI に格納し、生成したセットアップ実行ファイルと MSI 自体にも署名します。いずれも SHA-256 とデフォルトの RFC 3161 タイムスタンプを使用し、signtool verify /pa /v /tw による別途の検証も行います。署名または検証に失敗した場合、リリースは停止します。

未署名のインストーラーはローカルでのパッケージング検証には使えますが、ファイル形式が正常でも、現在の Windows のアプリケーション制御ポリシーによって遮断される場合があります。Murasaki はこの場合に警告を出します。公開する成果物には必ず署名し、利用者に SmartScreen や Smart App Control の無効化を求めないでください。

署名方法は次のいずれか1つを選びます。CI では設定ファイルを編集せずに環境変数で上書きできます。

SignerConfig環境変数による上書き
PFX/P12sign.windows.certificateFileMURASAKI_WINDOWS_CERTIFICATE_FILE + 任意で MURASAKI_WINDOWS_CERTIFICATE_PASSWORD
インポート済み証明書のサブジェクト名sign.windows.certificateSubjectNameMURASAKI_WINDOWS_CERTIFICATE_SUBJECT
インポート済み証明書の拇印sign.windows.certificateSha1MURASAKI_WINDOWS_CERTIFICATE_SHA1
Microsoft Artifact Signingsign.windows.artifactSigning.{dlib,metadata}MURASAKI_WINDOWS_ARTIFACT_SIGNING_DLIB + MURASAKI_WINDOWS_ARTIFACT_SIGNING_METADATA

セレクターを何も指定しない場合、SignTool /aCurrentUser/My から最適なコード署名証明書を選択します。マシンストアを使うには certificateStore: 'localMachine'、SignTool のパスは MURASAKI_SIGNTOOL_PATH、タイムスタンプサービスは MURASAKI_WINDOWS_TIMESTAMP_URL で上書きできます。false を指定するとタイムスタンプを無効化できますが、公開リリースでは非推奨です。

Microsoft Artifact Signing を使う場合、dlib には Azure.CodeSigning.Dlib.dllmetadata には機密情報を含まないアカウント/プロファイルの JSON を指定します。認証は Azure CLI・ワークロード ID・マネージド ID 側に残し、認証情報を JSON や Murasaki の設定に書き込まないでください。このプロバイダーでは、Murasaki はデフォルトで Microsoft の Artifact Signing タイムスタンプ局を使用します。

updater の Ed25519 マニフェスト署名は、Windows SmartScreen における発行者としての身元を確立するものではないため、Authenticode の代わりにはなりません。正しく署名していても、新規の発行者はレピュテーションが育つまで SmartScreen の警告が表示される場合があります。

GitHub Actions での Windows 署名済みリリース

最小構成の PFX の例です。証明書はランナーの一時ディレクトリにのみ書き込みます。証明書ポリシーに合う場合は、インポート済みのストア証明書、またはクラウド/HSM プロバイダーを優先してください。

.github/workflows/release-windows.yml
jobs:
  windows:
    runs-on: windows-2025
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 24
      - run: pnpm install --frozen-lockfile
      - name: Materialize signing certificate
        shell: pwsh
        env:
          CERTIFICATE_BASE64: ${{ secrets.WINDOWS_CERTIFICATE_PFX }}
        run: |
          [IO.File]::WriteAllBytes(
            "$env:RUNNER_TEMP\release.pfx",
            [Convert]::FromBase64String($env:CERTIFICATE_BASE64)
          )
      - name: Build signed installers
        env:
          MURASAKI_WINDOWS_CERTIFICATE_FILE: ${{ runner.temp }}\release.pfx
          MURASAKI_WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
        run: pnpm exec murasaki installer --target win32-x64 --sign

GitHub Actions での macOS 署名済みリリース

タグのプッシュで .dmg をビルドし、(任意で)署名・公証してから GitHub Release に添付します。あなたのアプリに .github/workflows/release.yml として次のワークフローを追加してください:

.github/workflows/release.yml
name: Release
on:
  push:
    tags: ['v*']
jobs:
  release:
    runs-on: macos-14
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 24
      - run: pnpm install
      - name: Import signing certificate
        if: ${{ secrets.APPLE_CERTIFICATE_P12 != '' }}
        env:
          CERT_P12: ${{ secrets.APPLE_CERTIFICATE_P12 }}
          CERT_PW: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
        run: |
          KC="$RUNNER_TEMP/app.keychain-db"
          security create-keychain -p "" "$KC"
          security set-keychain-settings -lut 21600 "$KC"
          security unlock-keychain -p "" "$KC"
          echo "$CERT_P12" | base64 --decode > "$RUNNER_TEMP/cert.p12"
          security import "$RUNNER_TEMP/cert.p12" -k "$KC" -P "$CERT_PW" -T /usr/bin/codesign
          security set-key-partition-list -S apple-tool:,apple: -s -k "" "$KC"
          security list-keychains -d user -s "$KC" $(security list-keychains -d user | tr -d '"')
      - name: Build installer
        env:
          APPLE_ID: ${{ secrets.APPLE_ID }}
          APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
          APPLE_APP_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }}
          HAS_CERT: ${{ secrets.APPLE_CERTIFICATE_P12 != '' }}
        run: |
          if [ "$HAS_CERT" = "true" ]; then
            pnpm exec murasaki installer --sign --notarize
          else
            pnpm exec murasaki installer
          fi
      - uses: softprops/action-gh-release@v2
        with:
          files: dist/*.dmg

署名・公証するには、次のリポジトリシークレットを追加してください(未署名の .dmg にする場合はすべて省略します): APPLE_CERTIFICATE_P12(あなたの Developer ID の .p12 を base64 エンコードしたもの)、APPLE_CERTIFICATE_PASSWORDAPPLE_IDAPPLE_TEAM_IDAPPLE_APP_PASSWORD

これらのシークレットを設定やソースにコミットしないでください — --notarize は意図的に、環境変数からしかそれらを読み取りません。

Linux: AppImage と .deb

ネイティブランチャーは、生成した AppDir / .AppImage / .deb を実際に動かします — ウィンドウ、WebView、シングルインスタンスロック、ディープリンク、グレースフルシャットダウン、クラッシュレポートは、いずれも macOS/Windows と同じように動作します。murasaki installer --sign は成果物を GPG で署名します — 下記の「GPG 分離署名」を参照してください。.rpm とリポジトリメタデータはまだ実装されていません — 下記の「まだ実装されていないこと」も参照してください。

murasaki bundle --target linux-x64      # dist/bundle/<Name>.AppDir/ + <Name>-<version>-linux-x64.AppImage
murasaki bundle --target linux-arm64
murasaki installer --target linux-x64   # dist/<debname>_<version>_amd64.deb
murasaki installer --target linux-arm64 # dist/<debname>_<version>_arm64.deb

どちらも、bundle --target win32-x64 と同様に macOS / Windows / CI からクロスビルドできます — Linux ホストは不要です。bundle.AppImage を作るには mksquashfs が PATH 上に必要です(AppDir フォルダ自体には追加の要件はありません):

brew install squashfs              # macOS
apt install squashfs-tools         # Debian/Ubuntu
dnf install squashfs-tools         # Fedora

installer が生成する .deb は、純粋な Node 製の ar/tar ライターによるものです — dpkg-deb も、追加のホストツールも必要ありません。

AppDir は、macOS の .app における Contents/Resources と同じレイアウトを usr/lib/<appId>/resources/ の下に反映します(クライアントビルド、Server Actions、ダウンロードした Node ランタイム、murasaki-meta.json など)。加えて、あらゆる Linux デスクトップ環境が必要とする freedesktop.org 由来の要素 — AppRun、ルートの .desktop ファイルと usr/share/applications/<appId>.desktopconfig.icon から生成する usr/share/icons/hicolor/ のアイコンテーマ一式(16〜512px)— も含みます。

AppImage を FUSE なしで動かす

AppImage は通常、自身を FUSE でマウントします。CI ランナーや最小構成のコンテナなど、一部のホストには動作する /dev/fuse がありません。通常の FUSE マウント経路を使うには libfuse2(Ubuntu 24.04 以降では libfuse2t64)をインストールしてください。あるいは --appimage-extract-and-run を渡すと、AppImage ランタイムが自身を一時ディレクトリへ展開し、そこから実行します — FUSE は不要になりますが、起動は遅くなります。Murasaki 自身の CI(.github/workflows/app-package-linux.yml)も、まさにこの理由から常に --appimage-extract-and-run を使っています。

AppImage と .deb: 更新の扱いの違い

  • .AppImage は Murasaki 自身の自己完結型の更新ペイロードであり、自己更新の対象でもあります — useUpdate()install() は、実行中の .AppImage ファイル自体をジャーナル方式でその場に置き換え(macOS の .app.zip / Windows の NSIS セットアップと同じ、同一ボリュームでのバックアップと起動時ヘルスチェックの保証つき)、--appimage-extract-and-run で再起動します(ホストに FUSE があってもなくても動作します)。更新後の最初の起動に失敗した場合は、自動的に前のバージョンへロールバックします。murasaki release --manifest<Name>-<version>-linux-{x64,arm64}.AppImage をスキャンします。詳しくは自動更新を参照してください。
  • .deb はパッケージマネージャーが所有します。更新ペイロードになることは決してありません — アップグレードは apt/dpkg の仕事であり、OS のパッケージマネージャーが他のインストール済みパッケージのライフサイクルを所有するのと同じことです。murasaki release --manifest.deb を探すことはなく、.deb でインストールした(または手動展開したそのままの AppDir の)アプリの useUpdate().check() は、マニフェストを確認する代わりに「更新はシステムのパッケージマネージャーが管理しています」という構造化された結果を返します — エラーにはなりません。

GPG 分離署名

ここでも、Murasaki が担うのは署名のオーケストレーションであり、代わりに GPG 鍵を生成・保管することはありません。既存の鍵を用意してください。--sign は、署名できない場合に未署名の成果物を黙って出力するのではなく、フェイルクローズで停止します。

pnpm exec murasaki installer --target linux-x64 --sign

--sign は、.AppImage.deb、そして両者のチェックサムをまとめた SHA256SUMS ファイルに対して、分離した ASCII-armored 形式の GPG 署名(<artifact>.sig)を生成します。受け取る側は、ファイル単位の署名とチェックサムマニフェストのどちらでも検証できます。dpkg-sig が PATH 上にあれば、Murasaki は .deb へ Debian ネイティブの署名も追加で埋め込みます(可能な場合のみの付加的な処理です)。これはベストエフォートであり、dpkg-sig が無くても --sign 自体は止まりません。

署名鍵は $MURASAKI_GPG_KEY(ローカルの gpg キーリングが認識しているキー ID、フィンガープリント、またはメールアドレス)、次に murasaki.config.tssign.linux.gpgKey の順で解決されます。パスフレーズは $MURASAKI_GPG_PASSPHRASE、またはすでにアンロック済みの gpg-agent からのみ取得します——設定やファイルからは読み取りません。

受け取る側は自分の gpg で検証します。

gpg --verify MyApp-1.0.0-linux-x64.AppImage.sig MyApp-1.0.0-linux-x64.AppImage
gpg --verify SHA256SUMS.sig SHA256SUMS

これは、成果物がその鍵によって署名された内容と一致することを証明するものであり、鍵自体が信頼できることまでは示しません——受け取る側は、鍵サーバーや公式サイト、リリースノートなどアウトオブバンドな経路で公開鍵を入手し、その信頼性については自分自身で判断する必要があります。

まだ実装されていないこと

  • .rpm やリポジトリメタデータ(apt/dnf のリポジトリインデックス)はありません。
  • 上記の GPG 署名には、apt/dnf のキーリングや distro リポジトリとの信頼統合はありません——証明するのは成果物の整合性であり、リポジトリの信頼ではありません。
  • .deb や、手動展開したそのままの AppDir には自己更新はありません — 置き換え可能なファイルを持つのは .AppImage だけです(上記参照)。

リリースチェックリスト

  • クリーンなチェックアウトと、ロック済みの依存関係グラフからビルドする
  • インストーラー化する前に bundle を起動して動作確認する
  • 公開するすべてのプラットフォーム/アーキテクチャについて、クリーンな環境でインストール・更新・アンインストールを確認する
  • 設定した URL スキーム/ファイル関連付けを、コールドスタート時と、メインのアプリ起動中の両方で確認する
  • Node Main のシャットダウン、オフライン起動、ネットワーク障害時の挙動を確認する
  • macOS の署名・公証、Windows の Authenticode 署名、Linux の GPG 署名を検証する
  • 最終ペイロードが変更不可能になってから、Murasaki の更新マニフェストを生成・署名する
  • ペイロード、latest.jsonlatest.json.sig を同時に公開する
  • 署名の認証情報や MURASAKI_UPDATE_KEY は CI のシークレットに保存し、murasaki.config.ts には書かない

コード署名と更新の署名はそれぞれ独立しています。アプリ内更新を有効にする前に、セキュリティ自動更新を確認してください。

次へ

GitHub でこのページを改善

On this page