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:
- Downloads a branded background image from
https://assets.openlogi.org/dmg/dmg-background.tiff - Invokes the external
create-dmgtool to generate a ULMO-compressed disk image - 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 productionflag, which triggers thestampfunction to inject the stableCFBundleIdentifier. 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 bundlecreates and signs the app, thencargo xtask macos dmgpackages it into a distributable disk image - Identity stamping: The production channel identity must be applied in
xtask/src/commands/macos/bundle/identity.rsbefore 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
verifyfunction - Compression: DMGs use ULMO compression via the
create-dmgtool with branded backgrounds fetched from OpenLogi assets - Environment control: The
OPENLOGI_SIGN_IDENTITYvariable 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →