macOS · distribution · Pier · Diskly

Skipping the App Store: Signing, Notarization, and Self-Updating for macOS Apps

How Pier and Diskly ship independently: Developer ID, notarytool, Sparkle, and a Homebrew tap.

Neither of my two macOS tools, Pier and Diskly, is on the Mac App Store. Both ship independently. This post documents the whole pipeline.

Why not the App Store

It’s not that I don’t want to–I can’t: Pier’s core features are inspecting ports, viewing processes, and killing processes, which rely on lsof and ps to read information about other processes. The App Sandbox explicitly forbids this, and sandboxing is a hard requirement for MAS. Diskly needs to scan entire disks, which also conflicts with the sandbox model.

The cost of independent distribution is handling the trust chain, updates, and payments yourself. The upside: features aren’t crippled by the sandbox, you set your own pricing and refund policy, and there’s no 30% cut.

The trust chain: sign + notarize + staple

For apps outside the store, macOS requires three steps at minimum. Skip any one of them and Gatekeeper blocks the app when users open it:

  1. Developer ID signing: codesign signs the entire .app with a Developer ID Application certificate;
  2. Notarization: xcrun notarytool submit uploads the DMG to Apple for scanning, with results back in a few minutes;
  3. Stapling: xcrun stapler staple attaches the notarization ticket to the DMG, so verification passes even when users are offline.

The whole flow is scripted; releasing means running a single make command.

Self-updating: Sparkle + appcast

No store means no automatic updates–you have to bring your own. Sparkle is the de facto standard: the app embeds an appcast.json URL, checks for new versions on launch, and users update with a single click. Installers live in a public GitHub Releases repo that the appcast points to–no download server of your own needed.

Install channels: website + Homebrew

Beyond downloading the DMG from the website, developer users prefer brew install --cask. Maintain a homebrew-tap repo, bump the version number and sha256 in the Cask on every release, and you get an extra distribution channel for free.

Licensing

Both apps are one-time purchases with multi-device licenses and a 14-day trial. License validation results are stored in the Keychain. The lesson: anchor the trial to the first-launch timestamp and guard against clock rollback, and don’t overcomplicate the rest–you’ll never stamp out piracy, so don’t punish the 99% who pay to stop the 1% who won’t.

Wrapping up

Once the pipeline is set up, shipping a new version takes about ten minutes: build, sign, notarize, upload to Releases, update the appcast and Cask. For an indie developer, it’s a one-time investment amortized over the long run.