SPOT TAPE
FRNT-200K2.180+1.42%
MID-128K0.4204−0.81%
OW-70B0.0840+3.11%
OW-8B0.0191+0.44%
VIS-1M1.6400−2.19%
RSN-XL6.9000+0.93%
EMB-S0.01100.00%
14:02:07 UTC
Documentation v1 · UPDATED 21 AUG 2026
Get an API key
On this page

SDK Telemetry Decisions

This record resolves the two telemetry decisions the tech spec left open — the opt-in default and the distinct_id strategy — for the Rust, Python, and TypeScript SDKs. Both are inputs to the SDK telemetry event schema and are quoted verbatim there. Each entry follows the tech-spec Decisions pattern: Status / Context / Options / Decision / Consequences.

The binding opt-in default is also recorded as a repo ADR at docs/decisions/sdk-telemetry-opt-in.md; this record and that ADR must agree.

Telemetry opt-in default

Status: decided

Context: The SDKs are distributed to external developers. Telemetry must never silently track anyone (a brief success criterion). The default determines the constructor/env-var shape every SDK exposes and how prominently disclosure must appear.

Options:

  • Opt-in by default — telemetry is off unless the developer explicitly enables it. Maximises privacy; funnel data is only collected from developers who opt in, so completeness is lower.
  • Opt-out with disclosure — telemetry is on by default with a prominent README + first-run disclosure and a documented way to turn it off. Maximises data completeness; relies on disclosure being unmissable.

Decision: Opt-in by default. Telemetry is off unless the developer explicitly turns it on.

  • Canonical constructor/config flag (identical string in every SDK): telemetry_enabled (Rust field telemetry_enabled, Python kwarg telemetry_enabled, TypeScript field telemetryEnabled). Default value: false.
  • Canonical environment variable that overrides the flag when set: TOKEN_GATEWAY_TELEMETRY. Values 1 / true enable; 0 / false disable. When both the constructor flag and the env var are set, the env var wins so operators can force telemetry off in any environment.
  • Disclosure surface: a telemetry section in each SDK README and a single first-run log line emitted the first time telemetry initialises. The canonical disclosure copy lives in the schema doc.

Consequences: Every SDK ships with telemetry disabled; no events are sent until the developer sets telemetry_enabled (or TOKEN_GATEWAY_TELEMETRY). Because the flag defaults to false, the disclosure copy is framed as "how to enable", not "how to turn off". Funnel completeness depends on opt-in rate, which the platform team accepts in exchange for a clean privacy posture.

Distinct_id strategy

Status: decided

Context: Cross-SDK funnel analysis (install → auth → first request → first API call → first order) requires every event from the same developer to carry the same distinct_id. The identifier must be stable across process restarts, must not embed secrets (it is sent to PostHog), and must degrade gracefully where persistent storage is unavailable (CI, serverless, read-only containers).

Options:

  • Locally-generated persisted UUID — generate a random UUIDv4 on first run and persist it to a config directory. Stable across restarts; contains no secret material.
  • Derived from API key hash — hash the developer's API key. Stable without persistence, but couples identity to a secret and breaks correlation when the key rotates or differs between install and auth steps.
  • Per-process ephemeral — new UUID each process. No persistence needed but cannot correlate events across restarts, defeating the funnel.

Decision: Locally-generated persisted UUID with an ephemeral in-memory fallback. The canonical tuple is:

  • Generation: a random UUIDv4 generated the first time telemetry initialises. Never derived from the API key or any secret.
  • Persistence: written to <config_dir>/token-gateway/telemetry-id, where <config_dir> is the OS user config directory ($XDG_CONFIG_HOME or ~/.config on Linux, ~/Library/Application Support on macOS, %APPDATA% on Windows). Overridable by setting TOKEN_GATEWAY_TELEMETRY_DISTINCT_ID, which takes precedence over the persisted file so callers can pin an id explicitly.
  • Fallback: if the config directory is unreadable or unwritable, generate a per-process ephemeral UUIDv4, use it for the lifetime of the process, and do not fail the SDK call. Correlation across restarts is lost in this case but telemetry (when enabled) still functions.

Consequences: All events from one developer on one machine share a stable distinct_id across restarts. The id contains no secret, so it is safe to send to PostHog. Every SDK reads/writes the same file path and honours the same override and fallback, so a developer who installs via one SDK and calls via another still correlates as long as the config file is shared. Stateless callers (serverless, CI) get an ephemeral id per invocation unless they set TOKEN_GATEWAY_TELEMETRY_DISTINCT_ID.