Hawser: sell your Mac app outside the App Store
For Mac developers selling outside the App Store
Everything between your Mac app and a paying customer.
Licence keys that survive a flight and a refund. Signed, notarised, auto-updating DMGs from one command. And 35 checks that stop the release which would have failed silently on a Mac you don't own.
One-time purchase · Unlimited apps · Kept current as Apple moves
Watch the full run
The full run from certificate to notarised DMG, in order.
The log is the tool's own output, word for word.
The App Store does five jobs.<br>Out here, they're yours.
Sell from your own site and you keep 100% minus payment fees, own your customers, and escape the sandbox. In exchange, Apple hands you this list and no tooling.
The jobHandled aloneWith the kit<br>Menu bar app shellNo Xcode template exists; tutorials are outdated, LLMs suggest deprecated APIsWorking skeleton, macOS 13+<br>NotarizationXcode notarises the app. The DMG you actually ship is a separate submission, and every release repeats the GUI by handOne script, both submissions, signature flags checked before the round-trip<br>DMG packaginghdiutil incantations; the DMG itself must be notarized too, which most guides skipSigned, notarized, stapled<br>Auto-updatesSparkle setup, EdDSA keys, appcast hosting, three silent failure modesWired, with delta updates<br>License keysVerification API, offline handling, refund handling, keychain storageGumroad flow, 14-day grace
What's in the box
The licence layer Gumroad key verification, storage in the Keychain, 14-day offline grace, and defined behaviour for refunds and revoked keys. Everywhere else on this page a mistake costs you an afternoon. Here it costs revenue, and you hear about it months later from the customer it locked out.
The Hawser app Unlocks your kit, reads your Developer ID out of the keychain, takes the notary key from a file dialog, and writes the config for you. No text editor required to get shipping.
Config.xcconfig The one file you edit. Name, bundle id, team, versions, update feed, product id. Everything follows it.
ship.sh Build → sign → notarize → staple → DMG. Verifies its own signature flags before wasting a round-trip to Apple, and refuses to ship what would silently fail.
appcast.sh Generates your signed update feed. Rejects an unsigned one, a failure Sparkle's own tooling lets through without a word.
Five guides Written from measured runs, not documentation folklore. The troubleshooting page is real incidents with exact fixes.
# you edit one file:<br>Config.xcconfig<br>ShipKit.xcodeproj<br>Sources/<br>ExampleFeatureView ← your app goes here<br>SettingsView launch-at-login, tabs<br>UpdaterView Sparkle wired<br>License* Gumroad + grace period<br>scripts/<br>ship.sh one command → DMG<br>appcast.sh signed update feed<br>docs/<br>01-quickstart … 05-troubleshooting
You could build this yourself.<br>Here's what you'd be finding out.
Nothing here is proprietary. Every command is public, and the ten worst ones are written up on this site for free. What you'd be buying is knowing which ones matter before one of them costs you a release.
Building it yourself
23<br>Dead ends between a working app and a customer who can open it. We wrote each one down the day we hit it.
10 written up in full on this site, free: the ones that cost the most to find
2 notarization submissions rejected for problems that looked perfectly fine locally
1 trap we only found after shipping: our own release, cracked in three commands the next day
Starting from Hawser
61s<br>Source to signed, notarised, stapled DMG. Warm run, measured across three cold installs.
1 file to configure: name, bundle id, team, versions, update feed
1 command to ship, with both notarization submissions included
35 points where the scripts stop and print the fix instead of a status code
Measured July 2026 on a real machine and a real Developer account. Not projected, not "up to". The long version is the ten traps, free and complete.
We fell in the holes so you don't
Three of the ten. Any one of these costs a lost afternoon, or worse, ships broken silently.
The default that expires your certificate
Apple's certificate page pre-selects "Previous Sub-CA". Accept it and your Developer ID dies in February 2027 no matter what the expiry says. The guide walks you past it; ship.sh's error text warns about it too.
The update feed that signs nothing
Add Sparkle after your first release and its own tool generates an unsigned feed with no warning . Updates then fail silently on every user's machine. appcast.sh verifies the signature and refuses to let it through.
The error that names nothing
errSecInternalComponent is codesign's way of saying "keychain permissions". It just never mentions keychains, or permissions. You get the one-line fix in the error message itself, when it happens, not after two hours of searching.
Read all ten, with the exact fix for each →
Building it once is the easy half.
Apple has changed the rules of this pipeline...