Skip to main content
getbased loads native ES modules during local development. Production deployments run a Rolldown build that creates a hashed startup bundle and keeps feature-level lazy chunks separate. The resulting runtime is still mostly static HTML, CSS, and JavaScript, plus optional API routes for hosted features.

Quick start

1

Clone the repository

2

Start a local server

Open http://localhost:8000/app in your browser. The dashboard runs immediately.
Use node dev-server.js for wearable OAuth and other features that depend on /api/proxy. A static server such as python3 -m http.server 8000 is sufficient only for browser-local features; it cannot load deployment OAuth settings or exchange confidential OAuth tokens, so Google Health remains unavailable there.
The quick local server loads source modules directly, so this path needs no npm install or compile step. Production deployments install dependencies and run node scripts/build-production.mjs; Vercel does this through the repository’s existing build command. The app is installable as a PWA and works fully offline for browser-local features — labs, charts, custom markers, already-imported wearable data, and local encryption are available without a network connection after the first load. OAuth connection, token refresh, and live wearable sync still require the provider network and the deployment’s server routes.

Optional CLI Companion

The hosted app and self-hosted app both support CLI agents. The Companion must run on the browser user’s computer, not merely on a remote server hosting getbased. A static website cannot launch it. For development with the generated Companion download and normal lifecycle setup, use an application checkout with dependencies:
This npm command builds the Companion bundle before starting the development server. Keep the same browser origin (including localhost versus 127.0.0.1 and port) to use existing local data. If port 8000 is occupied, stop your previous server normally; starting on another origin does not migrate its browser storage. For an independently running source Companion, use npm run companion. For login startup, use npm run companion:install. The public download runs with Node.js 20+ without a source checkout. A self-hosted HTTPS page outside the official allowlist needs its exact origin in the Companion’s GETBASED_AGENT_HOST_ALLOWED_ORIGINS; never expose the loopback listener publicly to solve connection failures. See Companion operation and security.

Wearable OAuth apps

The OAuth client_id values baked into the app belong to the maintainer’s registered apps, which are authorized for *.getbased.health redirect URIs only. If you self-host under a different domain, the provider returns invalid_client when the bundled credentials are paired with your redirect URI. To run wearable OAuth on your own host, register your own OAuth app with each provider you want to support and supply the credentials via environment variables.

Register your OAuth apps

