The short version: your data stays on your computer. We never see it. There is no Enduragent account, no Enduragent server that holds your training data, and no analytics anywhere in the product. The rest of this page explains exactly what that means and names every case where something does leave your machine.
This website
These are static pages. They set no cookies, run no analytics, load no tracking pixels, and have no sign-up, login, or contact form. Nothing you do here is recorded by us.
Like any website, these pages are served by a hosting provider, and that provider may keep standard server access logs (IP address, timestamp, requested URL, user agent) for operational and security purposes, under its own retention policy. We do not use those logs to build a profile of you, and we do not combine them with anything else.
Links from this site to GitHub, the npm registry, Railway, intervals.icu, or any model provider take you to services we do not run. Those services have their own privacy policies.
The app: where your data lives
Enduragent runs entirely on your machine — the desktop app on macOS, or the cycling-coach command-line tool on macOS and Linux (including in a container you host yourself). Everything it knows about you is written to your own disk, in a directory you control.
On the desktop, the default home is ~/.enduragent (override it with the ENDURAGENT_HOME environment variable). It is created with owner-only permissions and contains:
- config/config.yaml — your provider and model choice, intervals.icu athlete id, timezone, and data directory.
- config/auth-profiles.json — OAuth access and refresh tokens, if you sign in with a ChatGPT subscription. Owner-read-only, rotated automatically.
- config/daemon.token and a port file — the bearer token and port used by the app's own UI to talk to its local coaching service over loopback only.
- config/device-id — a local identifier used to mark which machine authored a record inside your own database. It is never transmitted anywhere.
- store/store.db — the derived athlete database (SQLite). App-private; not meant to be placed in a file-sync folder.
- archive/ — compressed, immutable copies of the raw inputs the app read: intervals.icu API responses and any ride files you imported.
- transcripts/ — append-only conversation transcripts, one file per chat.
- sessions/ — live session state. Resetting a conversation renames the file to a timestamped archive instead of deleting it, so old chats are kept until you remove them.
- memory/MEMORY.md and dated daily notes — your long-term memory: goals, injury history, preferences. Plain Markdown you can open, edit, or delete in any text editor.
- plans/current-plan.json — your active training plan.
The command-line tool uses the same layout in its own data directory: ~/.cycling-coach for existing installs, ~/.enduragent/cycling for fresh ones, or wherever CYCLING_COACH_HOME points. In a container or on Railway, the same layout sits on a persistent volume you own, mounted at /data.
The app: where your API keys live
Your API keys are credentials, and they are stored as such.
On the desktop, each provider key and your intervals.icu key are encrypted at rest, one file per slot, under ~/Library/Application Support/Enduragent/credentials-v1/. Encryption uses the operating system's own secure storage, with the key held in the macOS Keychain. The directory and files are owner-only, and that is verified on every read. If secure storage is unavailable, or the operating system only offers a plaintext fallback, the app refuses to store the key at all rather than writing it in the clear. It also refuses to read a credential through a symlink or from a file with the wrong permissions.
On the command line you choose the storage yourself during setup: plain text in config.yaml, the macOS login Keychain, the 1Password CLI, or a reference to any external resolver you already run (HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager, Bitwarden, an age-encrypted file, and so on). Environment variables take precedence over external references, which take precedence over plain YAML. Container deployments normally inject secrets as environment variables.
No telemetry
There is no analytics, product telemetry, crash reporting, or session recording anywhere in Enduragent. No Sentry, PostHog, Mixpanel, Amplitude, Segment, Datadog, OpenTelemetry, and no Electron crash reporter. We do not know how many features you use, which commands you run, or whether the app crashed.
The desktop app makes no automatic request to any server we operate. The one project-operated endpoint that exists at all is the version check made by the self-hosted Telegram bot, described below.
What leaves your machine
Local-first does not mean nothing ever goes out. An AI coach has to talk to a model, and a training coach has to read your training data. Here is the complete list, who receives what, and when.
1. Your model provider, at your direction
When you send a message or run a coaching command, the app sends a request directly to the model provider you configured, using your own API key, billed to your own account. That request contains the coaching instructions, your stored memory (goals, injury notes, preferences), the conversation so far, and the results of any data lookups the coach made, which includes your real training data: Fitness, Fatigue, Form, Load and Intensity values, ride history, FTP, weight, power and heart-rate zones, and wellness values such as HRV, resting heart rate and sleep.
This is the largest outbound flow in the product, and it is worth being blunt about it: the provider you pick sees your conversations and the training numbers included in them. Their handling of that data is governed by their own privacy policy and terms, not ours. Pick accordingly.
Depending on your choice, the destination is the official API of Anthropic (Claude), OpenAI (GPT), Google (Gemini), DeepSeek, Qwen via Alibaba Cloud DashScope, MiniMax, Kimi by Moonshot AI, Z.AI (GLM), or OpenRouter. No request is sent to a provider you have not configured, and nothing is routed through us.
2. ChatGPT subscription sign-in, only if you choose it
Instead of an API key you can sign in with an existing ChatGPT Plus, Pro, Business, Edu or Enterprise subscription. If you do, the app opens a standard OAuth 2.0 authorization flow in your browser against OpenAI's identity service, with the callback served on your own machine at a loopback address. After that, coaching requests, the same payload described above, go to the ChatGPT backend on your subscription's quota. The resulting tokens are stored locally, as described earlier. This path is governed by OpenAI's terms and privacy policy.
3. intervals.icu, with your own API key
If you connect intervals.icu, the app makes authenticated requests to intervals.icu using the API key you pasted in, to read your athlete settings, FTP, zones and weight, your activities, wellness records, activity streams, power and heart-rate curves, and bulk activity exports. This happens when you sync and when a coaching turn needs fresh numbers.
The app writes to intervals.icu only when you ask the coach to schedule training: it creates workout events on your own calendar. intervals.icu may then forward those workouts to the devices and platforms you have linked there: Garmin, Wahoo, Hammerhead, COROS, Suunto, Zwift. That onward sync is intervals.icu's, not ours; we never contact those platforms. intervals.icu's own privacy policy governs everything it holds.
4. Telegram, only in bot mode
The command-line tool can run as a Telegram bot that you host yourself. In that mode every message in and out passes through Telegram's servers, as any Telegram message does, and is subject to Telegram's privacy policy. Access is limited to a sender allowlist you control, so people you have not added are dropped before the coach ever sees them. This mode does not exist in the desktop app or in plain terminal chat.
5. Update checks and release notes
The desktop app checks for updates at launch and every 24 hours, over HTTPS, against a plain release feed configured when the app was built. That request carries no athlete data, no credentials, and no installation identifier. Updates are never installed behind your back: the app only installs when you choose to restart, and it will not install a prerelease or a downgrade.
When you explicitly ask the desktop app what's new, it makes one request to the public npm registry for the latest published version and one to the GitHub API for that version's release notes. Nothing about you is attached.
The self-hosted Telegram bot does one thing the desktop app does not: on startup and every 24 hours it makes a background version check to ping.enduragent.icu. The request carries the binary name, the running version, the install channel (npm or container), and an install identifier, a random UUID generated once on your machine and stored in your data directory. Its only purposes are to tell you a newer version exists and to give us a rough count of how many self-hosted instances are running. It carries no athlete data, no message content, no credentials, and nothing derived from your identity. Set CYCLING_COACH_NO_UPDATE_CHECK=1 to switch it off entirely; the same lookup then falls back to the public npm registry only when you run an update command yourself. Running /whatsnew or /update is an explicit command, so it still performs its lookup (npm registry, GitHub API, and for /update on npm installs, the npm registry again to install) even with background checks off.
6. Ride files you import
Ride files you import (.fit, .tcx, .gpx) are parsed entirely on your machine. They are never uploaded to any server we operate. If you later ask the coach about a ride, the relevant numbers are included in the prompt sent to your chosen model provider, exactly as described in section 1.
7. Links you click
Links in the app (GitHub releases, the Railway template page, a feedback survey link in update notices) open in your normal browser as ordinary navigations. The app attaches nothing to them.
That is the complete list. Nothing else leaves your machine.
What the Enduragent authors receive: nothing
There is no server-side component operated by this project that receives athlete data. No shared instance, no hosted backend, no database of users, no account system. If we wanted to look at your training data or read your conversations, there would be nowhere for us to look. The single project endpoint that exists, the bot's version check, receives a version string, a channel name, and a random number, and you can turn it off.
Where the software is self-hosted (a container, Railway, a VPS), you are the operator of that instance. The hosting provider you choose is your relationship, under their terms and privacy policy.
Children
Enduragent is not designed for or directed at children. It is a training tool for adults, installed and configured by the person using it.
Deleting your data
Because everything is local, deletion is local too, and it is complete.
- Delete individual pieces — memory files, transcripts, session archives and plans are ordinary files. Open the data directory and remove what you want gone.
- Delete everything — remove the app's home directory (~/.enduragent, or your ENDURAGENT_HOME / CYCLING_COACH_HOME path, or ~/.cycling-coach for older command-line installs). That takes the config, database, archive, transcripts, sessions, memory and plans with it.
- Remove stored API keys — on the desktop, delete the credential files under ~/Library/Application Support/Enduragent/credentials-v1/; they are unreadable without the operating system key that stays in your Keychain. On the command line, remove them from wherever you chose to keep them: the config file, the login Keychain, your password manager, or your secret store.
- Uninstall — on macOS, turn off "Open at Login" first, quit the app, then delete it. For the command-line tool, uninstall the npm package or remove the container and its volume.
Data you already sent elsewhere is outside our reach: conversations held by your model provider, activities and workouts on intervals.icu, messages in Telegram. Use each service's own controls to delete those.
Changes to this policy
If the product's data behavior changes, this page changes with it, and the effective date at the top moves. Material changes will also be noted in the release notes for the version that introduces them. The history of this page is public in the project repository, so you can see exactly what changed and when.
Contact
Questions, corrections, or something in this policy that does not match what the code actually does: open an issue on the project's GitHub repository. The source is public, so if you would rather verify than take our word for it, read it.
See also the Terms of Use.