I built a thing that watches your GitHub account and locks your phone. Written down like that, it sounds like the setup for a data grab. So before you hand OffMain the keys, I want to put the whole inventory in plain language: what we store, where it lives, and why the mechanism needs so little. The privacy policy is the formal version. This is the same set of facts, told the way I would want them told to me.
The whole list fits in one section
OffMain has a backend. It is the source of truth for your minutes, so some things have to live on the server, tied to your account:
- Your GitHub identity. User id, username, the emails connected to your account, and your time zone.
- Your OAuth tokens. The access and refresh tokens GitHub issues when you sign in, encrypted at rest with AES-256-GCM.
- Your contract. Which sites and app categories are blocked, what each signal pays in minutes, and the hours it runs.
- Your minutes ledger. A row for every earn, every spend, and every grace top-up.
- Your unlock sessions. When you spent minutes and how long the break ran.
- Your GitHub activity, as events. Repository name, event kind, an identifier, a timestamp. For a commit, three more numbers: how many files it touched, how long its message ran, and whether it was a force-push.
Plus the per-device basics that keep your account working: platform, a push token, and sign-in sessions stored as hashed tokens.
That is the inventory for the product itself. The beta adds one more stream, and it gets its own section below.
We never read your code
The activity events deserve a closer look, because “we watch your GitHub” is the part a reasonable person squints at. Here is the boundary: we store that an event happened, never what it contained. Not your code, and not a word you wrote in a commit message or an issue. Metadata and counts only.
The reason is mechanical, not virtuous. To pay out a commit, OffMain needs to know a commit happened, in which repository, and when. It prices the event, so mostly it needs the fact of the event, and our servers never ask GitHub for a diff or a file.
One honest exception, because you would find it in the schema anyway. A webhook hands us the commit message whether we want it or not. Before we throw it away we measure two things: how many characters it ran to, and how many files the commit touched. Those numbers, plus a force-push flag, are how the backend notices someone farming minutes with fifty one-character commits. We keep the numbers. The text goes in the bin, and none of it is ever written down.
I’ll be real: the strongest privacy feature is not needing the data in the first place.
Two things never leave your devices
On iPhone, the apps you pick to block are stored as opaque tokens. That is Apple’s design: iOS hands us tokens it never translates, so we could not learn your app names even if we wanted to. They stay on the device and are never sent to the server.
On the Mac, the content filter decides locally whether a site is blocked. Your browsing never leaves the machine. The server tells the Mac what the contract says; the Mac does the watching.
In the apps, nobody else is in the room, with one exception
The apps carry no ads and no tracking SDKs. We do not sell or share your data, and we do not profile you.
The one third party that sees anything is Sentry, which gets crash and error reports so I can fix what breaks. A report carries your account id plus device and OS metadata, and everything else stays out: the apps and sites you block, usage timings, session and push tokens, and any GitHub content are scrubbed before it leaves. Crash reporting is on by default in the apps, with a settings toggle to turn it off. In the browser extension it is off unless you turn it on.
The beta phones home once a day, and you can read the transcript
During the beta, each device sends one daily summary so I can tell whether the block actually holds up in real use. It carries counts and coarse time-of-day buckets. App names and browsing history never ride along, and the timestamps are rough buckets, not clock times.
A switch in settings turns it off. A local log on the device shows every payload sent, so you can check my work instead of taking my word. The summaries land in our own database, not in anyone’s analytics product.
The website counts visits now, and that is a change of mind
The first version of this post ended the section above with “No third-party analytics, ever.” I meant it when I wrote it. It is no longer true for the website, and you should not have to diff the page to find that out.
Here is what changed. The site had no numbers at all. I could not tell whether anyone read this post, or whether the folks who did went on to join the waitlist, and I was making calls about the landing page on taste alone. So I turned on PostHog. On the website. Not in the apps.
The version I turned on writes nothing to your browser. No cookies, nothing in localStorage. There is nothing stored on your machine to consent to, which is also why you did not get a cookie banner on the way in. PostHog hashes your IP and browser on its own servers and rotates that hash every day, so I get pageviews, referrers, scroll depth, and a waitlist conversion rate, and I cannot tell whether you have been here before. Session replay is off, and it stays off. Your email never goes near any of it.
I would rather rewrite a sentence in public than quietly ship an SDK and hope nobody opens devtools. Still true: the apps send nothing to any third-party analytics service.
Leaving actually deletes things
Disconnect GitHub and the stored tokens are deleted. Delete your account and everything above goes with it. In-app deletion has not shipped yet; until it does, email privacy@offmain.dev and I will delete it. And revoke OffMain from the GitHub side too, under your account’s application settings, so the door is closed from both ends.
So, what OffMain knows about you: that you did the work, which repo it was in, and when. What the work said is yours.