-
Notifications
You must be signed in to change notification settings - Fork 646
Privacy and Security
NOOP is offline by design. Your biometric data never leaves your device, and there is exactly one opt-in network exception: the optional AI Coach. This page explains the architecture, what gets collected, what doesn't, the threat model, and how both platforms enforce these guarantees.
Not affiliated with WHOOP. NOOP is an independent, unofficial, local-first companion app. It interoperates with a WHOOP strap that you own, reading your own biometric data from your own device. All computed outputs are approximations and not clinically validated. See the Disclaimer for details.
NOOP has no server, no account, no cloud, no telemetry, and no phone-home. The biometric pipeline runs entirely on your machine:
WHOOP strap ──(Bluetooth LE)──> Your device ──(local SQLite)──> Stored on disk
Your files ──(you select)──> Your device ──(local SQLite)──> Stored on disk
Everything you own—recovery scores, strain, sleep stages, HRV, heart rate samples, imported history—lives in a single SQLite database in your app's sandbox container. Nothing goes to any server unless you explicitly enable the AI Coach.
The AI Coach is the single feature that can use the network, and only on your terms:
Off by default. You need to turn it on by entering your own API key (OpenAI or Anthropic). No key, no network calls.
What it sends. When you ask it a question, NOOP builds a compact text summary of your recent metrics (recovery, strain, sleep, HRV, resting heart rate over ~14 days, plus 30-day averages and recent workouts) and sends it with your question directly to the provider's API—api.openai.com or api.anthropic.com, under your account.
What it does not send. No raw biometric streams, no Bluetooth frames, no device identifiers or account numbers—only the summary text and your question.
Available on both platforms. The AI Coach works the same way on macOS and Android. On macOS it is currently blocked by the App Sandbox (see The macOS sandbox below), so the feature doesn't network on the sandboxed build as shipped. On Android there is no equivalent sandbox, so the Coach works with your key.
Your key, your relationship. NOOP runs no proxy server and keeps no copy of your requests. The call goes straight from your device to your chosen provider, under your own API account.
If you never enable the AI Coach, NOOP makes zero network connections.
All data is stored in a single GRDB/SQLite database at <Application Support>/OpenWhoop/whoop.sqlite. On macOS, because NOOP is sandboxed, this path lives inside the app's sandbox container—other apps cannot read it through normal filesystem access.
The database holds:
- Decoded biometric streams (durable): heart rate samples, R-R intervals, SpO₂, skin temperature, respiration, accelerometer/gravity, battery, and events.
- Derived metrics: sleep sessions, daily metrics, detected workouts, journal entries, imported Apple Health data, and generic metric series.
- Device records: strap ID, MAC address, name, first/last-seen timestamps.
-
A transient raw outbox (
rawBatch): compressed raw Bluetooth frames, prunable—old batches are deleted to prevent unbounded growth.
The database uses WAL (Write-Ahead Log) mode optimized for bulk import, so you'll also see sidecar files (whoop.sqlite-wal and whoop.sqlite-shm) alongside the main database.
The SQLite file itself is not encrypted by NOOP. Instead, confidentiality relies on platform protections:
- FileVault (full-disk encryption, on by default on modern Macs) protects the database when your machine is powered off or locked.
- The sandbox container prevents other user-space apps from reading the database file directly.
What this does not protect: a user session that is unlocked and logged in, or a Time Machine backup made while FileVault was unlocked. Once the disk is mounted and your session is active, the data is plaintext SQLite.
Optional: SQLCipher. GRDB supports SQLCipher (an encrypted SQLite build) as a drop-in. A future version could wire NOOP's database to SQLCipher with a Keychain-derived key, providing encryption independent of FileVault. This is not enabled in the current build but is a contained change.
Raw Bluetooth frames in the rawBatch table are treated as transient, not the system of record. The decoded biometric streams (HR, R-R, SpO₂, etc.) are durable; raw frames are a compressed, prunable buffer. Old batches are automatically deleted with a configurable retention policy.
When diagnosing strap connection issues, the BLE client keeps an in-memory ring buffer of the last 2000 log lines on Android (and similar on macOS). This log contains:
What it includes:
- Scan results (strap advertised name, signal strength)
- Bond/handshake state machine events
- Command names and their outbound payload hex
- Offload progress (trim cursors, chunk acknowledgments)
What it does NOT include:
- Account credentials (none exist)
- Decoded biometric values (heart rate numbers, R-R intervals, etc. are never written to the log)
- Hello-tokens or secret handshake payloads
- The only mild identifier is the strap's advertised name (e.g.,
WHOOP 5AG…), included only if you choose to share the log
On Android, debug logging to the system log (logcat) is off by default. The log is mirrored only to the in-app buffer. Developers can enable Settings → Strap → "Debug logging" to see live output over adb logcat, but this does not change what is shared when you use the "Share strap log" button—that always exports only the in-app buffer, without any hidden system-log data.
On Android, wrist alerts (buzz the strap when selected apps notify you) require a NotificationListenerService so the OS can tell NOOP when a notification arrives. This is a powerful permission, so NOOP is precise about what it does with it:
Off by default, double opt-in. The service does nothing until you grant Notification Access in system settings and turn on Wrist alerts in NOOP, then enable specific apps (each off by default).
Only reads the posting app name. On a notification, NOOP checks which app posted it (and filters out system noise like foreground-service notifications). It then checks your settings (master toggle, that app's opt-in, quiet hours, only-when-worn) and—if all pass—sends a haptic command to the strap.
Never reads content. The notification's title, text, sender, and extras are never read, stored, logged, or transmitted.
On macOS, the App Sandbox provides a structural guarantee: the OS refuses outbound network connections that the app doesn't have permission to make. NOOP ships with a deliberately minimal entitlement set:
<key>com.apple.security.app-sandbox</key> <true/>
<key>com.apple.security.device.bluetooth</key> <true/>
<key>com.apple.security.files.user-selected.read-write</key> <true/>Three keys, and that's the full entitlement file:
-
app-sandbox— the process runs inside the macOS App Sandbox. -
device.bluetooth— permits Bluetooth Low Energy access to the strap. The usage string in the app states: "NOOP connects directly to your WHOOP strap over Bluetooth to read heart rate, R-R intervals, battery, and sensor data locally on your Mac. Nothing leaves your device." -
files.user-selected.read-write— allows the app to read import files you explicitly pick via the open panel, and to write the database in its own sandbox container.
Notably absent:
-
com.apple.security.network.client— no outbound network entitlement. The macOS sandbox will refuse any socket the app tries to open, including the AI Coach's. So on the sandboxed macOS build as shipped, the AI Coach cannot reach the network—the whole app, Coach included, is fully offline. (Android has no equivalent sandbox restriction, so the AI Coach works there when you enable it with your key.) -
com.apple.security.network.server— no inbound listener. - No
files.downloads,files.documents, or broad filesystem entitlements — the app cannot browse the disk; it sees only what you hand it.
This structural guarantee means "offline by design" is enforced by the OS, not merely by convention.
NOOP is not notarized by Apple. Notarization requires a paid Apple Developer ID tied to a real, traceable identity—which contradicts an anonymous, free project. The app is sandboxed and ad-hoc code-signed; the full source is here for inspection. See the Installation page for how to open it on first launch.
NOOP parses two classes of untrusted input: bytes arriving over Bluetooth, and files you choose for import. Both are treated as hostile.
Out of scope: NOOP cannot defend your data against an attacker who already has your unlocked, logged-in session. It also makes no claim of cryptographic authentication of the strap itself—that is provided by the OS Bluetooth stack and the device firmware.
A device advertising as a strap (or a real strap with corrupted firmware) could send malformed, truncated, oversized, or adversarial frames. WhoopProtocol is the reverse-engineering decode layer and the first line of defense.
CRC-gated parsing. Every frame is checked against its checksums before driving any application state. The protocol implements three checksums from the wire formats:
-
crc8(poly 0x07) over the length header -
crc32(zlib/reflected) over the inner payload -
crc16Modbusfor WHOOP 5.0 header
Frames that fail either checksum are rejected immediately and never update UI or state.
Bounds-checked decoding. Field reads use safe methods that return nil instead of reading past the end of a buffer. Schema-driven parsing skips any field whose offset is out of range; every slice is clamped to the actual buffer length. Even valid-checksum packets with short or lying length fields yield partial parses, never out-of-bounds reads.
Sane-value gating. Even a CRC-valid frame is range-checked before updating state. The handler discards implausible heart rates (outside 30–220 bpm) and only overwrites R-R intervals when a frame actually carries them.
Bounded reassembly. The frame reassembler resynchronizes on the start-of-frame byte, discards leading garbage, and only emits a frame once enough bytes are present—it does not unboundedly buffer arbitrary data.
Both WHOOP CSV and Apple Health importers assume the file is hostile.
Apple Health (AppleHealthImporter). Apple Health exports routinely exceed 1 GB; a malicious one could be far worse.
-
Streaming SAX parse, never DOM. The importer uses
XMLParserover anInputStreamopened directly on the file. It does not load the whole document into memory first. Element handling runs inside a per-elementautoreleasepoolso temporaries drain instead of accumulating. -
Zip-bomb cap. When the input is a
.zip, extraction aborts the moment decompressed total crosses an 8 GB ceiling. - Robust error handling. Parse failures are surfaced as typed errors; the parser distinguishes a genuinely malformed document from benign EOF conditions.
-
Temp cleanup. Temporary files are deleted via
defer.
WHOOP CSV export (WhoopExportImporter). The WHOOP data export is small but receives the same hardening.
- Per-entry size ceiling. Each CSV is capped at 256 MB. ZIP entries whose declared size exceeds the cap are rejected, and extraction enforces a running byte budget.
- CRC32 verification. Each extracted entry is verified for integrity; corrupt or truncated entries are skipped entirely.
-
Filename allow-list. Only four known CSV names are ever read:
physiological_cycles.csv,sleeps.csv,workouts.csv,journal_entries.csv. Everything else in the archive is ignored. - Tolerant parsing. Columns are matched by normalized header name (not position), every column is optional, and malformed input degrades gracefully to fewer rows, never a crash.
- No accounts, login, or credentials
- No telemetry, analytics, or crash reporting
- No cloud, sync, or remote backup
- No advertising identifiers or tracking
- No WHOOP account or API credentials (NOOP talks only to your strap over local Bluetooth)
| Surface | Risk | Mitigation |
|---|---|---|
| Process egress | Data exfiltration | Only the opt-in AI Coach networks (your key, to your chosen provider, text summary only), off by default; available on both macOS and Android; nothing else makes a network call |
| Filesystem access | Broad disk browsing | Only files.user-selected.read-write entitlement; data stays in sandbox |
| BLE frames | Malformed/adversarial packets | CRC8 + CRC32 (+ CRC16 for v5) gating; reject on failure |
| BLE frames | Out-of-bounds reads | Bounds-checked readers; slice clamping; min-length guards |
| BLE frames | Garbage/partial fragments | SOF-resync reassembler bounded by declared length |
| App state | Implausible-but-valid values | Range gates (HR 30–220) at state edge |
| Health import | XML/zip bomb | Streaming SAX over InputStream; 8 GB decompression ceiling |
| CSV import | Zip bomb / oversized entries | 256 MB per-entry cap; CRC32 verify |
| CSV import | Arbitrary archive members | Filename allow-list; tolerant optional-column parsing |
| Data at rest | Disk theft | FileVault + sandbox; SQLCipher available as option |
| Diagnostics | Logcat leakage | In-app buffer only; logcat mirroring opt-in (default off) |
The same privacy and security principles apply on both macOS and Android:
-
Core biometric pipeline. Both platforms use the same
WhoopProtocol(CRC-gated BLE parsing),WhoopStore(SQLite), andStrandAnalytics(recovery/strain/sleep math). The threat model and data persistence model are identical. - AI Coach. Both platforms support the optional AI Coach with the same "bring your own key" model. On Android it works with your API key; on macOS it is blocked by the sandbox as currently shipped.
-
File import. Both platforms use the same hardened importers (
StrandImport). - Minimal permissions. Android grants only the permissions needed for Bluetooth and import; macOS sandboxes the app with minimal entitlements.
NOOP is a hobbyist, non-commercial project provided as-is, with no warranty. If you discover a security or privacy issue, please open a GitHub issue describing the problem and reproduction steps. Sensitive reports can be coordinated privately via the contact on the project's GitHub profile.
- How NOOP Works — architecture overview
- Features — what NOOP can do
- Installation — how to get and run it
- Disclaimer — trademark and good-faith notice
- Protocol — BLE reverse-engineering details
NOOP is an independent, unofficial, non-commercial interoperability project — not affiliated with, endorsed by, or sponsored by WHOOP, Inc. "WHOOP" is a trademark of WHOOP, Inc., used nominatively. Works only with a device you own; not a medical device; every metric is an approximation, not medical advice. · Privacy and Security · Donations · Releases
Get started
Tutorials
- Tracking a Workout
- Recovery, Strain & Readiness
- Automations
- Breathe & Intervals
- Importing History
- AI Coach
- Widget & Notifications
- Reading Your Sleep
- Explore & Compare
Reference
Project