AppPulse updates the apps on your Mac, but only when it can prove the update comes from the same developer.
AppPulse finds the third-party apps on your Mac, checks for newer versions, and installs them after a chain of checks. The last check confirms the download was signed by the same developer as the app you already have. Every step is logged. There’s no telemetry, no account and no sudo.
Status
In development
Runs on
macOS
Built with
Swift 6, SwiftUI, Security framework
Why it exists
A valid code signature only tells you that some registered developer signed a file. It doesn’t tell you that the same developer made the app you’re about to replace. An updater that trusts its metadata and then checks only the signature can be pointed at the wrong app. And many apps have no reliable update channel at all. AppPulse is built around being honest about both problems.
What it does
Updates from two sources
It reads Homebrew’s cask data as plain data, without running brew to check, and Sparkle update feeds verified with EdDSA.
Installs with rollback
The old app is cloned before the new one is swapped in, and any failure swaps it back.
Says so when it can’t compare
When two version schemes don’t line up, it reports them as incomparable instead of guessing. Downgrades are shown, never applied.
Shows its work
A summary of your library, a step-by-step install timeline, and a log of every decision it makes.
Control per app
Skip, snooze, ignore or pin to a major version. Checks run on a schedule while it’s open, and silent installs are opt-in behind a five-part gate.
How it’s built
A valid signature is not proof of identity
The first time AppPulse sees an app, it records the installed copy’s Team ID and treats it as the pin. From then on, a different Team ID is a hard stop, whatever the metadata says. The check still holds if every upstream source is wrong.
Authenticated sources first
Sparkle with EdDSA comes first, then Homebrew’s cask data, then Sparkle without a signature. The EdDSA key already sits inside the installed app, so a network attacker can’t swap it. Cask data can’t be the integrity anchor, because some casks skip the checksum entirely.
What it refuses to do
It never runs .pkg installers, whose scripts run as root. It never uses sudo, never replaces a running app, and never launches an app to see whether an update worked. A disk image that needs a license agreement becomes a download-only update.
Boundaries a script enforces
The core library can’t import AppKit or touch the file system, network or processes outside its adapter layer, and a CI script fails the build if it tries. Every process call passes an argument array, never a shell string. The Hardened Runtime is on, with no entitlements.
1
Preflight
The app isn’t running, no privileges are needed, and there’s room on disk.
2
Extract
Into a fresh staging folder. An archive that tries to escape it through a symlink or hard link is rejected.
3
Match
Bundle ID, version and architecture must agree with the app already installed.
4
Signature
codesign --strict, then Gatekeeper’s own assessment.
5
Team ID
The developer must match the one pinned from the installed copy. A mismatch is a hard stop.
6
Swap
The current app is cloned for rollback, then swapped atomically with renamex_np.
7
Verify again
The signature is checked a second time, at its final path.
8
Release
Only then is the quarantine flag removed.
The install order. Nothing in /Applications changes until the
first five checks pass, and a failure after the swap puts the old app back.
On the record
Tests
272 automated tests, 19 of them against real system tools
End to end
An integration test installs a real app update over a copy of the installed version, then rolls it back
AppPulse is in development. There is no release date yet.