When registering, add both your local dev redirect URI (http://localhost:8000/app) and your production hostname (e.g. https://your-host.example/app) to the allowed redirect URI list in each provider’s portal — the URIs must match character-for-character. For a custom hostname that is not already listed, add the same URI to that provider’s oauth.redirectUris entry in js/wearable-adapters.js in your fork.

Environment variables

Create a .env.local file in the repo root (copy .env.local.example as a starting point):
Apple Health is file-import only — no OAuth, no credentials, no portal registration. It works identically on every self-hosted install.
At startup, the browser fetches active public client_id values and provider availability flags from /api/proxy. Google Health, WHOOP, and Ultrahuman are fail-closed: each is available only when its *_ENABLED value is exactly true and its client ID and secret are all present. Only the public client ID and availability flag reach the browser; each client secret stays on the server and is injected during token exchange and refresh. WHOOP is a supported self-host-only integration; Ultrahuman remains experimental. They are hidden on hosted deployments that have not enabled them. On localhost, disabled setup rows remain visible so an operator can find the required configuration; Connect appears only after the server reports a complete, enabled setup.

WHOOP developer program and operator responsibility

WHOOP is not supported on the official hosted getbased app. Each independent deployment uses its own WHOOP membership and developer application. Start with WHOOP’s Developer Platform overview, register the application in the WHOOP Developer Dashboard, configure the exact redirect URI, and complete any app approval WHOOP requires before offering it to users. The application requests read:profile, read:cycles, read:recovery, read:sleep, read:workout, and offline. getbased uses WHOOP’s current v2 profile, cycle, recovery, and sleep endpoints. WHOOP’s v1 data API is no longer supported; keep deployments aligned with the v1-to-v2 migration guide and current API reference. WHOOP uses a confidential authorization-code flow. Keep WHOOP_CLIENT_SECRET server-side. Supply WHOOP_CLIENT_ID through environment configuration rather than committing it to the open-source fork; the active client ID must reach the browser for the authorization redirect, while the secret is injected only by /api/proxy for exchange and refresh.
The self-host operator is the WHOOP application owner. Before enabling the integration, review the current WHOOP API Terms of Use and ensure the deployment’s consent, accessible support contact, privacy policy, security, retention/cache behavior, user access, deletion, and provider-side revocation process satisfy WHOOP and applicable law. Do not reuse or distribute another operator’s developer credentials.
getbased presents a WHOOP-specific disclosure before redirecting to authorization. It explains the read scopes, self-host data path, device encryption, optional encrypted Sync/AI context, and deletion/revocation path. Do not remove or bypass that disclosure. A custom deployment must also publish its own accurate privacy policy and support contact. At runtime, WHOOP token exchange, refresh, and data requests pass through the deployment’s same-origin /api/proxy; they never fall back to getbased-operated infrastructure. Tokens, raw daily rows, and WHOOP-specific local profile data are device-key encrypted. Tokens, raw rows, and the connection record stay device-local and out of backups and Sync; only allowlisted compact derived surfaces can participate in optional encrypted profile Sync or enabled AI/Agent context.
Google Health uses restricted, read-only OAuth scopes. The owner of each self-hosted Google Cloud project is responsible for the OAuth consent screen, verification, testing-user limits, and any Cloud App Security Assessment that Google requires. Do not reuse the official getbased OAuth project. See Google Health setup for current requirements.
The integration requests only these 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.sleep.readonly
A copy-ready explanation for Google’s How will the scopes be used? field is kept under 1,000 characters in google-health-scope-justification.md.
Google currently caps a new unverified OAuth client at 100 users. Testing mode requires each tester to be added explicitly, and its refresh tokens expire after seven days. Supporting more than 100 users requires a third-party security review. Check Google’s setup page before launch because these requirements can change.

Sun atmosphere source

An independent deployment does not inherit getbased’s operated CAMS upstream or bearer. You have two supported choices:
  • Select Open-Meteo in the Light lens for a browser-direct source with no server configuration.
  • Run a compatible getbased-uvdata service, set UVDATA_UPSTREAM to its explicit base URL, and set UVDATA_BEARER only when that service requires one.
The self-hosted /api/proxy fails closed for CAMS when UVDATA_UPSTREAM is empty. It does not silently use the getbased-operated service. The app then falls back to browser-direct Open-Meteo in Auto mode. For a multi-instance Vercel deployment, also configure a suitable proxy limiter with PROXY_RATE_LIMIT_BLOB_TOKEN, or explicitly accept the weaker per-instance fallback with PROXY_ALLOW_INSTANCE_RATE_LIMIT=1. PROXY_RATE_LIMIT_MAX, PROXY_RATE_LIMIT_WINDOW_MS, and PROXY_UPSTREAM_TIMEOUT_MS tune the shared proxy bounds. PROXY_POSTAL_QUEUE_MAX optionally caps queued Nominatim postal lookups per instance from 1 to 64; the default is 8.

Deploying to Vercel

A vercel.json is already present in the repo root — no extra configuration is needed.
1

Import the repository on Vercel

In the Vercel dashboard, click Add New → Project and import your fork of get-based. Vercel detects vercel.json automatically.
2

Set environment variables

Go to Project → Settings → Environment Variables and add your wearable credentials. Mark each *_CLIENT_SECRET value as Sensitive. Google Health, WHOOP, and Ultrahuman each require their enable flag, client ID, and secret. For WHOOP, finish the developer-program, redirect, disclosure, and terms review above before enabling production access.
3

Deploy

Vercel builds and deploys on every push to your default branch. The app runs at your Vercel project URL immediately after the first deploy.
For a basic self-hosted install, no additional build configuration is needed — the app runs out of the box with the example supplement catalog included in the repo.

Self-hosted profile sharing

The local development server provides only a process-memory /api/share store, so its links disappear when the process restarts. For durable temporary links, run server/profile-share-server.js behind the same HTTPS origin at /api/share, using the container assets under deploy/profile-share/. The standalone service requires an absolute PROFILE_SHARE_SQLITE_PATH and a unique PROFILE_SHARE_RATE_LIMIT_KEY of at least 32 characters. Optional bounds include PROFILE_SHARE_BIND, PROFILE_SHARE_PORT, PROFILE_SHARE_DATABASE_MAX_BYTES, and PROFILE_SHARE_REQUEST_TIMEOUT_MS. The browser encrypts before upload, but the service still observes ordinary connection metadata and stores keyed abuse-control, expiry, and deletion metadata beside the opaque envelope. Decide and document your own backup policy. The official operated service intentionally has no retained database backup so deleted or expired temporary links are not resurrected; that also means infrastructure loss can invalidate live links.

Running your own sync relay

Cross-device sync uses a relay — a blind store-and-forward server that holds encrypted blobs. The relay never sees your plaintext data. By default, getbased connects to wss://sync.getbased.health. You can run your own. These instructions target getbased-relay v2.0.0. The v2 line uses Evolu 8 and adds replay-protected compaction plus an owner-proof verifier for the separate Agent Access context gateway.
1

Clone a released relay

Clone the repository and check out the immutable release tag:
Review docker-compose.yml before starting it. In particular, set QUOTA_PER_OWNER_MB to the encrypted-history allowance you want each Sync identity to have.
2

Create the private verifier configuration

The supplied Compose stack starts both the Evolu relay and the Agent Access context gateway. They share a private verifier socket and require a random internal credential.
Paste the generated value into .env as CONTEXT_VERIFIER_TOKEN. Keep it private: it authenticates only the gateway-to-relay verifier call and is not an Evolu mnemonic, owner key, or Agent Access read token.
3

Start and verify the stack

Both relay and context-gateway should become healthy. The default Compose file binds the WebSocket relay to host loopback port 4000, the context gateway to loopback port 4001, and owner self-service to loopback port 4003. Relay administration stays container-only on port 4002.Persistent state lives in separate relay-data and context-data volumes. The verifier uses a private socket volume; it does not expose owner keys or a public TCP listener.
4

Add TLS with Caddy

Install Caddy and create /etc/caddy/Caddyfile:
The /api/* route serves Agent Access and must overwrite X-Getbased-Client-IP from the actual connection peer so callers cannot choose their own rate-limit identity. /self/* serves owner-signed storage and compaction requests. The remaining path carries the Evolu WebSocket connection; Caddy handles the upgrade automatically.Restart Caddy:
Caddy provisions a TLS certificate automatically.
5

Point DNS

Add an A or AAAA record for sync.yourdomain.com pointing to your server.
6

Point getbased at your relay

In getbased, open Settings → Data → Cross-device sync → Advanced, enter wss://sync.yourdomain.com, and click Save. Use Sync status & storage to verify both relay connectivity and the configured quota.
The v2.0.0 Docker image pins Node.js 24.20.0 and installs @evolu/common 8.9.0 with @evolu/nodejs 3.2.0. A native, non-Docker deployment requires Node.js 24.20.0 or newer. Browser and relay adapters use independent package version streams; exact package numbers do not need to match, but they must share a supported Evolu protocol generation.
A small Linux VPS with 1 CPU, 1 GB RAM, Docker, persistent storage, and a domain with TLS is sufficient for a personal relay. Monitor disk usage and keep regular backups of both persistent data volumes.
Before a major relay upgrade, record the current release and image, stop the stack, and back up the relay-data volume, context-data volume, and deployment configuration. Verify the archives before starting the new image. A safe rollback can require restoring the matching data backup; changing only the container image is not guaranteed to reverse an on-disk migration.
Do not expose the admin port, verifier socket, or database volumes publicly. Route /api/* and /self/* through the same TLS hostname with the boundaries shown above.

Local AI with a self-hosted instance

getbased supports Ollama, LM Studio, Unsloth Studio, Jan, llama.cpp, and other OpenAI-compatible servers through Settings → AI → Local AI. Enter the server’s base URL and getbased sends AI calls directly from the browser to that endpoint. Privacy depends on the address and model you choose. A loopback endpoint with a downloaded model keeps inference on the browser device. A LAN server moves requests to another machine, while a remote endpoint or Ollama :cloud model sends them off your network. getbased shows the detected execution location in the provider panel.
The page and model-server schemes must satisfy browser mixed-content rules. An HTTPS getbased page can use an HTTP loopback endpoint, but the browser blocks plain HTTP LAN and remote endpoints. Put the model server behind HTTPS or an encrypted local tunnel. A getbased instance served over plain HTTP can call an HTTP LAN server when CORS allows the page origin, but the traffic itself is unencrypted unless a VPN or tunnel protects it.
Allow the exact getbased origin in your model server’s CORS configuration. For example, a self-hosted page at http://192.168.1.20:8000 should use that origin instead of * in OLLAMA_ORIGINS. LM Studio users should enable CORS and API authentication. See Connect an AI provider for backend detection, context and VRAM behavior, and model-testing links.

Tor access

A .onion address is available for getbased. When you access the app via Tor Browser, the sync relay automatically switches to the .onion address (ws:// — no TLS needed over Tor’s end-to-end encryption). No additional configuration is required.