# How the no-mistakes Telemetry System Works: Local vs. Remote Data Collection

> Explore the no-mistakes telemetry system's local and remote data collection. Learn how this CLI buffers events locally and sends them privately to Umami with strict privacy controls.

- Repository: [Kun Chen/no-mistakes](https://github.com/kunchenguid/no-mistakes)
- Tags: internals
- Published: 2026-07-13

---

**The no-mistakes CLI embeds a lightweight telemetry subsystem that buffers events locally in-process and delivers them asynchronously to a remote Umami analytics endpoint, with strict privacy controls that ensure no data leaves the machine when telemetry is disabled.**

The `no-mistakes` repository implements a privacy-first telemetry architecture that separates local decision-making from remote data transmission. Understanding how this telemetry system works helps developers integrate analytics without compromising user privacy or CLI performance. This guide examines the split between local collection buffering and remote delivery mechanisms based on the actual source implementation in [`internal/telemetry/telemetry.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/telemetry/telemetry.go).

## Core Components of the Telemetry System

The telemetry implementation centers on a minimal interface-based design that abstracts the underlying transport mechanism.

### Config and Client Structure

The **`telemetry.Config`** struct holds connection details including the host, website ID, application name, version, and OS/architecture metadata. The **`telemetry.Client`** struct implements the `Sink` interface and manages the HTTP delivery pipeline. When constructed via `NewClient`, it stores the normalized endpoint (`/api/send`) and configures an HTTP client with a **1-second timeout** to prevent blocking operations.

### Sink Interface and No-Op Fallback

The **`Sink`** interface provides the public API surface with three methods: `Track`, `Pageview`, and `Close`. When telemetry is disabled or misconfigured, the system falls back to **`noopSink`**, a no-op implementation that silently discards all events. The **`Default()`** function returns a singleton `Sink` instance, lazily creating a `Client` only when telemetry is enabled and properly configured, otherwise returning `noopSink`.

## How Local Collection Works

Local collection encompasses all in-process decision logic that determines whether events will be generated and how they are buffered before transmission.

### Environment Variable Controls

The system checks the **`NO_MISTAKES_TELEMETRY`** environment variable to determine activation status. In [`internal/telemetry/telemetry.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/telemetry/telemetry.go), the `telemetryDisabled()` function parses this variable:

```go
func telemetryDisabled() bool {
    switch strings.ToLower(strings.TrimSpace(os.Getenv(telemetryEnv))) {
    case "0", "false", "off":
        return true
    default:
        return false
    }
}

```

When this function returns `true`, `Default()` immediately returns `noopSink`, ensuring zero network activity and no data retention.

### Configuration Resolution

Before constructing a real `Client`, the system resolves two critical parameters locally:

1. **Website ID**: The Umami analytics platform requires a website identifier. The resolution order is:
   - Environment variable `NO_MISTAKES_UMAMI_WEBSITE_ID`
   - `.env` file in the repository root (only for development builds where `buildinfo.CurrentVersion()` returns `"dev"`, loaded via `loadDotEnvValues`)
   - Compiled default `buildinfo.TelemetryWebsiteID`

2. **Host**: The telemetry endpoint is resolved similarly:
   - Environment variable `NO_MISTAKES_UMAMI_HOST`
   - `.env` file (development builds only)
   - Compiled default `buildinfo.TelemetryHost`
   - Hard-coded fallback `https://a.kunchenguid.com`

If the website ID resolution yields an empty string, `Default()` falls back to `noopSink`, preventing unauthorized or orphaned data transmission.

## How Remote Delivery Works

Remote delivery occurs only when local validation passes and a configured `Client` instance exists. This phase handles the actual HTTP transmission to the Umami server.

### Asynchronous HTTP POST

Each call to `telemetry.Track()` or `telemetry.Pageview()` invokes `Client.Track`, which spawns a **new goroutine** per event to avoid blocking the caller. The goroutine acquires a slot in a `sync.WaitGroup` via `wg.Add(1)` and constructs an HTTP request using `http.NewRequestWithContext`. The request targets the resolved endpoint with:
- **Method**: POST
- **Content-Type**: `application/json`
- **User-Agent**: `no-mistakes/<version> telemetry`

The goroutine releases its WaitGroup slot when the request completes, regardless of success or failure.

### Payload Structure

The `collectRequest` JSON payload contains:
- `website`: The resolved website ID
- `hostname`: Hard-coded as `"cli"`
- `title`: `"no-mistakes CLI"`
- `url`: Derived from the event URL parameter
- `name`: The event name (e.g., `"pipeline.finished"`)
- `fields`: Caller-supplied map of additional metadata
- `timestamp`: Event time

The system explicitly excludes raw CLI output, file contents, secrets, or sensitive arguments from the payload.

### Graceful Shutdown

The **`Client.Close(ctx)`** method marks the client as closed and blocks on `wg.Wait()` to allow pending goroutines to complete. It respects the supplied context deadline, returning when either all requests finish or the context expires. This prevents data loss during rapid program termination while ensuring the CLI exits within a reasonable timeframe.

## The Split Between Local and Remote Data Collection

Understanding the architectural boundary between local processing and remote transmission reveals how the system maintains privacy and performance:

- **Local Decision Authority**: All enable/disable logic, configuration resolution, and privacy checks execute locally before any network code initializes. The `telemetryDisabled()` function and `Default()` constructor act as gatekeepers that prevent remote contact when telemetry is opted out.

- **In-Process Buffering**: Events are queued only in memory via goroutines and `sync.WaitGroup` counters. There is no persistent local storage, log files, or crash dumps containing telemetry data.

- **Fail-Silent Design**: If the HTTP POST fails or times out, the payload is silently dropped without retry logic. Remote failures do not affect CLI execution or exit codes.

- **Zero Data Leakage**: When `noopSink` is active (telemetry disabled), the `Track` and `Pageview` methods return immediately without serialization or network allocation, guaranteeing that disabled telemetry means zero data emission.

## Practical Implementation Examples

### Checking and Controlling Telemetry State

Disable telemetry completely before running sensitive commands:

```go
os.Setenv("NO_MISTAKES_TELEMETRY", "off")
if !telemetry.Enabled() {
    fmt.Println("Telemetry is disabled")
}

```

### Tracking Custom Events

Send structured events with custom fields:

```go
fields := telemetry.Fields{
    "run_id":   runID,
    "pipeline": "review",
    "duration": 1234, // milliseconds
}
telemetry.Track("pipeline.finished", fields)

```

### Implementing Graceful Shutdown

Ensure all pending telemetry transmits before program exit:

```go
defer func() {
    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
    defer cancel()
    _ = telemetry.Close(ctx) // wait for pending sends
}()

```

## Summary

- The telemetry system lives in [`internal/telemetry/telemetry.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/telemetry/telemetry.go) and exposes a simple `Sink` interface with `Track`, `Pageview`, and `Close` methods.
- **Local collection** handles all configuration via environment variables (`NO_MISTAKES_TELEMETRY`, `NO_MISTAKES_UMAMI_WEBSITE_ID`, `NO_MISTAKES_UMAMI_HOST`) and falls back to `noopSink` when disabled or misconfigured.
- **Remote delivery** only occurs when a valid `Client` is constructed, sending JSON payloads asynchronously via HTTP POST to the Umami endpoint with a 1-second timeout.
- Events are sent in goroutines tracked by `sync.WaitGroup`, with graceful shutdown handled via `Close(ctx)`.
- No secrets, file contents, or raw CLI output leaves the process; only explicit event names and supplied fields transmit to the server.

## Frequently Asked Questions

### How do I completely disable telemetry in no-mistakes?

Set the environment variable `NO_MISTAKES_TELEMETRY` to `off`, `false`, or `0`. When disabled, the system instantiates `noopSink` and discards all events locally without attempting network connections or serializing payloads.

### What data is sent to the remote Umami endpoint?

The remote server receives only the event name (e.g., `"cli.start"` or `"pipeline.finished"`), the configured website ID, and any fields explicitly passed by the caller. The system does not transmit raw command output, file contents, environment variables (beyond the opt-out flag), or secrets.

### Does telemetry affect CLI performance?

No. The telemetry system uses non-blocking goroutines for each event with a 1-second HTTP timeout. Failed transmissions are silently dropped without retry logic, ensuring that analytics collection never blocks the main execution thread or slows down user-facing operations.

### Where is the telemetry code located for review?

The core implementation resides in [`internal/telemetry/telemetry.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/telemetry/telemetry.go). Unit tests verifying environment variable handling and fallback behavior are in [`internal/cli/telemetry_test.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/cli/telemetry_test.go), while example usage from the CLI surface appears in [`internal/cli/axi_telemetry_test.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/cli/axi_telemetry_test.go).