The Problem

Owners of a WHOOP 4.0 band have no officially supported way to read raw biometric data locally. Existing solutions either lock the data in WHOOP’s cloud or require reverse‑engineering that drifts over time. This repo supplies a self‑hosted, local‑first stack that lets a user collect, decode, store, and query their own data without relying on third‑party services.

What This Does

  • iOS app (ios/): SwiftUI + CoreBluetooth client that streams BLE packets, decodes them with the shared schema (protocol/whoop_protocol.json), and writes to a local SQLite store (Packages/WhoopStore).
  • Server (server/): FastAPI service (server/ingest/app/main.py) that receives decoded payloads from the phone, validates them (server/ingest/app/analysis/validation/), and persists them in TimescaleDB. The Python package whoop-protocol (server/packages/whoop-protocol/) contains the reference decoder used by both sides.
  • Dashboard (dashboard/): Tiny static web UI (dashboard/static/index.html + app.js) that can query the server’s REST endpoints for quick inspection.
  • Reverse‑engineering tooling (re/): Scripts that capture BLE traffic, generate the JSON schema, and support the write‑up in FINDINGS.md.

Key shared artefacts are the protocol JSON (protocol/whoop_protocol.json) and the parity‑tested decoder implementations in Swift (Packages/WhoopProtocol/) and Python (whoop_protocol module).

How It Is Wired

  1. Start‑up – Running docker compose -f server/docker-compose.yml up --build builds the ingest container (server/ingest/Dockerfile) and launches FastAPI. The entry point is server/ingest/app/main.py, which creates a FastAPI instance and includes routers from server/ingest/app/analysis/.
  2. Incoming data – The iOS app calls the server’s /ingest endpoint (code in server/ingest/app/whoop_api/client.py). Payloads are passed to server/ingest/app/analysis/validation/stats.py for schema validation, then to server/ingest/app/analysis/exercise / daily / sleep modules for domain‑specific processing.
  3. Core utilities – The most‑imported internal module is server/ingest/app/analysis/_utils (imported by 5 other modules, no outward imports), acting as a low‑risk utility hub. Modules such as server/ingest/app/analysis/exercise have high instability (Ce = 4, Ca = 0), meaning they depend on many others but are not widely reused—changing them has limited blast radius.
  4. Persistence – After analysis, data is written via the whoop-protocol Python package (whoop_protocol/models.py) into TimescaleDB tables. The server never writes files directly; all I/O is via the DB driver.
  5. Dashboard – The static UI fetches JSON from the FastAPI /metrics endpoint; no additional server‑side code beyond the existing API is required.
  6. Reverse‑engineered schemaprotocol/whoop_protocol.json is loaded by both the Swift decoder (WhoopProtocol/Interpreter.swift) and the Python package (whoop_protocol/framing.py), guaranteeing parity across platforms.

No circular import cycles were detected, simplifying future refactors.

How To Use It

# Clone the repo
git clone https://github.com/moses-y/wearable.git
cd wearable

# Server side
cp server/.env.example server/.env          # edit .env to set DATA_ROOT, DB credentials
docker compose -f server/docker-compose.yml up --build -d

# iOS app
open ios/OpenWhoop.xcodeproj                # Xcode opens the SwiftPM project
# Copy the example config and fill in your server URL + API key
cp ios/OpenWhoop/Config/Secrets.example.xcconfig ios/OpenWhoop/Config/Secrets.xcconfig

# Dashboard (optional)
python -m http.server 8000 --directory dashboard/static
# Browse http://localhost:8000

The repository supplies no Makefile or npm scripts; Docker and the provided .env file are the only build/run artefacts. The re/ scripts require manual creation of re/device_local.py from the example file before they can be executed.

Real‑World Use

A health‑tech startup could deploy the server component on a private cloud, ship the iOS client to users, and query the stored metrics via the REST API for personalized analytics. The same JSON schema can be reused in a separate Python analytics pipeline without rewriting the decoder.

Code Health & Issues

Measured findings (static analysis)

  • HIGH – Deep nesting (48 occurrences) – e.g., server/ingest/app/whoop_api/client.py reaches 8‑level indentation.
  • HIGH – Duplicated code (repeated 6‑line blocks across Swift and Python decoders).
  • MEDIUM – Broad exception handling (2 places) – catches Exception without logging.
  • MEDIUM – File opened without context manager (9 places) – potential handle leaks.

Code‑health audit

  • HIGH – No LICENSE – add an MIT/Apache‑2.0 file.
  • HIGH – No lockfile for whoop-protocol – generate and commit poetry.lock or requirements.txt with hashes.
  • HIGH – No CI pipeline – add a GitHub Actions workflow that builds the Docker image and runs the Python test suite.
  • HIGH – No build gate for docker-compose.yml – incorporate image build validation in CI.
  • MEDIUM – Enable Dependabot – auto‑update Python, SwiftPM, and Docker dependencies.
  • MEDIUM – Pin Docker base image by digest – replace python:3.11-slim with a digest.
  • MEDIUM – Add non‑root USER to container – improve runtime isolation.
  • LOW – Add repository convention files (.editorconfig, formatter configs).

SDLC observations

  • Tests exist (115 files) but are not wired into any CI.
  • Secrets example file (ios/OpenWhoop/Config/Secrets.example.xcconfig) is present; real secrets are git‑ignored, but the example should be clearly marked as non‑sensitive.
  • No .github/ directory; CI/CD must be introduced.

The Bottom Line

The repo provides a functional, cross‑language WHOOP 4.0 data pipeline with a clear separation of concerns (iOS client, Python server, shared schema). It is usable as‑is for personal research, but production adoption requires addressing high‑severity code‑health issues (license, lockfile, CI) and refactoring deep nesting/duplication to lower maintenance cost. Engineers comfortable with FastAPI, SwiftUI, and Docker will find the architecture straightforward to extend.