telemetry

package
v2.1.1-rc1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 12, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

Documentation

Overview

Package telemetry reports that swarmcli is being used, and nothing about what it is used on.

What it sends is enumerated in `docs/license.md` under Privacy and is the whole of it: an install identifier, the version and edition, the operating system and architecture, how the binary was installed, and whether it is the TUI or the controller. Deliberately absent, and listed here so the next person adding a field has to argue past it: hostnames, cluster or node names, service, stack and image names, command arguments, error text, and the address the request came from. Those describe a customer's infrastructure, and the licence documentation promises "how many, never which ones".

It is on by default and `SWARMCLI_TELEMETRY=off` switches it off. The update check is a separate thing and survives that switch — see Client.

Index

Constants

View Source
const (
	// Env is the one variable that governs the startup request, and it has
	// three states rather than two — see State.
	//
	// Spelled as a value rather than as SWARMCLI_DISABLE_TELEMETRY so that
	// `SWARMCLI_TELEMETRY=off` reads the way people write it, and so the third
	// state had somewhere to go without a second variable.
	Env = "SWARMCLI_TELEMETRY"

	// DefaultURL is the telemetry endpoint, which also answers the update check.
	DefaultURL = "https://swarmcli.io/api/v1/telemetry"

	// FallbackURL is the original update check, and the whole of what is called
	// when telemetry is off: it carries no install id and nothing about the
	// machine.
	FallbackURL = "https://swarmcli.io/api/v1/version"
)
View Source
const (
	EventStarted   = "swarmcli_started"
	EventHeartbeat = "swarmcli_heartbeat"
)

Events. A closed set matching the server's; an unknown name is refused there.

View Source
const (
	ModeTUI = "tui"
	ModeCLI = "cli"
	ModeCD  = "cd"
)

Modes. Which of the three products is asking.

View Source
const (
	MethodBrew    = "brew"
	MethodScoop   = "scoop"
	MethodDocker  = "docker"
	MethodSource  = "source"
	MethodTarball = "tarball"
	MethodUnknown = "unknown"
)

Install methods. A closed set, matching the server's zod enum: a value outside it is refused there, so inventing one here would silently drop the whole event rather than just the field.

View Source
const (
	NoticeTitle = "Usage reporting is on"

	NoticeBody = `` /* 743-byte string literal not displayed */

)

NoticeTitle and NoticeBody are the first-run disclosure.

**This is the half of opt-out that makes it legitimate.** A default-on reporter with no notice is the thing people are right to object to; the difference between that and this is that the first run says what is being sent, before anything has been sent, and says how to stop it.

Written to be read in three seconds, so it enumerates rather than explains. The enumeration is the point: it is the same list as `docs/license.md` and the same list the server's schema accepts, and if the three ever disagree the documentation is wrong rather than the reader.

It names what is *not* sent as well, because that is the question somebody running this against a production swarm actually has, and answering it in the notice is cheaper than having them find the documentation to be reassured.

Variables

This section is empty.

Functions

func Arch

func Arch() string

func Enabled

func Enabled() bool

Enabled reports whether a usage report will be sent.

func InstallID

func InstallID() string

InstallID returns this installation's identifier, creating and persisting one on first use.

Returns "" when no identity can be stored — no home directory, an unwritable config directory. That is deliberate and the caller treats it as "do not report": a per-process identifier would turn every launch into a new install and silently inflate the only number this exists to produce. Better to count nothing than to count wrongly.

Reads first and writes only when there is nothing to read, so the id is stable across launches, which is the entire point of it.

func InstallMethod

func InstallMethod() string

InstallMethod guesses how this binary got here, and says "unknown" when it cannot tell.

The question it answers is which distribution channel is worth maintaining — the Homebrew tap, the Scoop bucket, the Docker image or the release tarball. Nothing in swarmcli has ever recorded it, so today that is decided on nothing.

