Skip to main content

Overview

Open Wearables collects anonymous usage telemetry from self-hosted instances. Telemetry is enabled by default and can be disabled at any time with a single environment variable. When it is enabled, the API logs a notice at startup so it is never running silently.

Why we collect it

Open Wearables is self-hosted, so without telemetry we have no way of knowing how it’s used. We use the data for two things:
  • What to build next. Knowing which providers, data types and features people actually use shows us where to put our time and what we can safely deprecate.
  • How big deployments are. Whether an instance has a handful of test users or thousands of real ones changes what we optimise for, like sync frequency, how incoming data is processed, or storage.

What is collected

One small JSON document (“ping”), at most once per 24 hours, plus one on startup (debounced to at most one per 12 hours, so restarts and redeploys don’t send extra data). Every field is either an aggregate count, reported as an order-of-magnitude bucket rather than an exact number, or a configuration flag.

Instance and runtime

Usage counts

Counts are sent as order-of-magnitude buckets, never exact numbers: 0, 1-10, 11-100, 101-1k, 1k-10k, 10k-100k, 100k-1M or 1M+. An exact count sent every day would let anyone holding the pings of a small instance read day-to-day activity off the differences, or match a total against a public profile. A bucket only moves when the count crosses an order of magnitude.

Provider configuration

One entry per row in the provider_settings table.

Feature flags

API usage

How often each API endpoint is called, for the last complete UTC day. This is the only part of the ping that describes activity rather than state. Each level is deliberately coarse:
  • Route is the route template from the API’s own route table, such as GET /api/v1/users/{user_id}/timeseries. The raw request path, query string and any ID in it are never recorded. The one path parameter kept is {provider}, and only when it is a known provider name. Requests that match no route are counted together as unmatched.
  • Caller type is which kind of credential the request presented: developer_jwt (the dashboard), api_key (a backend integration), sdk_token (the mobile SDK) or none. It tells us whether an endpoint is used through the dashboard or through the API. No key, token or identity is recorded.
  • Status class is 2xx, 3xx, 4xx or 5xx.
  • Count is an order-of-magnitude bucket, never the exact number: 1-10, 11-100, 101-1k, 1k-10k, 10k-100k, 100k-1M or 1M+.
Endpoints for menstrual cycle data are excluded entirely. User agents are not recorded, and there is no per-hour breakdown. Counting happens in memory and is flushed to Redis every 30 seconds, off the request path. The cost is a few microseconds per request.
An example payload:

What is never collected

  • No user data of any kind: no names, emails, IDs, or health data
  • No credentials, tokens, API keys, or secrets
  • No hostnames, IP addresses, or URLs of your deployment
  • No per-user or per-request information, only instance-wide aggregate counts
  • No raw request paths, query strings or user agents
  • No exact numbers of users, connections, records or requests, only order-of-magnitude buckets
  • No commit hash, which could point to a custom build or a fork
Pings are sent to https://telemetry.openwearables.io, a collector operated by the Open Wearables team. The sending code lives in backend/app/services/telemetry_service.py and the request counters in backend/app/services/endpoint_usage.py. Both are easy to audit.

What we do with the data

The data is aggregated across instances and used only to decide what to build, what to optimise and what to deprecate. It is not sold, shared with third parties, or used to identify or contact anyone.

Why opt-out and not opt-in

Opt-in telemetry is almost never enabled, so the resulting data represents a tiny, unrepresentative slice of users and is useless for the decisions above. We chose opt-out with a single, well-documented switch, a startup log notice so it is never silent, and a payload small enough to review in a minute.

Opting out

Set one environment variable in your backend .env and restart:
Open Wearables also honors the DO_NOT_TRACK convention shared by many developer tools:
Either one fully disables telemetry: no scheduled task, no startup ping, no HTTP calls, no request counting. Set it on every backend container (API, Celery worker and Celery beat): the API counts requests, the worker builds and sends the ping, and beat schedules it.

Verifying it is disabled

When telemetry is enabled, the API logs this line at startup:
If the line is absent, telemetry is off. You can also confirm that the Celery beat schedule has no send-telemetry-ping entry.

Blocking at the network level

If you want a guarantee that does not depend on configuration, block outbound traffic to telemetry.openwearables.io in your firewall. Telemetry is best-effort, so a blocked request is logged at WARNING and never affects the instance.

Failure behavior

Telemetry can never affect your instance: delivery uses a 5-second timeout, failures are logged at WARNING and swallowed, and the ping is simply retried on the next hourly due-check.