Your strap. Your data. Your machine. Offline, on-device, no cloud.

Now in the all-new Liquid Metal design: one living look across iPhone, Android and Mac.

⬇ Download · ❓ FAQ · 💬 Discord · 👽 Reddit · Features · Protocol · Raw data capture ·

The all-new Liquid Metal look: living liquid scores, a sky that moves with your day, rebuilt on every screen. The same Today on iPhone and Android, and a metric’s own trend. One design across iPhone, Android & Mac.

Pre-built apps you can run right now:

First launch on macOS. NOOP is not notarized by Apple — notarization needs a paid Apple Developer ID tied to a real identity, which doesn't fit an anonymous, free project. The app is sandboxed and ad-hoc code-signed, and the full source is here to inspect. Because it isn't notarized, macOS Gatekeeper blocks it on first open (you may see "damaged" or "unverified developer" — that's the download quarantine flag, not real damage). To open it, do one of these once:

- Terminal (most reliable): drag

NOOP.appto Applications, then runxattr -dr com.apple.quarantine /Applications/NOOP.appand open NOOP normally.- No Terminal: double-click NOOP (it'll be blocked), then open System Settings → Privacy & Security, scroll to the bottom, and click "Open Anyway" next to NOOP. (On macOS 14 and earlier you can also right-click the app → Open.)

Prefer to avoid this entirely? Build from source — see Quickstart.

Installing on Android (Play Protect blocked it?). NOOP isn't on the Play Store — it's an unsigned, source-available APK you sideload, because the project is anonymous and has no paid Play identity to publish or sign under. So Android treats it as an "unknown app" and Google Play Protect may warn or block on install (most stubbornly on stock Pixel / recent Android). Nothing is wrong with the file — it's just missing a Play signature. To get it on:

- Tap "Install anyway." When the warning appears, choose More details → Install anyway.

- No "Install anyway" button? It can vanish after a first install + uninstall. Grant the source directly: Settings → Apps → Special app access → Install unknown apps, pick the browser or file manager you're installing from, turn on "Allow from this source", then open the APK again.

- Still blocked by Play Protect? It's your call to make for an unsigned app you trust: open the Play Store → your profile icon → Play Protect → ⚙ Settings, toggle "Scan apps with Play Protect" off, install NOOP, then switch it back on.

- Reinstalling is safe. Uninstalling and installing again won't hurt anything — NOOP keeps all data on-device with

allowBackup=false, so a reinstall simply starts fresh. There's no cloud copy to lose either way.

Prefer to build it yourself? See docs/BUILD.md.

Everything runs offline by default — nothing about you leaves the device unless you switch on a feature that sends it. NOOP makes only three kinds of network request, all described in docs/PRIVACY_SECURITY.md: the optional AI Coach (off until you add your own API key), a once-a-day check for a newer release, which sends nothing about you and never installs anything, and Android's default-off Experimental one-way push to an endpoint you own. Turn the check off in Settings → About and it makes no request at all. NOOP operates no server, account, or telemetry service.

NOOP is a standalone, fully offline companion app for WHOOP straps (4.0 and 5.0). It pairs directly with the strap over Bluetooth, stores everything on your own device in SQLite, imports your existing WHOOP and Apple Health history, and computes recovery, strain, HRV, and sleep locally, with no WHOOP account and no WHOOP cloud.

There is also experimental support for the Oura Ring (measured on a Gen 3; a Ring 5 is reported working over the same path) — real overnight data from a ring you own, on iOS and Android, with real limits. See Oura ring support.

It is built on prior community interoperability work and exists for one reason: to let someone who owns a WHOOP strap read their own biometric data from their own device, on a machine they control.

Not affiliated with WHOOP. NOOP is an independent, unofficial interoperability project. It is not affiliated with, endorsed by, or connected to WHOOP, Inc. "WHOOP" is used only to identify the hardware NOOP talks to. Use it only with a device you own, and not in breach of any agreement that applies to you. NOOP is not a medical device; every derived metric is an approximation, not clinical data. See

DISCLAIMER.md.

- Why NOOP

- Features

- Platform status

- Architecture

- Quickstart (macOS)

- How your data flows

- Privacy

- Attribution

- Disclaimer

- License

- Docs

You bought the strap. The biometric stream it produces is yours. NOOP is built on that premise:

- Own your data. NOOP reads heart rate, R-R intervals, SpO₂, skin temperature, respiration, accelerometer/gravity, battery, and event data straight off the strap over Bluetooth and writes it to a local SQLite database. Nothing is uploaded anywhere.

- Account-free and local. NOOP never logs into a WHOOP account and never hits a WHOOP server. It does not bypass any login, paywall, or DRM; it simply talks to a device you own and reads data you generated.

- Bring your history. Already have years of data in the official app or in

Apple Health? Import the WHOOP CSV export and/or your Apple Health export.xmlonce, and it's permanently on your machine.

- Transparent math. Recovery, strain, HRV, and sleep are recomputed on-device from documented, citable methods (Task Force 1996 HRV, Karvonen %HRR, Edwards / Banister TRIMP, Tanaka HRmax, and so on). The algorithms are approximations of — not reproductions of — any proprietary model, and every analyzer file documents exactly what it does.

The macOS reference app organizes everything behind a single sidebar

(Strand/App/RootView.swift). Each item below is a real screen in

Strand/Screens/. The same feature set ships on macOS, Android, and iOS via the

shared cross-platform code.

There is also a menu-bar extra (Strand/MenuBar/MenuBarContent.swift) with a

glanceable live HR readout and a compact popover, a first-run onboarding wizard

that sets expectations (independent/experimental, WHOOP 4.0 vs 5/MG, on-device only),

and an in-app "What's new" changelog shown after each update.

Strand/Screens/AutomationsView.swift + Strand/System/MacActions.swift:

- Double-tap → Mac action. Double-tap the strap to lock the Mac, buzz back to

confirm, mark a moment, do nothing, or run any macOS Shortcut by name (via

the shortcuts://URL scheme, so it's sandbox-friendly).

- Wear & presence. Lock the Mac (or run a Shortcut) the moment the strap leaves your wrist; run a Shortcut when it goes back on. (macOS reserves true auto-unlock for Apple Watch — NOOP can lock, not unlock.)

- Haptic coaching. HR-zone coaching and an experimental resting-stress nudge — the strap buzzes so you don't have to watch a screen.

- Inactivity reminder. An optional gentle wrist buzz after you've been sitting still too long — your idle threshold, your active hours, a re-nudge cooldown, respects quiet hours, off by default.

- Smart alarm. Arms the strap's own firmware alarm to buzz at your wake time (still fires if the Mac is asleep or NOOP is closed), with an optional light-sleep wake window when the Mac stays awake and connected.

NOOP's logic lives in cross-platform Swift packages, and the same protocol, storage, analytics, and scoring is ported to Kotlin on Android. Both apps pair with the strap and score recovery, strain and sleep on your own device — no import required.

NOOP is an independent, experimental project — capable, but a work in progress.

NOOP's analysis screens and algorithms can only be as complete as the sensor inputs it can reliably decode. On WHOOP 5.0 / MG, important overnight inputs remain unavailable or incomplete:

In short: seeing the Sleep, Health, Readiness, or Insights screens doesn't mean their deepest analysis is available from a WHOOP 5.0 / MG alone yet — scoring and correlations can't conjure a measurement the strap hasn't given up. Decoding these inputs reliably is what we're working on, and it's the prerequisite for the full 5/MG picture. We'd always rather tell you that straight.

A WHOOP strap holds an encrypted Bluetooth bond with only one device at a time, and yours is normally bonded to the official WHOOP app on your phone. You can't just scan for it in NOOP — if the strap is still bonded to the WHOOP app, NOOP's pairing is refused and the strap log shows "Encryption is insufficient" / "bond refused." (Live heart rate is the exception — it rides the standard Bluetooth heart-rate profile, so it streams without a bond. But pairing — needed for the deeper features — does not.)

To pair properly:

- Close the official WHOOP app on your phone (fully quit it, or turn that phone's Bluetooth off) so it isn't holding the bond.

- Put the strap in pairing mode — on a 5.0/MG, tap the band repeatedly (firm taps on the sensor) until the LEDs flash blue.

- In NOOP: Live → choose "WHOOP 5.0 / MG" → Scan & Connect. Success looks like "CLIENT_HELLO acked — link established" in the strap log (not "bond refused"). It can take a couple of attempts.

Only one device at a time. Because the strap holds a single bond, don't leave it connected to your phone and your Mac (or the WHOOP app) at once — live heart rate will still show on all of them (that rides the bond-free standard profile), but none of them will have the real encrypted bond. If HR streams fine yet buzz, alarm, double-tap and history don't work, that's the tell: the strap isn't truly bonded to this device. Free it from everything else, then pair here.

Bonding to NOOP may take the strap's bond away from the WHOOP app, so the official app might need to re-pair afterwards. This is the hardest part of 5/MG support — if it refuses, you're almost certainly still bonded to the WHOOP app (or another device); free the strap and retry.

The app always tells you what's live now versus still building, both in onboarding and on each screen.

NOOP has experimental, clean-room support for the Oura Ring. It is not a supported strap and it is not on the same footing as a WHOOP 4.0: it lives behind the experimental-device path in the pairing wizard, and parts of it are permanently limited by what the ring will hand over. It reads real data from a ring you own, over Bluetooth, with no Oura account and no Oura cloud — the same rules as everything else here.

Which ring. Every row in the table below was measured on a Gen 3. The framing, the auth

handshake and the event-record dictionary are the same across Gen 3, Ring 4 and Ring 5

(docs/OURA_PROTOCOL.md §7.2); what changes per generation is the MTU, which

characteristics are discovered and the live-HR enable command set — verified on Gen 3, expected the same

on 4/5. A Ring 5 has been reported working over that path (#2075: pairing/auth, live HR and

inter-beat intervals, skin temperature, battery), which is marked per row as Ring 5 ✓. Nothing has

been run on a Ring 4 yet. The two rows marked not possible from this data are about what the ring

banks, not which ring banks it, so they hold across the three until a newer ring is shown transmitting

true beat-to-beat intervals.

Not affiliated with Oura. Independent interoperability work with hardware you own. "Oura" is used only to identify that hardware. NOOP does not use, decompile, or redistribute any Oura app code, and does not bypass any login, paywall, or DRM.

connect() is issued cleanly and no CoreBluetooth callback ever arrives. Factory-resetting the ring

from the official Oura app first, then pairing with NOOP on macOS, works. Documented in

docs/OURA_PROTOCOL.md §3.8.

There are two ways to pair. The standard path provisions NOOP's own key, which requires a factory reset of the ring and carries none of your Oura account's server-side entitlements.

The Advanced path reuses the

auth_keythe genuine Oura app already holds for your own ring, extracted from your own local iPhone backup (Apple's standard, unencrypted backup — no jailbreak, no decryption bypass). Because that key is tied to your account, the ring keeps whatever cloud configuration the real app already unlocked for it, which is what makes otherwise server-gated streams (SpO₂, Exercise HR, real steps) start arriving. Step-by-step recipe:docs/OURA_PROTOCOL.md§3.7.Read the limits honestly before counting on it:

- It requires an active paid Oura membership at the time you enable each feature. These are account-side entitlements; NOOP cannot set them, and a free account does not carry them.

- What happens after a membership lapses is untested. We don't know whether the unlock persists, needs a server re-check, or reverts. Treat it as good for the period actually tested (one billing month), not as permanent.

- It does not unlock the ❌ rows above. HRV and respiratory rate are limited by what the ring's interval stream physically contains, not by an entitlement — no key changes that.

- Treat the key like a password. NOOP stores it locally (Keychain / EncryptedSharedPreferences) and transmits it nowhere.

NOOP computes your scores on your own device, so like any recovery wearable it needs a little data before everything fills in:

- Live heart rate shows the moment the strap connects.

- Strain and sleep appear after you've worn it and synced — the strap's last ~14 days offload automatically over the first few minutes.

- Recovery needs a few nights for the app to learn your personal baseline, then sharpens each night. WHOOP makes you wait for the same reason.

- In a hurry? Import your WHOOP export in Data Sources and your full history fills in about a minute.

The repository is split into platform-pure Swift packages plus a macOS app target.

All packages declare both .iOS(.v16) and .macOS(.v13); framework-specific UI is

guarded with #if canImport(UIKit) / #if canImport(AppKit).

Strand/ macOS SwiftUI reference app (this is what you build)

Packages/

WhoopProtocol/ BLE frame parsing, CRC, command/event/packet decode

OuraProtocol/ clean-room Oura ring BLE protocol (framing, auth, decoders, driver)

PolarProtocol/ Polar PMD decoder (HR / PPI / ECG / ACC streams)

WhoopStore/ GRDB/SQLite persistence (migrations, streams, caches)

StrandAnalytics/ HRV / recovery / strain / sleep / correlation math

StrandImport/ WHOOP CSV + Apple Health importers

StrandDesign/ SwiftUI design system (palette, components, charts)

NoopLocalAccess/ local read-only data-access layer (on-device, no network)

Tools/Backfill/ CLI tool for backfilling decoded data

Fixtures/ sample WHOOP export for tests

Platform-pure (no CoreBluetooth import) so it runs in tests and CLI tools unchanged. It implements the on-wire frame format for both strap generations, so NOOP can speak to a device you own:

public enum DeviceFamily: String, Sendable, CaseIterable {

case whoop4 // CRC8 (poly 0x07) header check; service 61080001-…

case whoop5 // CRC16-Modbus header check, "puffin" packet types; service fd4b0001-…

}Decoding is schema-driven (Resources/whoop_protocol.json) and includes CRC8,

CRC16-Modbus, and zlib CRC-32 implementations, frame framing, value

interpretation, and historical-stream reassembly. The app layer (Strand/BLE/,

Strand/Collect/) wraps these UUID strings in CBUUID and handles bonding,

offload, and live notifications.

Everything is stored on-device in SQLite (using

GRDB.swift). The schema is a versioned

migrator (Database.swift, currently past v28). Examples of decoded-stream

tables created in v1–v3:

CREATE TABLE hrSample (deviceId TEXT, ts INTEGER, bpm INTEGER, PRIMARY KEY(deviceId, ts));

CREATE TABLE rrInterval (deviceId TEXT, ts INTEGER, rrMs INTEGER, PRIMARY KEY(deviceId, ts, rrMs));

CREATE TABLE spo2Sample (deviceId TEXT, ts INTEGER, red INTEGER, ir INTEGER, PRIMARY KEY(deviceId, ts));

CREATE TABLE skinTempSample(deviceId TEXT, ts INTEGER, raw INTEGER, PRIMARY KEY(deviceId, ts));

CREATE TABLE respSample (deviceId TEXT, ts INTEGER, raw INTEGER, PRIMARY KEY(deviceId, ts));Later migrations add server-derived metric caches (sleepSession, dailyMetric),

cursors, a raw frame outbox, and more.

Pure, database-free analyzers. Each is documented and grounded in published methods (and is explicitly an approximation, not a reproduction of any proprietary model):

- WHOOP CSV export (WhoopExportImporter.swift): header-name-driven, tolerant parser forphysiological_cycles.csv,sleeps.csv,workouts.csv, andjournal_entries.csv, from a folder or.zip. The same schema covers WHOOP 4 / 5 / MG.

- Apple Health export (AppleHealthImporter.swift): a streaming SAX parser (XMLParser) forexport.xml(which can exceed 1 GB), with correlation-dedupe, unit normalization (e.g. SpO₂ fraction → %), and sleep-stage mapping.

- Nutrition CSV — a tolerant importer for daily-nutrition exports from Cronometer and MacroFactor, so calories and macros line up alongside your recovery and sleep on a shared timeline.

Palette, typography, motion, and reusable components/charts (RecoveryRing,

StrainGauge, Hypnogram, Sparkline, TrendChart, YearHeatStrip,

StrandCard, StatePill, …) — no external UI dependencies.

Requirements: macOS 13+, Xcode 15+ (Swift 5.9), and a Mac with Bluetooth. To pair live, you need your own WHOOP strap; to just explore, you can import a CSV / Apple Health export instead.

The Xcode project is generated from project.yml with

XcodeGen.

# 1. Clone

git clone <your-fork-url> NOOP

cd NOOP

# 2. (Re)generate the Xcode project from project.yml

brew install xcodegen # if you don't have it

xcodegen generate

# 3. Open and run

open Strand.xcodeproj

# Select the "Strand" scheme → Run (⌘R). The built app is named NOOP.Notes:

- Bundle id com.noopapp.noop, product name NOOP, sandboxed with the Bluetooth and user-selected-files entitlements.

- Swift Package Manager resolves the only third-party dependencies automatically: GRDB.swift (SQLite) and ZIPFoundation (export unzip).

- Run the tests from Xcode (the StrandTeststarget + each package's test target), or per-package withswift testinsidePackages/<Name>/.

To explore without an Xcode project, the packages build on their own:

cd Packages/WhoopProtocol && swift build && swift testWHOOP strap ──BLE──▶ Strand/BLE + Strand/Collect ──▶ WhoopProtocol (decode)

│

WHOOP CSV ─┐ ▼

Apple Health ├─▶ StrandImport (parse) ──────────▶ WhoopStore (local SQLite)

Nutrition CSV┘ │

▼

StrandAnalytics (recovery/strain/

HRV/sleep, on-device)

│

▼

Strand (SwiftUI) + StrandDesign

Every arrow stays on your machine.

Offline by default. NOOP has no server, no telemetry, and no account. Your strap data, imports,

and computed metrics live in a local SQLite database on your device. They leave only through an

export or optional network feature you deliberately configure, including Android's default-off

Experimental one-way push to your own endpoint; see

docs/PRIVACY_SECURITY.md.

The app makes two kinds of network request, neither carrying anything about you: the optional

AI Coach (off until you add your own key), and a once-a-day read of the latest release number so a

sideloaded install can tell you an update exists — on by default, switchable off in Settings → About,

and it never installs anything. Both are detailed in

docs/PRIVACY_SECURITY.md.

NOOP stands on community interoperability and protocol-documentation work. With thanks:

- johnmiddleton12/my-whoop— the WHOOP 4.0 BLE protocol; the- WhoopProtocoland- WhoopStorepackages and the collection logic are adapted from this work.

- b-nnett/goose— the WHOOP 5.0 / MG BLE protocol documentation (the- fd4b0001-…service family, CRC16-Modbus header, and "puffin" packet types) that NOOP's WHOOP 5.0 path is ported from.

- groue/GRDB.swift— SQLite persistence.

- weichsel/ZIPFoundation— export unzipping.

NOOP contains no WHOOP proprietary code, firmware, logos, or assets, and performs

no DRM circumvention. Full detail in ATTRIBUTION.md.

NOOP is an independent, unofficial, non-commercial interoperability project. It is not affiliated with, endorsed by, or connected to WHOOP, Inc. All references to "WHOOP" are nominative — used only to identify the third-party hardware NOOP interoperates with.

NOOP is not a medical device. Heart rate, HRV, recovery, strain, sleep stages, SpO₂, respiratory rate, and skin temperature are approximations computed from published methods. They are not clinically validated and are not medical advice. Do not use them to diagnose, treat, or make health decisions — consult a qualified professional.

Provided as-is, with no warranty, for personal and educational use. You

use it at your own risk. Read the full notice in DISCLAIMER.md.

NOOP is source-available under the PolyForm Noncommercial License 1.0.0: free for personal and other non-commercial use — read it, run it, fork it, and contribute. Commercial use is not granted by this license. (PolyForm Noncommercial is a proper software license with patent terms; it is deliberately not an OSI "open-source" licence, because that would permit the commercial use this project's non-commercial nature rules out.)

The license covers NOOP's own original code and docs. Protocol facts (frame layouts,

command numbers, byte offsets) are uncopyrightable and free to reuse; bundled

dependencies keep their own licenses (GRDB.swift and ZIPFoundation are MIT — see

NOTICE). By opening a pull request you agree your contribution is licensed

under the same terms — see docs/CONTRIBUTING.md.

NOOP is public and built to be hard to erase. Clone it freely — git clone https://github.com/ryanbr/noop.git — and you're welcome to mirror or fork it to Codeberg, GitLab or your own server. More copies make the project more resilient, which is the whole point after being deplatformed.

Two simple asks:

- Keep it non-commercial and keep the LICENSE+Copyright 2026 NoopAppnotice intact (PolyForm Noncommercial — mirror and use freely, just don't sell it or ship it in a paid product).

- Point people back to the canonical home, github.com/ryanbr/noop, so everyone lands on the current code and releases rather than a stale fork.

That's it — copy away.

- CHANGELOG.md— release history and what to expect (also shown in-app under What's new).

- DISCLAIMER.md— trademark, interoperability, and medical/legal notice.

- ATTRIBUTION.md— full credits and licensing notes.

- docs/SCOPE.md— which WHOOP-app features stay out of scope (and why) versus their local, on-device equivalents.

- project.yml— XcodeGen project definition (source of- Strand.xcodeproj).

A live snapshot of the last 30 days — issues, pull requests, pushes, and the people moving NOOP forward. Huge thanks to everyone filing reports, sharing strap logs, and reverse-engineering the protocol alongside us — this project is built on it.

If NOOP's useful to you, a ⭐ genuinely helps it reach more WHOOP users — and it's the single best free way to support the project.