Skip to content

Privacy, checked

Don't take our word for it.

A camera app earns the right to stay open by being checkable, not by promising. This page lists exactly what BetterPosture reads, what it keeps on your Mac, and the connections it can make. Then it shows you how to confirm each one yourself.

  • The posture detector makes zero network requests
  • The app connections are listed with their purpose
  • Every table is copied from the app, not written for marketing

What one reading contains

Every five minutes by default (you can change this in Settings) the app opens the camera at 640 by 480, hands the picture to the posture detector, keeps thirteen numbers, and closes the camera. While a nudge is on screen it checks every two seconds instead, so the nudge can clear the moment you reset. The frame itself is never written anywhere. These are the thirteen numbers.

timestamp
When the reading was taken
verdict
Upright or Drifting for this reading
reason
A short code for why, such as head forward or no person
stable_verdict
The verdict after smoothing over recent readings
forward_ratio
How far your head sits ahead of your shoulders, as a ratio
head_height_ratio
How high your ears sit above your shoulders, as a ratio
shoulder_width
Distance between your shoulders, as a fraction of the frame
head_tilt_deg
Tilt of the line between your ears, in degrees
torso_lean_deg
Lean of your torso from vertical, in degrees
ear_visibility
How confident the detector was that it saw both ears
shoulder_visibility
Same, for both shoulders
threshold
Your calibrated line between upright and drifting
margin
How far this reading sat from that line

No image. No list of body points. Nothing you could draw a person from. The macOS camera light is on only while a reading is taken.

What stays on your Mac, and where

Your posture history lives in a database file on your Mac. Optional Pro account and purchase records are separate.

~/Library/Application Support/BetterPosture/betterposture.db
readings
The rows described above, one per check
calibrations
Your upright and slouch baselines as the same kind of numbers, plus the threshold derived from them
user_settings
Check interval, snooze length, sensitivity, chosen camera, and a random install id
insights
The short notes the assistant has written for you, with the day they belong to

Optional assistant keys and account credentials use macOS secure storage. If secure storage is unavailable, new credentials cannot be saved. To inspect the database:

sqlite3 ~/Library/Application\ Support/BetterPosture/betterposture.db ".tables"

What the app asks macOS for

These are the app's entitlements, the permissions it declares to macOS at signing time. Hardened Runtime is enabled; App Sandbox is not. These declarations are not a guarantee that the app can only access files you select.

com.apple.security.device.camera
Lets the app open the webcam. macOS still asks you first, and on a MacBook the green light shows whenever it is on.
com.apple.security.network.client
Lets the app make outgoing connections. Used only for the connections listed below. Block them and detection keeps working.
com.apple.security.files.user-selected.read-write
Declared in the signing configuration, but this build does not enable App Sandbox. This entitlement does not restrict the app to files you choose. Your posture data is stored in the app’s Library folder.
com.apple.security.cs.allow-jit
Required by the app framework to run JavaScript quickly. Standard for every Electron and Chromium based app.
com.apple.security.cs.allow-unsigned-executable-memory
Same reason. The framework needs it to run.
com.apple.security.cs.allow-dyld-environment-variables
Same reason. The framework needs it to run.

The text macOS shows you when the app first asks for the camera:

“BetterPosture analyzes posture from your webcam on this Mac. Frames are processed on-device and never stored or transmitted.”

To read the entitlements straight off the installed app:

codesign -d --entitlements - /Applications/BetterPosture.app

What can leave your Mac

Each connection has a switch or a host you can block. None carries an individual posture reading.

1. Update check

github.com and its release file host
When
Thirty seconds after launch, then every six hours. Only in the installed app, never in development builds.
What
The version you have, so it can see whether a newer one exists. The new version downloads in the background and asks before restarting.
To stop it
Block github.com for BetterPosture. You will need to download updates by hand.

2. Install ping and launch check-in

