Small tools in your macOS menu bar: tasks, Google Analytics, Marketplace watches, and how busy a place is right now
Swift
1
130 commits
updated Sep 30, 2026
Small tools that live in your macOS menu bar.
Perch is a host. The tools themselves are plugins: today there's Tasks, a todo list with the current task in the menu bar; Analytics, which puts your Google Analytics numbers a click away; Market, which watches Facebook Marketplace searches; Busy, which shows how busy a place is right now; Server, which watches the health of machines you run; and Internet, which tells you whether your connection is actually working. More can be added without disturbing what's already there.
▶ Watch the 47-second demo, with sound
Every plugin is a tab in the same panel, and the menu bar shows whichever tab you picked:
Grab the latest Perch.zip from the Releases page,
unzip it, and drag Perch.app into your Applications folder.
Requires macOS 14 (Sonoma) or later. Apple Silicon and Intel are both supported.
Releases are signed with a Developer ID certificate and notarized by Apple, so
Perch.app opens with a normal double-click. No Gatekeeper workaround is
needed.
(Releases before v2.2.1 were signed ad hoc and did need right click › Open the first time.)
Tasks declares no capabilities: everything it stores stays on your Mac.
Your GA4 numbers in the menu bar, without opening a browser or running a script.
Analytics declares network and credentials: it talks to Google, and it
holds a service-account key. It is the first plugin in Perch to do either.
How busy a place is right now — Google's live busyness, in the menu bar. Made for picking a gym time.
Busy declares network. There is no official API for live busyness, so it
loads the Google search page for each place in a hidden browser and reads the
Popular times box — the same thing you'd see in a browser. It never signs in
to anything. Google changes its page from time to time; when that breaks the
reader, the panel says so rather than showing nothing.
…@….iam.gserviceaccount.com) as a Viewer.Discovery uses whatever name the property carries in GA, which is often the nickname someone typed into the console years ago rather than the domain. The name is editable in Settings; renaming is local and nothing is written back to Google.
The ? button beside Credentials in Settings has these steps with links, for when you need them and this file isn't open.
Don't back the key file up — make a new one. Google hands a service-account key over exactly once, so a copy in cloud storage is a credential sitting somewhere else you have to defend, in exchange for saving the two minutes below. Everything that took setting up survives the machine anyway: the service account, its access to your properties, and the enabled APIs all live in Google.
If you do want a copy, put it in a password manager rather than a file — and never in a folder that might one day become a git repository.
The key goes into your login Keychain. Perch reads the file you pick once and never copies it or keeps a reference to it.
How your servers are doing, in the menu bar. Made for watching a small VPS you would otherwise only check after something broke.
Server declares network and credentials: it polls each machine over HTTPS,
and a password for a server behind basic auth is kept in your login Keychain
rather than in Perch's settings file.
Server does not log in over SSH. Each machine runs a small agent, vpsstat,
which reads that machine's own /proc and container cgroups and serves them as
JSON on /api/now. Perch polls that endpoint every minute.
The agent is a single dependency-free Python file, runs under systemd with a
64 MB memory cap, and costs about 26 MB of RAM and well under 1% of a core. Put
it behind TLS and basic auth (Caddy does both in four lines) and give Perch the
address, for example https://status.example.com.
Perch keeps its own short history, so a card's sparkline covers only what Perch has watched. The agent's own dashboard has the full 24 hours and 30 days; open it from a server's ••• menu.
Whether the internet is working right now, and how well, without opening a browser to find out.
ping rather than counting TLS
handshakes.Internet declares network: while enabled it sends three small HTTPS requests
every 30 seconds, and checks again straight away when you change networks.
Click the gear icon in the panel.
| Pane | What's in it |
|---|---|
| General | Title display and length, launch at login, global hotkey |
| Plugins | Enable or disable each plugin, and see what each one can access |
| Tasks | Per-plugin settings, when a plugin has any |
| Analytics | Service-account key, watched properties, which one is primary |
| Market | City, search radius, how often to check |
| Busy | How often to check |
Each plugin gets its own directory inside Perch's sandbox container, on your Mac only:
~/Library/Containers/org.ahlab.Perch/Data/Library/Application Support/Perch/Plugins/<plugin-id>/
Tasks stores a plain JSON file there and nothing else. If that file is ever
unreadable, Perch keeps a .bak copy beside it and tells you, rather than
silently starting empty.
Analytics is the exception, and it is the honest illustration of the limit here: macOS grants permissions to an app, not to individual plugins. Because Analytics needs the network, the whole binary carries the network entitlement, including the plugins that would never use it. Perch's Settings window discloses what each plugin does, but disclosure is all it can offer — it cannot sandbox one plugin away from another's permissions. Analytics sends nothing but authenticated requests to Google's own APIs, and stores its key in the Keychain rather than in the container.
Server follows the same rule: it talks only to the addresses you give it, and
each server's password goes to your login Keychain, never to the JSON document
beside it. A password pasted into the address bar as https://user:pass@host
is stripped out before the address is stored.
The cup in the panel's footer keeps your Mac awake when the lid is closed. It
is the same setting as sudo pmset -a disablesleep 1, without the Terminal.
The helper does one thing, accepts requests only from Perch, and exits a few seconds after each use.
Perch is a SwiftUI app built around MenuBarExtra. The Xcode project is
generated with XcodeGen, so only
project.yml is tracked in git.
brew install xcodegen
git clone https://github.com/prasanthsasikumar/perch.git
cd perch
xcodegen generate
open Perch.xcodeproj
Run the tests with:
xcodebuild test -project Perch.xcodeproj -scheme Perch -destination 'platform=macOS'
PerchKit/ The public plugin API. Knows nothing about the host.
Plugins/TasksPlugin/ The Tasks plugin: model, store, views
Plugins/AnalyticsPlugin/ The Analytics plugin: GA4 client, auth, store, views
Plugins/MarketPlugin/ The Market plugin: Marketplace scraping, poller, store, views
Plugins/BusyPlugin/ The Busy plugin: Google popular-times scraping, store, views
Plugins/ServerPlugin/ The Server plugin: vpsstat agent client, alert rules, store, views
PerchKeepAwake/ The companion app that registers the keep-awake helper
PerchHelper/ The root helper behind the keep-awake toggle
Shared/ What the app, the companion and the helper all compile
Perch/
PerchApp.swift MenuBarExtra scene, plugin instantiation
Host/ Registry, panel chrome, menu bar label
Views/ Settings window
Support/ Launch at login, hotkey state, title truncation
PerchTests/ Unit tests for the kit, the plugin, and the host
Dependencies are KeyboardShortcuts for the sandbox-safe global hotkey and MenuBarExtraAccess for opening the panel programmatically.
PerchKit is the contract. A plugin is a Swift package depending on it, with one
type conforming to PerchPlugin, added to the array in PerchApp.makePlugins().
The API is 0.x and unstable — it will change once a second plugin proves the shape is right. Plugins are compiled in rather than loaded at runtime.
MIT. See LICENSE.
Swift
90.0%
HTML
5.7%
Python
3.3%
JavaScript
1.0%
Small tools in your macOS menu bar: tasks, Google Analytics, Marketplace watches, and how busy a place is right now
Swift
1
130 commits
updated Sep 30, 2026
Small tools that live in your macOS menu bar.
Perch is a host. The tools themselves are plugins: today there's Tasks, a todo list with the current task in the menu bar; Analytics, which puts your Google Analytics numbers a click away; Market, which watches Facebook Marketplace searches; Busy, which shows how busy a place is right now; Server, which watches the health of machines you run; and Internet, which tells you whether your connection is actually working. More can be added without disturbing what's already there.
▶ Watch the 47-second demo, with sound
Every plugin is a tab in the same panel, and the menu bar shows whichever tab you picked:
Grab the latest Perch.zip from the Releases page,
unzip it, and drag Perch.app into your Applications folder.
Requires macOS 14 (Sonoma) or later. Apple Silicon and Intel are both supported.
Releases are signed with a Developer ID certificate and notarized by Apple, so
Perch.app opens with a normal double-click. No Gatekeeper workaround is
needed.
(Releases before v2.2.1 were signed ad hoc and did need right click › Open the first time.)
Tasks declares no capabilities: everything it stores stays on your Mac.
Your GA4 numbers in the menu bar, without opening a browser or running a script.
Analytics declares network and credentials: it talks to Google, and it
holds a service-account key. It is the first plugin in Perch to do either.
How busy a place is right now — Google's live busyness, in the menu bar. Made for picking a gym time.
Busy declares network. There is no official API for live busyness, so it
loads the Google search page for each place in a hidden browser and reads the
Popular times box — the same thing you'd see in a browser. It never signs in
to anything. Google changes its page from time to time; when that breaks the
reader, the panel says so rather than showing nothing.
…@….iam.gserviceaccount.com) as a Viewer.Discovery uses whatever name the property carries in GA, which is often the nickname someone typed into the console years ago rather than the domain. The name is editable in Settings; renaming is local and nothing is written back to Google.
The ? button beside Credentials in Settings has these steps with links, for when you need them and this file isn't open.
Don't back the key file up — make a new one. Google hands a service-account key over exactly once, so a copy in cloud storage is a credential sitting somewhere else you have to defend, in exchange for saving the two minutes below. Everything that took setting up survives the machine anyway: the service account, its access to your properties, and the enabled APIs all live in Google.
If you do want a copy, put it in a password manager rather than a file — and never in a folder that might one day become a git repository.
The key goes into your login Keychain. Perch reads the file you pick once and never copies it or keeps a reference to it.
How your servers are doing, in the menu bar. Made for watching a small VPS you would otherwise only check after something broke.
Server declares network and credentials: it polls each machine over HTTPS,
and a password for a server behind basic auth is kept in your login Keychain
rather than in Perch's settings file.
Server does not log in over SSH. Each machine runs a small agent, vpsstat,
which reads that machine's own /proc and container cgroups and serves them as
JSON on /api/now. Perch polls that endpoint every minute.
The agent is a single dependency-free Python file, runs under systemd with a
64 MB memory cap, and costs about 26 MB of RAM and well under 1% of a core. Put
it behind TLS and basic auth (Caddy does both in four lines) and give Perch the
address, for example https://status.example.com.
Perch keeps its own short history, so a card's sparkline covers only what Perch has watched. The agent's own dashboard has the full 24 hours and 30 days; open it from a server's ••• menu.
Whether the internet is working right now, and how well, without opening a browser to find out.
ping rather than counting TLS
handshakes.Internet declares network: while enabled it sends three small HTTPS requests
every 30 seconds, and checks again straight away when you change networks.
Click the gear icon in the panel.
| Pane | What's in it |
|---|---|
| General | Title display and length, launch at login, global hotkey |
| Plugins | Enable or disable each plugin, and see what each one can access |
| Tasks | Per-plugin settings, when a plugin has any |
| Analytics | Service-account key, watched properties, which one is primary |
| Market | City, search radius, how often to check |
| Busy | How often to check |
Each plugin gets its own directory inside Perch's sandbox container, on your Mac only:
~/Library/Containers/org.ahlab.Perch/Data/Library/Application Support/Perch/Plugins/<plugin-id>/
Tasks stores a plain JSON file there and nothing else. If that file is ever
unreadable, Perch keeps a .bak copy beside it and tells you, rather than
silently starting empty.
Analytics is the exception, and it is the honest illustration of the limit here: macOS grants permissions to an app, not to individual plugins. Because Analytics needs the network, the whole binary carries the network entitlement, including the plugins that would never use it. Perch's Settings window discloses what each plugin does, but disclosure is all it can offer — it cannot sandbox one plugin away from another's permissions. Analytics sends nothing but authenticated requests to Google's own APIs, and stores its key in the Keychain rather than in the container.
Server follows the same rule: it talks only to the addresses you give it, and
each server's password goes to your login Keychain, never to the JSON document
beside it. A password pasted into the address bar as https://user:pass@host
is stripped out before the address is stored.
The cup in the panel's footer keeps your Mac awake when the lid is closed. It
is the same setting as sudo pmset -a disablesleep 1, without the Terminal.
The helper does one thing, accepts requests only from Perch, and exits a few seconds after each use.
Perch is a SwiftUI app built around MenuBarExtra. The Xcode project is
generated with XcodeGen, so only
project.yml is tracked in git.
brew install xcodegen
git clone https://github.com/prasanthsasikumar/perch.git
cd perch
xcodegen generate
open Perch.xcodeproj
Run the tests with:
xcodebuild test -project Perch.xcodeproj -scheme Perch -destination 'platform=macOS'
PerchKit/ The public plugin API. Knows nothing about the host.
Plugins/TasksPlugin/ The Tasks plugin: model, store, views
Plugins/AnalyticsPlugin/ The Analytics plugin: GA4 client, auth, store, views
Plugins/MarketPlugin/ The Market plugin: Marketplace scraping, poller, store, views
Plugins/BusyPlugin/ The Busy plugin: Google popular-times scraping, store, views
Plugins/ServerPlugin/ The Server plugin: vpsstat agent client, alert rules, store, views
PerchKeepAwake/ The companion app that registers the keep-awake helper
PerchHelper/ The root helper behind the keep-awake toggle
Shared/ What the app, the companion and the helper all compile
Perch/
PerchApp.swift MenuBarExtra scene, plugin instantiation
Host/ Registry, panel chrome, menu bar label
Views/ Settings window
Support/ Launch at login, hotkey state, title truncation
PerchTests/ Unit tests for the kit, the plugin, and the host
Dependencies are KeyboardShortcuts for the sandbox-safe global hotkey and MenuBarExtraAccess for opening the panel programmatically.
PerchKit is the contract. A plugin is a Swift package depending on it, with one
type conforming to PerchPlugin, added to the array in PerchApp.makePlugins().
The API is 0.x and unstable — it will change once a second plugin proves the shape is right. Plugins are compiled in rather than loaded at runtime.
MIT. See LICENSE.
Swift
90.0%
HTML
5.7%
Python
3.3%
JavaScript
1.0%