OpenRemedy — privacy notice¶
Last updated: 2026-05-08
This document is the canonical reference for what data leaves an
OpenRemedy installation, why it leaves, and how to stop it from leaving.
If anything below disagrees with the source code in
openremedy-backend
or openremedy-telemetry,
the source code is the truth and this document is a bug.
TL;DR¶
- Every OpenRemedy install posts a small set of anonymous counters
to
telemetry.openremedy.ioonce every 24 hours. - The payload contains no personal data: no tenant names, no user emails, no server hostnames, no IPs, no log lines, no API keys.
- The same install also pulls the latest released version of each OpenRemedy component from the same endpoint, so the dashboard can show an "update available" indicator.
- Either or both can be turned off with one environment variable.
- The receiver does not log source IPs at any layer.
What is sent (one POST every 24 hours)¶
The full payload, fields documented one by one:
{
"instance_uuid": "1f4b56ca-4ede-4494-aa6e-808870ca673a",
"version": "v0.1.2",
"git_sha": "95ef56ad5ea0a1d7d6a67d21348d645cbfd1e7ea",
"tenant_count": 2,
"user_count": 5,
"server_count": 12,
"agent_count": 4,
"incidents_30d": 52,
"executions_30d": 22,
"incidents_resolved_30d": 50,
"llm_provider": "deepseek",
"deployment_age_days": 30,
"telemetry_seq": 47
}
| Field | Meaning |
|---|---|
instance_uuid |
Random UUID generated by your install on first proactive boot, persisted in the local telemetry_state table. Has no mapping to any person, organisation, or email. Used only to deduplicate consecutive pings from the same install. |
version |
The OpenRemedy backend version running, e.g. v0.1.2. Or dev for unbuilt local checkouts. |
git_sha |
The 40-character commit SHA the running image was built from, or null for dev builds. |
tenant_count |
Integer. Number of rows in your local tenants table. |
user_count |
Integer. Number of rows in your local users table. |
server_count |
Integer. Number of rows in your local servers table. |
agent_count |
Integer. Number of rows in your local agents table. |
incidents_30d |
Integer. Number of incident rows whose created_at falls in the last 30 days. |
executions_30d |
Integer. Number of execution rows whose created_at falls in the last 30 days. |
incidents_resolved_30d |
Integer. Number of incident rows in resolved status with a non-null resolved_at. |
llm_provider |
One of deepseek, openai, anthropic, kimi, or null. Derived from which API key environment variable is populated. |
deployment_age_days |
Integer. Days since the local telemetry_state.created_at row was first written. |
telemetry_seq |
Integer counter incremented per successful send. Lets us detect dropped pings. |
What is not sent¶
Guaranteed at code level. The payload schema is enforced by the
Pydantic model in
openremedy-telemetry/src/openremedy_telemetry/schemas.py
— any field outside that schema is rejected by the receiver.
- ❌ Tenant names, slugs, domains, custom labels.
- ❌ User emails, names, IDs, roles.
- ❌ Server hostnames, IP addresses, fingerprints, SSH usernames.
- ❌ Recipe slugs, incident contents, log lines, audit log entries, agent transcripts, sidechain transcripts, evidence bodies.
- ❌ LLM API keys, encryption keys, JWT secrets, SSH private keys, any other secret.
- ❌ Cookies, browser fingerprints, User-Agent strings (this is a server-to-server call, not a browser-originated request).
- ❌ Source IP addresses. The receiver runs uvicorn with
--no-access-log, the FastAPI middleware stripsServerandX-Powered-Byresponse headers, and the Caddy reverse proxy in front of it haslog { output discard }for this vhost. No IP ever lands on disk.
Frequency¶
The proactive container's TelemetryLoop runs once on startup and then
every 24 hours. The HTTP timeout is 10 seconds. Failures retry up to
three times with exponential backoff (1s, 4s, 16s). After three
consecutive failures, the loop sleeps until the next 24-hour cycle.
If the receiver is rate-limiting your install (HTTP 429), the loop backs off until the next cycle without retrying.
How to turn it off¶
Two environment variables, both default to false:
| Variable | Effect |
|---|---|
OREMEDY_TELEMETRY_DISABLED=true |
Suppresses the metrics push (POST /v1/ping). The version-check pull (GET /v1/latest) still runs so the dashboard can still show "update available". |
OREMEDY_OFFLINE_MODE=true |
Overrides everything. Suppresses both the push and the pull. No outbound traffic to telemetry.openremedy.io at all. |
Set them in your docker/.env file (openremedy-deployment) and
recreate the proactive container:
After the next loop tick (or container restart), docker logs
openremedy-proactive-1 | grep TelemetryLoop should show the new
state in the startup log line.
Right to erasure (GDPR Article 17)¶
If you have a copy of your instance_uuid (you can read it locally
from the telemetry_state table), you can request deletion of all
records the receiver holds for that UUID:
# Read your instance_uuid out of your local database
docker compose -p openremedy --env-file .env exec -T postgres \
psql -U openremedy openremedy -tAc "SELECT instance_uuid FROM telemetry_state"
# Delete it from the receiver
curl -X DELETE https://telemetry.openremedy.io/v1/instances/<paste-uuid-here>
The receiver returns HTTP 204 on success. The row is hard-deleted from
the instances table and the rate-limit memory entry is cleared. The
endpoint is public and unauthenticated by design — possession of the
UUID is the authorisation, since the UUID was generated by your install
and shared only by your install.
Retention¶
The receiver keeps a single most-recent row per instance_uuid.
A daily cleanup task removes rows whose last_seen_at is older than
90 days, so an install that is decommissioned will fall out of the
receiver naturally without needing a deletion request.
Lawful basis¶
The lawful basis under GDPR Article 6(1)(f) is legitimate interest: maintaining a B2B infrastructure product responsibly, where knowing how many self-hosters exist and on which versions is necessary to schedule support, security backports, and end-of-life notices.
The fields collected are pseudonymous (random UUID), aggregated counts, and version strings — they cannot be used to identify a natural person. Even taken together with everything an attacker could reasonably obtain, they cannot be re-identified to a real-world operator without information the receiver does not have (and does not log).
If your jurisdiction requires opt-in rather than opt-out collection
even for non-personal aggregate data, set
OREMEDY_TELEMETRY_DISABLED=true and we will not be offended.
Source code¶
Everything described here is open to inspection:
- Receiver: https://github.com/OpenRemedy/openremedy-telemetry
- Sender (
services/telemetry.py): https://github.com/OpenRemedy/openremedy-backend/blob/main/src/openremedy/services/telemetry.py - Frontend footer (display only): https://github.com/OpenRemedy/openremedy-frontend/blob/main/src/components/layout/VersionFooter.tsx
Contact¶
Questions, complaints, requests for further detail:
albertof@barrahome.org with subject prefix [OpenRemedy Privacy].
This notice will be versioned alongside the project. Material changes to what is collected will be reflected here at least one minor release before they take effect, and the file's git history is the audit trail.