Chat with your Apple Health data.

openhealth turns your Apple Health export into seven short markdown files any LLM can read. Drop the zip, get the files, paste them into ChatGPT, Claude, or your local Ollama — and start asking real questions about how you actually sleep, train, and recover.

Drop your export here

How do I get my Apple Health data?

Six taps in the Health app. Nothing leaves your phone until you drop the zip here.

Apple Health Summary page Profile page Export All Health Data button Export confirmation dialog iOS share sheet with export.zip iPhone Camera scanning the desktop QR code

What do I get?

Seven short markdown files your chatbot can actually reason about:

Want to keep the AI side local too? Run it through Ollama.

ChatGPT and Claude are convenient — but if you'd rather your weight, sleep, and HR data never hit a third-party server, point a local model at it instead. Ollama is the easiest way: one binary, one model pull, ask questions over your own GPU.

  1. 1
    Install Ollama
    brew install ollama
    # or download from ollama.com/download
  2. 2
    Pull a model

    Anything with a 128k+ context window. llama3.1:8b runs comfortably on a 16 GB Mac. qwen2.5:14b or gpt-oss:20b if you've got more RAM.

    ollama pull llama3.1:8b
  3. 3
    Pipe in your bundle + the prompt

    Use --bundle in the CLI, or click Download bundled openhealth.md in the browser, then:

    cat prompt.md openhealth.md | ollama run llama3.1:8b

    Or open Open WebUI / Chatbox, attach the bundle, and chat over it like you would with ChatGPT.

Now your export, the parser, the bundle, the model, and the chat all run on hardware you own. Your network panel stays empty the entire time.

Your data never leaves this device.

Not as a principle — as a mechanism. Every byte of your export is parsed inside this browser tab. There is no "upload" endpoint because there is no server to upload it to.

What actually happens when you drop the zip

  1. 1
    The browser hands this tab a file reference.

    The standard Web File API passes a handle to the zip. No network request is made. DevTools' Network panel stays empty.

  2. 2
    A Web Worker streams-parses it.

    The zip is unzipped in-memory by fflate and the XML is fed through saxes — a streaming SAX parser that keeps peak RAM around 50 MB even on a full 200 MB export. The work runs on a background thread so the UI stays responsive.

  3. 3
    Seven markdown strings live in tab memory.

    We call URL.createObjectURL on each blob — that's a browser-local URL of the form blob:http://…. Clicking a download link triggers your browser's native Save As. Still local, still no network.

  4. 4
    Close the tab. Everything vanishes.

    No localStorage, no IndexedDB, no session caches of your data. The garbage collector reclaims every byte when the tab closes.

What we don't do

The one exception — phone-to-desktop handoff

If you scan the QR code to send your zip from your phone to this desktop browser, a tiny ~100-line Cloudflare Worker brokers the WebRTC handshake. It must see:

It never sees the zip. The file travels directly between your phone and desktop over an encrypted WebRTC DataChannel; the relay only helps them find each other. Source: packages/signaling — read it, it's shorter than this section.

Verify it yourself (takes a minute)

  1. Open your browser DevTools → Network tab. Tick Preserve log.
  2. Drop a zip onto the top of this page. Scroll through the request list.
  3. Every row should be either the page assets, the Google Fonts CSS, or a blob: URL when you click download. Nothing else.
  4. For the nuclear test: turn WiFi off, reload once from cache, and drop a zip. It still works.

Read the source: github.com/jonnonz1/openhealth · MIT-licensed · no minified blobs, no pre-built bundles committed. The site you're reading right now was built from the commit pinned in the repo.