Where Pomeroy connects
Nothing else. No crash reports, no error logs, no “usage statistics”, no
heartbeat, no font list, no screen size, no serial number, no advertising id.
One more address appears when you click Buy Pomeroy: the store’s checkout
page opens in your browser. That is your browser talking to the store, not
Pomeroy, which sends nothing and learns nothing about it.
Requests Pomeroy makes itself are checked against this list in code before a
connection is opened, and it refuses anything not on it. That is
NetworkPledge, and the test that drives every flow through a recorder is
NetworkPledgeTests. The update check and the counts are made by two libraries,
Sparkle and posthog-ios, which open their own connections. What holds those to
this page is described under each below.
The licence check
Pomeroy sends two fields and no others:license_key, the key from your purchase email, exactly as you typed it.instance_name, which isPomeroyfollowed by eight random characters, generated once on this Mac and kept. It is not your Mac’s name, serial number, hostname or account. It exists so the store can show you which activation to free when you move to a new Mac.
The update check
Sparkle askshttps://pomeroy.app/appcast.xml for the list of released
versions. It is a plain GET: no identifier, no licence key, no count. The
answer is signed with an Ed25519 key whose public half is baked into the app,
and a download whose signature does not match the bytes is thrown away
(ADR-0009).
Turn it off in Settings → General and Pomeroy never asks.
The weather question
Weather answers come from Apple’s WeatherKit, the same source the Weather app uses. When you ask about the weather, and only then, the framework sends Apple the coordinates of the place you named, and enough about the app itself (its identity and signature) for Apple to agree to answer. The weather actions take place names, never your location; the coordinates sent are “Clapham, London”, not where your Mac is. Apple’s own words on it: “Location is used only to provide weather forecasts and is not associated with any personally identifying information and is never shared or sold.” Like Sparkle and posthog-ios, WeatherKit opens its own connection, so it cannot be held to the in-code allowlist. Instead its address is written inNetworkPledge beside the others, and the test that compares this page against
that list fails if the two drift. Pomeroy never calls a third-party weather
service: a build Apple will not answer (one not signed by the paid team) gets no
weather at all, and says so, rather than a fallback that leaks your questions
elsewhere.
The four counts
They exist to answer one question: of the people who install Pomeroy, how many go on to buy it. There are four, there have only ever been four, and a test fails if the code and this page disagree.
Each one carries the event name, the moment it happened, the identifier
described below, a random id for that one message, and exactly these four
properties. That is the whole event, checked by pointing the library at a local
server and reading what it sent:
The identifier, stated plainly
The four counts share a random identifier generated on your Mac the first time Pomeroy needs one. It is a UUID from the system random number generator. It is not derived from your hardware: not your serial number, notIOPlatformUUID, not a MAC address, not your hostname, not your licence key.
It is not connected to your name, your email, or your purchase either.
It exists for one reason: without it, the four counts are four unrelated
numbers, and “how many people who install go on to buy” cannot be answered.
With it, they form one install’s timeline.
Being exact about what that means: this makes the counts pseudonymous, not
anonymous. Four events from this Mac can be told apart from four events
from another one. Nobody receiving them can work out whose Mac it is, and the
identifier is not readable by any other app or website. But it is an
identifier, and calling it anything else would be untrue.
You can throw it away. Deleting Pomeroy’s settings (defaults delete com.joesaunderson.pomeroy) discards it, and the next identifier is unrelated to
the last.
What is deliberately not there. No licence key. No hardware identifier.
No macOS version, no Mac model, no Mac name, no screen size, no language, no
time zone, no CPU. No tool names. No counts of your events, messages or mail.
Nothing you typed and nothing your assistant said.
About the counting library
The counts are sent by posthog-ios. Out of the box that library does a great deal more than count: it captures taps, screen views, application lifecycle events, feature-flag evaluations and person profiles, and it attaches roughly twenty-five properties to every event: the Mac’s model and friendly name, its screen size, its language, its time zone, whether the app was sideloaded, and more. Pointed at a local server with the collectors off but no filtering, it sends twenty-one properties Pomeroy never asked for, including this Mac’s exact macOS version, its model, its name, its language, its time zone and a session identifier. Pomeroy switches off every collector the library allows to be switched off, and then, because none of those twenty-one can be switched off, filters every event through an allowlist before it is queued: anything not in the four-row table above is dropped. The same local-server check with the filter in place sends the four properties above and nothing else. That filter keeps what is named rather than removing what is known, so a property added by a future version of the library is dropped by default instead of shipped by accident. The code isPostHogTelemetrySink, it is the only file in Pomeroy allowed to see the
library, and a test fails if any switch goes unapplied (ADR-0008).
One thing cannot be switched off: when the library starts, it fetches its own
settings from us-assets.i.posthog.com. That request carries Pomeroy’s
PostHog project token in the address and nothing else. No identifier, no
event, nothing about you. It is on the table above because it happens and you
should know it happens.
So Pomeroy does not start the library when it launches. It starts it the
first time a count is actually going to be sent, which is after the wizard
has been closed once. Until then the library never starts, and that request
never happens either.
The library also writes three things under ~/Library/Application Support/com.joesaunderson.pomeroy/: two identifiers of its own (which never
leave your Mac, because the only request that would carry them is the
feature-flag call, and that is switched off), and a queue folder holding counts
waiting to be sent. Those queued files contain the count as it will be sent,
after the allowlist has already been applied, so the filtering above happens
before anything touches your disk rather than only before it reaches the
network.
What can reach Pomeroy
Everything above is about what goes out. This section is the other direction, and it is here because a buyer reading a page called “What leaves your Mac” deserves to find it rather than discover it. There are two ways an assistant reaches Pomeroy. Direct, which is the default and what almost everything uses: the extension inside Claude Desktop, or the small pipe inside Pomeroy’s own app bundle. Neither is a port. Both meet Pomeroy at a file in your own home folder, mode0600 inside a 0700 directory, so the only things that can
reach it are programs already running on this Mac as you. macOS decided that,
not Pomeroy, which is why nothing here asks you to approve a connection.
The local address, for an assistant that cannot start a program on this
Mac: one running in a container, or one that takes a URL and nothing else.
That one is a listening port, and four things are true of it.
- It is off until you switch it on. There is no launch default, and no environment variable can force it open.
- It listens only on this Mac. The listener binds
127.0.0.1and::1and refuses every other interface. There is no bind setting and no interface picker. A test pins it and names ADR-0014 in its failure message, so the next person tempted to widen it reads the reasoning first. - How it is guarded is yours to choose, from three modes: Token, the default, where one secret opens the address and Pomeroy writes it into the settings of every assistant it can reach; Approval, where there is no shared secret and each assistant asks for its own, which you grant in a Pomeroy window; and Open, where nothing is checked at all. Open is offered because a container or a script can have a real reason to need it, and switching to it asks you first, in a sheet that names what becomes reachable. On this Mac the credential is the whole boundary: a local connection carries no identity macOS will vouch for, so there is nothing else standing behind it.
- It sends nothing anywhere. An inbound listener is not an outbound
request. The hosts table above is unchanged by this feature, and
networkDiscipline.test.tsstill asserts it against the allowlist in code.
What the other end can see anyway
Being straight about the limits: any server you connect to sees that a connection arrived, and from roughly where, the way every server does. The$ip and $geoip_disable instructions above stop PostHog storing your
address on the event or turning it into a location; they cannot stop its
servers seeing the connection. Deleting Pomeroy’s settings throws the
install identifier away; it does not un-send a count that already left.
Pomeroy’s own requests do not make it worse: they carry a fixed User-Agent
of Pomeroy and Accept-Language: en, replacing the ones macOS would attach,
which name your exact macOS build and your language. They follow no
redirects, keep no cookies and write nothing to disk.
The counting library sets its own User-Agent of posthog-ios/<version> on
its two requests, which names the library and its version and nothing about
you or your Mac.