Usage telemetry

The gaffer VS Code extension collects anonymous usage statistics and sends them to Kurrent, Inc. when the extension runs. Telemetry data helps us refine and improve gaffer based on real usage patterns.

The extension also spawns the gaffer CLI as a child process for several of its features (LSP, debug sessions, MCP). The CLI emits its own telemetry under its own rules - see the gaffer CLI's telemetry notice for what it collects. When telemetry is on, the extension passes its emitter_id to those spawned CLIs so the extension and its child processes are recognised as one user. Opting out of telemetry in the extension also suppresses telemetry in CLI processes the extension spawns. CLI invocations made directly from a terminal follow the CLI's own opt-outs.

What is usage telemetry

Gaffer telemetry only tracks non-Personally-Identifiable Information. Collected data does not allow Kurrent to fingerprint users by any of the collected data points.

Examples of the telemetry data collected:

What the extension does not track:

There are two event types. Each is wrapped in an envelope alongside shared install metadata (extension version, host OS, architecture, the runtime environment - local or ci) before being sent.

The receiving worker stamps each event with its own deploy timestamp.

The precise wire format lives in telemetry/schemas/events.cue (event shapes) and telemetry/schemas/wire.cue (envelope).

extension_activated

Records whether the gaffer CLI binary is reachable on the PATH when the extension activates, along with the editor version, the CLI version when reachable, and a bucketed activation duration. When the CLI is unreachable, the event carries a categorical reason for the failure: binary_not_found, binary_spawn_failed, timeout, workspace_untrusted, or unknown_error. No paths or error messages from the failed spawn are attached.

Example envelope
{
	"schema_version": "1",
	"emitter_id": "0b51e34d-aac8-4cce-bce4-9d2c7c6e3b8a",
	"run_id": "01938e7a-1b2c-7d4e-9faf-2a3b4c5d6e7f",
	"context": {
		"emitter": "vscode",
		"lib_version": "0.4.2",
		"os": "darwin",
		"arch": "arm64",
		"runtime_environment": "local"
	},
	"events": [
		{
			"name": "extension_activated",
			"timestamp": "2026-05-08T12:00:00.000Z",
			"properties": {
				"editor": "vscode",
				"editor_version": "1.95.2",
				"cli_reachable": true,
				"cli_version": "0.4",
				"activation_duration_ms": 100
			}
		}
	]
}

exception

Records crashes in the extension's own code (unhandled JS errors in the extension host). Exception messages are always written by gaffer and never propagated from your projection code. Stack frames are scrubbed: file basenames only, user-JS frames dropped entirely.

Example envelope
{
	"schema_version": "1",
	"emitter_id": "0b51e34d-aac8-4cce-bce4-9d2c7c6e3b8a",
	"run_id": "01938e7a-1b2c-7d4e-9faf-2a3b4c5d6e7f",
	"context": {
		"emitter": "vscode",
		"lib_version": "0.4.2",
		"os": "darwin",
		"arch": "arm64",
		"runtime_environment": "local"
	},
	"events": [
		{
			"name": "exception",
			"timestamp": "2026-05-08T12:34:56.000Z",
			"properties": {
				"exceptions": [
					{
						"type": "Error",
						"value": "failed to start LSP client",
						"in_app": true,
						"stacktrace": {
							"type": "raw",
							"frames": [
								{
									"filename": "client.ts",
									"function": "spawnLanguageClient",
									"lineno": 123,
									"in_app": true
								}
							]
						}
					}
				],
				"phase": "activation"
			}
		}
	]
}

Disclosure

On first activation, the extension shows a notification similar to:

Gaffer collects anonymous usage data to improve your experience. The data is collected by Kurrent, Inc. [Learn more]

[Dismiss] [Learn more] [Disable telemetry]

Closing the notification with the X is treated the same as [Dismiss] (telemetry stays on). [Learn more] opens this page. The notification re-appears on next activation until you pick [Dismiss] or [Disable telemetry].

If telemetry collection has already been disabled (for example via KURRENTDB_TELEMETRY_OPTOUT carried over from KurrentDB, or DO_NOT_TRACK), no disclosure is shown.

Reporting frequency

The extension emits events at the boundary of work, not on a periodic schedule:

An extension session that does no work beyond activation emits one event. There is no periodic heartbeat.

How to opt out

Telemetry transmission can be disabled by any one of the following:

When opted out, the extension does not collect telemetry. No envelope is constructed and no event is recorded locally. The opt-out also propagates to CLI processes the extension spawns (LSP, gaffer dev, MCP), so those processes do not emit telemetry either. Opting out mid-session takes effect for future spawns. CLI processes already running exit naturally; their own opt-out is checked when they next start.

How to see what's being sent

Set GAFFER_TELEMETRY_DEBUG=1 (truthy values: 1, true, yes, on) in the environment VS Code launches under, and the extension prints every event as JSON to the Gaffer output channel before sending it. When opted out, no envelopes are constructed and nothing is printed.

Where data is stored

Telemetry data is stored in PostHog's EU instance. Envelopes transit Cloudflare's edge network on the way there. Cloudflare's standard request logs include IP and are retained for around 30 days; gaffer does not forward IPs to PostHog. The worker that handles ingest is open source and lives in the gaffer repository, alongside the machine-readable schema for the events described above.

How to delete your data

Email privacy@kurrent.io with the identifier the extension stores below. All events associated with that id are deleted from PostHog within 30 days. Session-stitching and identity-merge rows the worker holds for that id expire automatically within 25 hours and 30 days respectively.

The extension persists its telemetry id in telemetry.json inside VS Code's global storage directory for the gaffer extension (kurrent-io.gaffer). The full path depends on your OS and editor variant:

Replace Code with Code - Insiders, Cursor, etc. for other editor variants. The telemetry_id field is your id, identical to what every envelope carries as emitter_id and what PostHog stores as the person id.