Breathing Index 🫁

Privacy

Last updated 11 August 2026

There is no server that knows you. No account, no login, no database of yours anywhere. Your logs are a value in your browser's storage on this device, and the only way they leave is if you export them yourself. The one server this app runs is a stateless relay for data sources that need an API key, or that publish a file too big to hand a phone — it receives your coordinates rounded to about 11 km (anything sharper is refused, in code), holds no state, keeps no logs, and its source is public. Opt-in accounts with encrypted sync are on the roadmap — when they ship, this page changes first, and local-only stays the default.

What stays on your device

Everything you type or tap. Ratings, confounder tags, notes, saved places, settings, the exposure readings captured with each entry, and the last good air reading kept for offline use. They live under these keys in this browser's localStorage, plus one marker in IndexedDB used to notice that the browser wiped the rest:

Clearing site data deletes all of it, permanently, and I cannot get it back for you — I never had it. Settings has an export button. Use it.

No cookies. Nothing here is a cookie, and the app sets none.

What gets sent, and to whom

Four other services see a request from your browser. That is the whole list.

Open-Meteo Air quality and weather. Sees your coordinates, rounded to three decimals (about 110 m), and nothing else.
The relay Pollen forecasts (Google), station measurements (AirNow, US only, off unless you turn it on in Settings), the satellite smoke map (NOAA HMS, North America), nearby air sensor readings (PurpleAir) and reference-monitor readings outside the US (OpenAQ) travel through the relay described above, so those sources see the relay's request, not your browser — and the relay itself never receives your location sharper than about 11 km. Its routes are /v1/pollen, /v1/airnow, /v1/smoke, /v1/purpleair and /v1/openaq, each taking nothing but those rounded coordinates, plus /v1/mold/stations and /v1/mold, which take no coordinates at all. Mold counts come from health departments and hospitals that post a number on a web page once a weekday morning; you choose a counting station by name in Settings, and the relay is told that station's id and nothing else. Every reader of that station gets the same answer, so the request says nothing about which of them you are.
BigDataCloud Turns those coordinates into the place name in the header. Same rounded coordinates, asked once per location and then cached.
Mixpanel Usage pings — the table below. Off if you turn it off.

Fonts and icons are served from this domain, so opening the app is not a request to Google or a CDN. There is no ad network, no tag manager, no embedded anything.

Usage pings, exactly

Turn them off in Settings and the analytics code is never even loaded. While they are on, these are the events. No rating, tag, note, place name, coordinate, or pollutant value appears in any of them.

Page view Automatic, on load and on every in-app navigation: the URL path and query string of the screen (/, /diary, /settings…).
Prediction viewed diaryEntries — how many entries your log holds stale — whether the reading shown was a cached one locationSourceauto, saved or default, never which place
Diary entry saved coldStart — whether the forecast was still unpersonalized saveMs — how long the save took totalEntries — how many entries after it Not the rating.
Conflict tagged kindsuperseded or unmodeled-trigger, the type of card, not the tag you chose
Intro dismissed choicelocation or manual
Diary eviction seen No properties. Records that a browser wiped somebody's logs.
Diary restored entries — how many entries the backup you loaded added
Persistence requested granted — whether the browser agreed to stop treating your logs as disposable cache

Mixpanel attaches its own defaults to every event: a random device id (not an account), browser and operating system name and version, screen size, referrer, and the current URL. It also records the city, region and country it works out from the network address your request arrives on — the same rough guess any server you contact can make. That is the only geography in a ping. The precise location you grant the app is never sent, and neither is any pollutant value, rating, tag, or note.

What the pings are for: knowing whether people can log an entry and whether anything is broken. Nothing is sold, shared, or joined to anything else. There is nobody to sell it to; this is one person's side project. My own devices tag their pings with a flag so I can subtract myself from the count.

Children

Not directed at children under 13, and since nothing identifying is collected, there is nothing of theirs for me to hold.

Changes

If the app ever sends something new, this page changes in the same commit — that is a rule I hold myself to, not a promise in the abstract. The date at the top is the last time it did.

Contact

Drew Hoover — drewhoover.com. The code is public at github.com/DrewHoo/breathing-index, so you can check any of this rather than take my word for it.