# What Telemetry Data Is Stored Locally vs Sent Remotely in No-Mistakes

> Learn what telemetry data No-Mistakes stores locally and what it sends remotely. Understand local storage of CLI fingerprints and remote event transmission.

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

---

**No-mistakes stores high-frequency CLI surface fingerprints in `$(NM_HOME)/telemetry-gate.json` locally to throttle network requests, while event and pageview metadata are transmitted via HTTP POST to a configured Umami analytics endpoint.**

In the `kunchenguid/no-mistakes` CLI, telemetry is architected to minimize redundant network traffic while maintaining observability. Understanding what telemetry data is stored locally vs sent remotely helps users audit data residency and developers implement custom instrumentation. The system separates local state persistence from remote transmission through two distinct mechanisms: a disk-based gating system for read-only commands and an asynchronous HTTP client for event submission.

## Local Telemetry Storage

Local telemetry exists solely to deduplicate high-frequency read operations. The system writes a small JSON file that tracks when specific CLI surfaces were last emitted, preventing duplicate remote requests for identical operations.

### ReadSurfaceGate Implementation

The `ReadSurfaceGate` type defined in [`internal/telemetry/readgate.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/telemetry/readgate.go) manages local state persistence. This gate is consulted before sending any read-only telemetry event to determine if sufficient time has elapsed since the last identical emission.

The gate writes to `$(NM_HOME)/telemetry-gate.json` atomically using `os.Rename` to prevent partial writes during crashes. According to the source analysis, the implementation limits the file size by enforcing `maxReadSurfaceEntries = 64`; when this threshold is exceeded, older entries are automatically pruned (see lines 46-71 of [`readgate.go`](https://github.com/kunchenguid/no-mistakes/blob/main/readgate.go)).

### File Location and Format

The local telemetry file contains a JSON map tracking fingerprints of read-only CLI surfaces such as `axi-status`, `axi-home`, `status`, and `runs`. Each entry stores the fingerprint together with the Unix timestamp of the last successful remote emit.

The gate file is updated only when `ReadSurfaceGate.ShouldEmit` returns true, which happens when either the fingerprint changes or the configured interval (default 30 seconds) has elapsed since the last emission.

```go
gate := telemetry.NewReadSurfaceGate(
    filepath.Join(os.Getenv("NM_HOME"), "telemetry-gate.json"),
    30*time.Second, nil,
)
if gate.ShouldEmit("axi-status", fingerprint) {
    telemetry.Pageview("/status", nil)
}

```

## Remote Telemetry Transmission

While local storage merely tracks emission timestamps, the actual telemetry content flows to a remote Umami analytics host. The `Client` type in [`internal/telemetry/telemetry.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/telemetry/telemetry.go) handles all HTTP communication asynchronously.

### Event Tracking via `Track`

When pipeline steps or CLI commands execute significant operations, they call `telemetry.Track` with an event name and arbitrary key-value fields. This method constructs a `collectRequest` payload and dispatches it via goroutine to prevent blocking the CLI.

```go
telemetry.Track("run", telemetry.Fields{
    "step":   "review",
    "status": "success",
    "duration_ms": 1234,
})

```

### Pageview Tracking via `Pageview`

For command invocations that represent user navigation (such as viewing status dashboards), the system uses `telemetry.Pageview`, recording the virtual path and associated metadata.

```go
telemetry.Pageview("/status", telemetry.Fields{
    "runs_total": 5,
})

```

### Request Payload Structure

Every remote request includes static metadata regardless of the specific method called. The JSON payload sent to the Umami `/api/collect` endpoint contains:

```json
{
  "type":"event",
  "payload":{
    "website":"<website-id>",
    "hostname":"cli",
    "title":"no-mistakes CLI",
    "url":"app://no-mistakes/run",
    "name":"run",
    "data":{"step":"review","status":"success","duration_ms":1234},
    "timestamp":1721163425
  }
}

```

The default host resolves from environment variables or falls back to `https://a.kunchenguid.com/api/send`, with the website ID sourced from `buildinfo` constants.

## Controlling Telemetry Collection

Users can completely disable both local and remote telemetry by setting the environment variable `NO_MISTAKES_TELEMETRY=off`. When this variable is present, `telemetry.Default()` returns a `noopSink` implementation that silently discards all tracking calls without creating the local gate file or issuing HTTP requests.

## Summary

- **Local storage** persists only high-frequency CLI surface fingerprints in [`telemetry-gate.json`](https://github.com/kunchenguid/no-mistakes/blob/main/telemetry-gate.json) (maximum 64 entries) to throttle redundant network requests.
- **Remote transmission** sends event names, pageview paths, caller-supplied fields, and static metadata (version, OS/arch, hostname) to the configured Umami analytics host.
- **Implementation files** are located in [`internal/telemetry/telemetry.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/telemetry/telemetry.go) (HTTP client) and [`internal/telemetry/readgate.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/telemetry/readgate.go) (local gating).
- **Privacy control** is available via the `NO_MISTAKES_TELEMETRY=off` environment variable, which switches to a no-operation sink.

## Frequently Asked Questions

### Where does no-mistakes store its local telemetry data?

No-mistakes stores local telemetry data in `$(NM_HOME)/telemetry-gate.json`. This file is created and managed by the `ReadSurfaceGate` struct in [`internal/telemetry/readgate.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/telemetry/readgate.go). It contains a JSON map of CLI surface fingerprints and their last emission timestamps, never exceeding 64 entries.

### What specific data is sent to remote analytics servers?

Remote telemetry includes event names (e.g., `run`, `command`), pageview paths (e.g., `/status`), and arbitrary key-value fields supplied by the caller. Every request also transmits static metadata: the application name (`no-mistakes`), version, operating system, architecture, hostname (`cli`), and a Unix timestamp. The data is sent to an Umami host via POST request to `/api/collect`.

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

Set the environment variable `NO_MISTAKES_TELEMETRY=off` before running any commands. This causes `telemetry.Default()` to return a `noopSink` instance, ensuring no local [`telemetry-gate.json`](https://github.com/kunchenguid/no-mistakes/blob/main/telemetry-gate.json) file is created and no HTTP requests are dispatched to analytics servers.

### Does the local telemetry file grow indefinitely?

No, the local telemetry file does not grow indefinitely. The `ReadSurfaceGate` enforces a hard limit of `maxReadSurfaceEntries = 64` and automatically prunes older entries during write operations. The file is updated atomically using `os.Rename` to prevent corruption.