Vercel Pulse

Your Vercel deployments and Cloudflare analytics, live in the macOS menu bar.

Download for macOS

Version 1.6.0 (build 7) · 3.6 MB · released 2026-10-28 · macOS 14 or later

What's new

Added

  • Automatic updates, via Sparkle. The app checks daily, and Preferences gains an Updates section with a "Check Now" button and a toggle for automatic checks. Pinned project menus get a "Check for Updates…" item. This is the first version that can update itself — 1.5.0 and earlier have to be replaced by hand once.
  • LICENSE (proprietary) and README.md.

Fixed

  • Stale status was shown as if it were current. A project kept its last known deployment while every poll failed, and the error was only ever surfaced for projects that had no deployment at all — so after a revoked token or a dropped network, the popover went on showing a confident green "Live" nobody had confirmed in an hour. Failing rows now carry a warning and a dimmed status, a "can't reach Vercel" banner appears when everything is failing, and pinned menu bar items show the error instead of hiding it behind the last known deployment.

Changed

  • Accounts with more than 100 projects now load all of them — the project list follows Vercel's pagination cursor instead of stopping at the first page.
  • Cloudflare zones are matched to projects by exact base domain. The old rule used a substring test, so a project called "app" claimed the zone "myapp.com", and whichever project sorted first won.
  • The notification permission prompt now appears on the first deployment state change rather than at launch, where it had no context.
  • Distribution is now a DMG, not a zip. Unzipping with Finder strips the stapled notarization ticket, which forces Gatekeeper to phone home on first launch. The DMG keeps the ticket and gives the normal drag-to-Applications install. The zip is now only the transport used to submit to Apple.

Internal

  • The release pipeline was rebuilt around the nine-stage process from the Spelko handbook: sign inside-out → notarize + staple the app → build the DMG → sign + notarize + staple that too → Sparkle signature strictly after stapling → appcast → deploy the site last → verify with cache-busting polls.
  • scripts/sign-release.sh signs Sparkle's helpers plain and innermost-first and the app last with entitlements, never --deep, and finishes by running Sparkle's Autoupdate to assert the kernel didn't kill it (exit 137). That failure mode breaks in-app updates silently and permanently for everyone on the affected version, and nothing else detects it.
  • scripts/make-appcast.sh refuses to run on an unstapled DMG, since stapling changes the file size and a length measured earlier makes Sparkle reject the download for every user.
  • scripts/verify-release.sh proves a published release is reachable, polling with Cache-Control: no-cache and checking an older DMG too.
  • A test suite (30 tests) covering the pure logic that has broken before: timestamp formatting, build duration, domain selection, zone matching and the stale-status rules. swift test, and release.sh gates on it.
  • release.sh automates the whole release: checks, version bump, tests, signed and notarized build, appcast regeneration, commit and tag.
  • Both scripts prefer a stored notarytool keychain profile over environment variables, so the Apple app-specific password is entered once, ever.
  • Sparkle is vendored in Vendor/Sparkle/ rather than fetched as a remote package; SwiftPM's artifact download hangs on the build machine.
  • build-app.sh embeds and signs the framework inside-out. --deep signing, which it used before, mis-signs Sparkle's XPC services and embedded Updater.app in a way that only shows up when an update is attempted.
  • The update feed and DMGs are hosted on Vercel at vercelpulse.loxo.hr, from the static site/ directory. make-appcast.sh also rewrites the download page, so the page and the feed can't disagree about the current version.
  • release.sh edits Info.plist in place instead of with PlistBuddy Set, which rewrote the whole file and dropped every comment in it.