us.i.posthog.com
When
One ping after you finish onboarding, then a check-in each time the app opens. Default on; the first onboarding screen shows the setting before anything is sent.
What
App version, operating system, chip type, and a random id generated on your Mac. No posture data, no readings, no calibration, no name, no email.
To stop it
Settings, then turn off anonymous install and launch counts. Off means nothing is sent, not even the check-in. Blocking us.i.posthog.com works too.

3. AI-written assistant notes

api.anthropic.com
When
Only with Pro, your opt-in in Settings and your own Anthropic API key. Provider usage is billed separately. Off by default.
What
Daily totals, nothing finer: upright percentage, minutes watched, nudges, days watched, this week against last, your longest upright run, and your personal records. Never a frame, never a body measurement, never an individual reading.
To stop it
Leave it off, or turn it off. Every note has a local wording that works without it.

4. Optional Pro account and access

betterposture.app/api/account
When
When you sign in to activate or refresh Pro. Free detection, profiles, history and built-in notes do not need an account.
What
Your email, sign-in code and session credentials go to BetterPosture. Our server uses Supabase for authentication and access records, and Resend for sign-in email. No frames, body measurements, calibration or reading history.
To stop it
Use the free app without signing in. Blocking account connections prevents activation and access refresh; saved Pro access lasts up to seven days after the last successful online check. Sign-in sessions last up to 30 days.

What is never sent, by any of them

  • Camera frames, stills, or video
  • Body points or any per-reading number
  • Your calibration

This website, separately, counts page views with PostHog. That is a website thing and has nothing to do with the app on your Mac.

Check it with Little Snitch

Little Snitch, LuLu, or any outbound firewall will do. The test takes an hour of ordinary work.

  1. 1
    Add a rule

    In Little Snitch, create a rule for BetterPosture that denies every outgoing connection. In LuLu, choose Block the first time it asks.

  2. 2
    Calibrate and work

    Open the app, calibrate, and carry on with your day. Slouch on purpose once or twice.

  3. 3
    Watch what still works

    Nudges keep appearing. The Progress window keeps filling. Calibration still saves. Nothing about detection changes, because none of it ever used the network.

  4. 4
    Watch what stops

    The update check fails quietly. If you had turned on assistant notes, they fall back to the local wording. Pro sign-in and access refresh need a connection. Saved Pro access expires at most seven days after the last successful online check.

  5. 5
    Read the monitor

    Open Little Snitch's Network Monitor and look at BetterPosture's history. Compare its connections with the services listed above. If you see an unexplained connection, we want to know.

The detector, proven offline

The detection files ship inside the app bundle. Earlier builds fetched them from a content delivery network at launch; this one does not. A script in the app's own build loads the detector, records every request it makes, and fails if any of them leaves the Mac. This is its real output from the build dated 2026-10-12. The same check passes against the packed app archive.

npx electron scripts/check-offline.mjs
ok   analyzer main.ts has no http(s) URLs
     7 requests observed
        file://…/out/renderer/analyzer/index.html
        file://…/out/renderer/assets/analyzer-7rfG2OUO.js
        file://…/out/renderer/assets/tokens-CldbicnH.css
        file://…/out/renderer/assets/geist-latin-wght-normal-BgDaEnEv.woff2
        file://…/out/renderer/mediapipe/pose_landmarker_lite.task
        file://…/out/renderer/mediapipe/wasm/vision_wasm_internal.js
        file://…/out/renderer/mediapipe/wasm/vision_wasm_internal.wasm
ok   pose model initialised
ok   zero outbound http(s) requests
ok   model and wasm loaded over file:
OFFLINE CHECK PASSED

Seven requests, all to files inside the app. The detector and its runtime load from disk. Nothing is fetched.

Found something this page gets wrong?

An unexpected host, a number that does not match, a permission you did not expect. Write to hello@betterposture.app and it gets fixed here first, then in the app. The rest of the privacy story is on the home page.