Skip to content
covey

Anyone who runs covey behind their own firewall gets asked a question that no assurance answers: which connections does this software open by itself, and what is in the packets. An answer to it sits where the software assembles those packets.

Hacker News has carried the question in the same shape for years. On 25 October 2019, in the thread about GitLab's telemetry (story 21343761), rswail wrote: “Telemetry to any external service from inside our VPN is a definite no-go.” In the thread about the GitHub CLI's telemetry (story 47862331), yamajun93 set down on 23 April 2026 what is missing: “Would be good to see a clearer breakdown of whats collected and how it is anonymized.” Another comment there states the harder half, that a user has no way to check such a disclosure for completeness.

Once a day, thirteen fields of counts go out

covey sends for the first time five minutes after start, then every 24 hours. telemetry.url holds the destination, and it ships as https://covey.work/rueckkanal. Counts go to the path /telemetrie under a 20 second timeout.

What arrives there can be listed in full. The list runs version and commit, the number of organisations, humans and agents, and the number of agents that are hired. Three counters describe the tasks of the last day, a fourth counts the tasks that sit blocked, one counts the registered runners, and the fields runtimes and targets carry names with a count each. Thirteen fields, every one of them a count from the database or a version string, assembled in the function Zahlen in internal/telemetry/telemetry.go.

One more value travels with them: a UUID the installation generates for itself on first use, kept in the setting telemetry.id. It carries no name and no address. An administrator cannot set it. The key is on the read-only list, so that two installations are never counted as one.

A second value exists only where somebody has stored one. telemetry.key is the optional key of an installation the project knows by name, sealed like the SMTP password, and where it is set it sits in the same body as the counts.

What never goes out is held down by a test

TestTelemetrieSchicktNurZahlen creates an agent with the slug geheimer-slug and a task titled “Ein Titel, der niemanden etwas angeht”. The test then intercepts the send, searches the body for exactly those strings and for the address admin@test.local, and fails the moment one of them shows up.

Two switches are covered by the same test. After telemetry.mode = off nothing may arrive any more, and COVEY_TELEMETRY=off from the environment has to win over a setting that is on.

That objection from April has an answer in three parts. AGPL-3.0 is the licence, the assembly is a single function, and the source sets the standard that a reader should be able to check the list in a minute.

Three switches, and each one closes the channel completely

First, telemetry.mode = off under Platform and Settings. Second, an empty telemetry.url: the settings validation allows the empty value on purpose and carries it as the second way. Third, COVEY_TELEMETRY=off in the environment, and that one works before the first start.

Only one place is asked: TelemetryOn returns destination and permission together, and the environment wins over the setting there.

Source and documentation both name the trade-off. Telemetry is on by default, and the reason given is that an installation which has to be asked first reports nothing, which turns every answer about the running version into a guess. In the thread of 22 April 2026 the counter-position is just as short. “This should be opt-in”, goosejuice writes.

An agent's bug report takes one of two routes, and only one hangs on the switch

covey Doctor owns the second way out. When that agent finds a platform fault no configuration fixes, it files the finding as an issue, along one of two routes.

Where a token of the organisation's own is stored as a secret, github_token or gitlab_token among them, the control plane files under that account. The token stays in the control plane, and the agent gets back the result of the call. This route never asks TelemetryOn, so an organisation with a stored token keeps filing issues after telemetry.mode = off.

Where no such token is stored and the destination is the project's own repository, the report goes through the channel to the project, and that route asks the same function the counts ask. A person reads it there before it becomes an issue, unless the project knows this installation. Known ones are released at once, and nothing reaches a public tracker unread.

Anyone who does not want the layer at all sets the target system under “This platform's source” to the entry “not at all”, which is stored as a hyphen.

Two properties hold on both routes. The destination is master data rather than a parameter of the call, so no run decides where a report about this platform lands. And the same report is recorded in the organisation's inbox, so that the operator reads what went out.

So the decision stays with the operator, and it costs a look at one function, one setting or one environment variable.

Back to all posts