macOS Bundle Signing and DMG Packaging Workflow in OpenLogi Releases

OpenLogi orchestrates macOS distribution through a two-stage xtask workflow that first builds and signs the app bundle with a production channel identity, then packages it into a branded, ULMO-compressed DMG with optional final code signing.

The macOS bundle signing and DMG packaging workflow in OpenLogi is managed entirely through the xtask command-line utility located in the AprilNEA/OpenLogi repository. This system ensures that every distributed copy carries the correct Production channel identity required for macOS Transparency, Consent, and Control (TCC) permissions, while supporting both signed releases and unsigned local development builds.

Stage 1: Building and Signing the App Bundle

The first stage creates the .app structure and applies the necessary identity metadata before any cryptographic signing occurs.

Bundle Creation and Channel Identity

The command cargo xtask macos bundle generates the application bundle at target/release/bundle/osx/OpenLogi.app. Before signing, the utility invokes the identity stamping logic in xtask/src/commands/macos/bundle/identity.rs to ensure the bundle carries a stable Production identity.

The stamp function in identity.rs writes the following critical values to Info.plist:

  • CFBundleIdentifier: A stable reverse-domain identifier used by macOS to track permission grants across updates
  • CFBundleName: The display name presented to users in TCC dialogs

According to the source code comments in identity.rs (lines 4-8), applying this production identity before signing is mandatory. If a developer were to sign a bundle with a development identity first, existing user permissions would break when the app updates, as macOS treats different identities as distinct applications.

Code Signing the Bundle

When the OPENLOGI_SIGN_IDENTITY environment variable or --sign-identity flag is provided, the xtask utility passes the certificate to the codesign tool:

export OPENLOGI_SIGN_IDENTITY="Developer ID Application: Jane Doe (ABCD1234)"
cargo xtask macos bundle --channel production

The verify function in identity.rs ensures every nested component—including the Agent helper and Overlay helper—carries the same signing identity before the process completes. This satisfies Apple's "sign-inside-out" requirement where all bundled executables must share the same designated requirement.

If no signing identity is supplied, the bundle builds unsigned, which is useful for local testing:

cargo xtask macos bundle  # Creates unsigned dev bundle

Stage 2: Packaging the Signed Bundle into a DMG

Once the signed bundle exists, the second stage creates the distribution artifact.

DMG Creation with create-dmg

The cargo xtask macos dmg command (implemented in xtask/src/commands/macos/dmg.rs, lines 31-80) performs the following actions:

  1. Downloads a branded background image from https://assets.openlogi.org/dmg/dmg-background.tiff
  2. Invokes the external create-dmg tool to generate a ULMO-compressed disk image
  3. Configures Finder window geometry and positions the app icon on the background image

The command accepts the bundle path and output location:

cargo xtask macos dmg \
    --app target/release/bundle/osx/OpenLogi.app \
    --output target/release/OpenLogi.dmg \
    --sign-identity "$OPENLOGI_SIGN_IDENTITY"

Signing the Distribution Image

If a signing identity was provided to the bundle stage, the same identity must sign the final DMG. The sign_dmg function in dmg.rs (lines 91-96) executes codesign --force against the completed disk image. This creates a consistent cryptographic chain from the nested helpers through the app bundle to the outer DMG container.

Unsigned DMG creation remains available for ad-hoc testing by omitting the --sign-identity flag:

cargo xtask macos dmg  # Creates unsigned DMG for local testing

CI/CD Automation

The release pipeline defined in .github/workflows/release.yml automates this two-stage workflow for official distributions:

- name: Build production bundle
  run: cargo xtask macos bundle --channel production

- name: Package DMG
  run: cargo xtask macos dmg --sign-identity "$OPENLOGI_SIGN_IDENTITY"

The CI job reads the OPENLOGI_SIGN_IDENTITY secret from repository settings. If the identity is unset, the workflow produces unsigned artifacts suitable for testing but blocks production release gates.

Local Development vs. Production Releases

Understanding the distinction between ad-hoc and production builds prevents permission corruption on developer machines:

  • Production builds require the --channel production flag, which triggers the stamp function to inject the stable CFBundleIdentifier. These builds must be signed with a valid Apple Developer ID for distribution outside the App Store.
  • Development builds omit the channel flag, resulting in a dynamic bundle identifier. These never carry a signing identity and cannot overwrite production app permissions in macOS TCC databases.

Summary

  • Two-stage workflow: First cargo xtask macos bundle creates and signs the app, then cargo xtask macos dmg packages it into a distributable disk image
  • Identity stamping: The production channel identity must be applied in xtask/src/commands/macos/bundle/identity.rs before signing to preserve user permissions
  • Sign-inside-out: All nested helpers, the main app, and the final DMG must share the same signing identity, verified by the verify function
  • Compression: DMGs use ULMO compression via the create-dmg tool with branded backgrounds fetched from OpenLogi assets
  • Environment control: The OPENLOGI_SIGN_IDENTITY variable controls whether artifacts are signed or left unsigned for testing

Frequently Asked Questions

What happens if OPENLOGI_SIGN_IDENTITY is not set?

When the OPENLOGI_SIGN_IDENTITY environment variable and --sign-identity flag are both absent, both the bundle and dmg commands produce unsigned artifacts. The bundle builds normally but skips the codesign invocation, and the DMG packages the unsigned bundle without cryptographic sealing. This configuration is intended strictly for local development and CI testing pipelines.

Why must the production identity be stamped before signing?

According to the source code in xtask/src/commands/macos/bundle/identity.rs, macOS TCC (Transparency, Consent, and Control) permissions are bound to the CFBundleIdentifier. If a developer signs a bundle with a development identity first, macOS records permissions against that temporary identifier. When the app later updates with the production identity, macOS treats it as a completely different application, invalidating all previous user grants for microphone, screen recording, and accessibility access.

What is the "sign-inside-out" requirement?

Apple's signing architecture requires that every executable inside an app bundle—from the main binary to helper tools in Contents/Library/LoginItems/—carries the same cryptographic identity as the bundle itself. The verify function in identity.rs recursively checks all nested components, and the CI workflow aborts if any component lacks the production signature. This prevents "identifier collision" errors during notarization.

Can I customize the DMG background image?

Yes, though the default fetches from https://assets.openlogi.org/dmg/dmg-background.tiff. The dmg.rs implementation supports passing a custom background path to the underlying create-dmg invocation. For branded enterprise releases, modify the arguments passed to the background configuration in xtask/src/commands/macos/dmg.rs or replace the asset at the remote URL if using the default fetch behavior.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →