![]()
OpenFit is a private, desktop-first Electron dashboard for Google Fitbit Air and other Fitbit devices. Its adaptive interface prioritizes a small set of useful insights and only displays views, metrics, and navigation when Google Health returns real data.

The renderer uses React, shadcn/Radix, Tailwind CSS v4, assistant-ui, Inter Variable, JetBrains Mono, and Nucleo Essential Outline icons.
Project status: the application is complete and buildable. Demo mode works without configuration. Accessing personal data requires an OAuth client in your own Google Cloud project.
Fitbit Air does not provide a public Bluetooth synchronization interface for third-party applications. The supported data path is:
Fitbit Air -> Bluetooth -> Fitbit/Google Health mobile app
|
v cloud sync
Google Health API -> OpenFit
OpenFit uses Google Health API v4 as its default provider. The legacy Fitbit Web API remains available only as a transitional adapter and is scheduled for deprecation in September 2026.
The desktop application can replace the browsing and analysis experience, but it cannot perform initial device pairing, firmware updates, or phone-to-device synchronization.
Requirements:
npm install
npm run dev
Useful commands:
npm run build # Type-check and bundle the renderer
npm test # Run normalizer and adapter tests
npm run capture:ui # Run desktop/mobile visual QA in Electron Chromium
npm run dist # Package the app for macOS, Windows, or Linux
Packages generated locally in release/ are unsigned unless an Apple Developer ID certificate is available in the Keychain. For public distribution, follow the release checklist.
You need:
npm run dev or as an installed desktop application.API configuration, OAuth consent, and OAuth credentials must all belong to the same Google Cloud project.
OpenFit.OpenFit and enter a support email.While the application remains in OAuth testing mode, only explicitly listed test users can authorize it. Google normally expires refresh tokens for external applications in testing after seven days; reconnect the account when needed or complete Google's production requirements.
Open Google Auth Platform scopes, choose Add or remove scopes, and add these read-only Google Health scopes:
https://www.googleapis.com/auth/googlehealth.activity_and_fitness.readonly
https://www.googleapis.com/auth/googlehealth.health_metrics_and_measurements.readonly
https://www.googleapis.com/auth/googlehealth.ecg.readonly
https://www.googleapis.com/auth/googlehealth.irn.readonly
https://www.googleapis.com/auth/googlehealth.location.readonly
https://www.googleapis.com/auth/googlehealth.nutrition.readonly
https://www.googleapis.com/auth/googlehealth.profile.readonly
https://www.googleapis.com/auth/googlehealth.settings.readonly
https://www.googleapis.com/auth/googlehealth.sleep.readonly
Do not add write scopes. OpenFit also requests the standard openid and profile scopes to display the account name and avatar.
OpenFit Desktop.text
http://127.0.0.1:42813/oauth/callback
Do not commit or share these credentials. During authorization, OpenFit starts a temporary loopback server on port 42813, validates OAuth state and PKCE, receives the authorization code, and then closes the server.
http://127.0.0.1:42813/oauth/callback.The connection is working when OpenFit shows Google Health instead of Demo mode, displays a last synchronization time, and begins showing real device and health metrics. Metric availability depends on the device, region, granted consent, and recent Fitbit mobile synchronization.
The Client Secret, OAuth tokens, and health cache stay in Electron's main process and are encrypted with safeStorage using Keychain on macOS, Credential Manager on Windows, or an available secret store on Linux. They are not exposed to the renderer or written to the repository.
A Client Secret distributed in a desktop binary is not a durable global secret. The current setup is appropriate for personal use and development. A public release should move the OAuth code exchange to a small backend and complete Google's verification and security-review requirements.
redirect_uri_mismatch
http://127.0.0.1:42813/oauth/callback exactly. Do not use localhost, omit the path, or add a trailing slash.Access blocked, access_denied, or unauthorized user
invalid_client
HTTP 403 or API not enabled
Port 42813 is already in use
Some metrics are missing
For a longer checklist, see Google Health setup.
electron/
main.cjs Electron shell, OAuth loopback, IPC, encrypted storage
preload.cjs Minimal typed IPC bridge
codex-service.cjs Read-only Codex app-server JSONL client
google-health-service.cjs Google Health API v4 provider
fitbit-legacy-service.cjs Legacy Fitbit Web API provider with PKCE
src/
components/ Views, charts, and assistant-ui chat
data/ Demo data and provider-independent normalization
lib/ Formatting and pure utilities
App.tsx UI and connection-state orchestration
types.ts Shared renderer/preload contracts
scripts/
capture-ui.cjs Electron visual smoke test
docs/
ARCHITECTURE.md System decisions and boundaries
DATA_COVERAGE.md Data coverage and limitations
GOOGLE_HEALTH_SETUP.md Extended OAuth setup guide
RELEASE.md Signing, notarization, and release process
See Architecture for security boundaries and design decisions.
The chat button in the top bar opens a right-side panel built with assistant-ui primitives. Its bridge uses codex app-server, the same local interface used by Codex clients, with a read-only sandbox, approvals disabled, and tool calls denied by default.
When you send a message, OpenFit creates a compact context containing normalized metrics, available dates, and details for the selected day. It does not include OAuth credentials or encrypted files. This context is sent to Codex/OpenAI only after you use the chat. Codex may navigate to an OpenFit view or date, but it cannot modify health data.
No Codex model name is hard-coded in this repository. The app-server selects its configured default model unless a model is supplied programmatically through the service options.
Icons: Nucleo Essential Outline (c) Nucleo, used under the Nucleo license.
The information displayed by OpenFit is not a diagnosis or medical advice.
$ claude mcp add openfit \
-- python -m otcore.mcp_server <graph>