**It is a guess from the executable's path, and deliberately conservative.** Everything it cannot place is `unknown` rather than `tarball`, even though a tarball is the likeliest answer for an unmatched path: a default that claims the most common channel would make that channel look better than it is, and the number would then be used to justify keeping it. An honest `unknown` bucket is something a reader can see and discount; a wrong `tarball` is not.

func IsFirstRun

func IsFirstRun() bool

IsFirstRun reports whether no install identity has been stored yet.

Called before InstallID so the notice is shown on the run that creates the identity rather than the one after it. Any error answers false: a machine that cannot read its own config directory should not be told about telemetry it is also unable to store an identity for.

func OS

func OS() string

OS and Arch are the build's, from the runtime rather than from the machine. `runtime.GOOS` is what swarmcli was compiled for, which is the thing worth knowing when deciding which builds to keep publishing.

func ShouldNotice

func ShouldNotice() bool

ShouldNotice reports whether this run should show the disclosure.

True exactly once per installation: on the run that has no stored identity yet and is about to create one. A run with telemetry already switched off never shows it, because there is nothing to disclose — and showing it anyway would be an advert rather than a notice.

Types

type Client

type Client struct {
	// URL and FallbackVersionURL are fields rather than constants so tests can
	// point them at a local server.
	URL                string
	FallbackVersionURL string
	HTTP               *http.Client
}

Client performs the one outbound call swarmcli makes on startup.

func New

func New() *Client

New returns a client against the live endpoints.

func (*Client) CheckIn

func (c *Client) CheckIn(event, version, edition, mode string, shape Shape) (string, error)

CheckIn reports one event and returns the newest released version.

**The update check and the telemetry are one request, and that is the rule this whole change is built around.** swarmcli has always made exactly one call per launch, and adding usage reporting must not begin by doubling that: a second beacon per launch is both twice the traffic from every install and twice the surface to explain to somebody deciding whether to trust it. So the telemetry endpoint answers the version question too, and a launch stays one request whichever branch below is taken.

The two branches are the opt-out. With telemetry on, the full report goes to the telemetry endpoint. With it off, the original version check runs unchanged — the update notice is a feature the user asked for by running swarmcli, not telemetry, and switching off usage reporting must not also stop telling them a new release exists.

type Shape

type Shape struct {
	Nodes         *int
	Managers      *int
	Services      *int
	DockerVersion string
}

Shape is what kind of swarm this is, as counts.

**Counts, never names.** `docs/license.md` publishes that line for the licence requests — "how many, never which ones" — and this holds to it: a node count is a fact about scale, a node name is a map of somebody's estate. Nothing here can be turned into the second.

Pointers, so that absent and zero stay different facts. A swarm that has not been observed yet sends nothing; a swarm observed to be running no services sends `0`, and that is a real and different answer. Only `Services` can honestly be zero — a swarm always has at least the node you are asking — but all three are pointers so the next reader does not have to know which.

type State

type State int

State is what the startup request does. Three states, because "no telemetry" and "no network" are different asks and conflating them served neither.

const (
	// StateFull reports usage and checks for updates, in one request. The
	// default.
	//
	// **On by default**, which is the decision worth being explicit about
	// rather than burying in a boolean. What makes that defensible is the shape
	// of what is sent: an install identifier and a country the server derives
	// and the client never sees, with no address stored and nothing about the
	// cluster. The first-run notice says so before the first report leaves.
	StateFull State = iota

	// StateUpdateOnly sends the version-only request: no install id and nothing
	// about the machine, byte-identical to what shipped before usage reporting
	// existed. The update notice still works, because being told a new release
	// exists is a feature somebody asked for by running swarmcli and is not
	// telemetry.
	StateUpdateOnly

	// StateSilent makes no outbound request at all. The state an air-gapped or
	// policy-restricted cluster wants, and the one no amount of "off" could
	// express while this was two variables.
	StateSilent
)

func Reporting

func Reporting() State

Reporting reads the state from the environment.

Anything unrecognised leaves reporting **on**. That is the deliberate direction for a value whose absence also means on: a typo in a chart cannot silently disable it, which would be a failure nobody notices until the numbers are already wrong.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL