Telemetry
CDK Insights ships with optional, aggregate-only telemetry that helps us prioritise rules and grow auto-fix coverage. It is off by default and only sends data when you explicitly opt in and have a license key configured (free-tier keys are enough). This page documents exactly what does and does not leave your machine.
The privacy posture in four lines
- Off by default. Nothing is sent until you set
telemetry.enabled: truein.cdk-insights.json. - License key required. Telemetry only fires when both the opt-in flag is on and a license key is configured. Without a license, the CLI has no hashed identifier to send and skips the call entirely โ no per-machine UUIDs are ever generated. Free-tier keys count.
- License ID is hashed. The CLI computes
sha256(licenseKey)locally and only sends the first 32 hex chars. The backend cannot reverse the hash. - Aggregates only. Counts, severity buckets, and rule-id histograms โ never code, resource names, file paths, or fingerprints.
- Fire-and-forget. Sends are bounded to a 1.5-second timeout and never block, slow, or fail your run. If the network is down the event is dropped silently.
How to opt in (or out)
During cdk-insights init
When you run init in an interactive terminal, the CLI prompts you after the license-key step:
๐ Anonymous Telemetry
Sending aggregate counts (severities, rule histograms, fix activity) helps us
prioritise rules and improve auto-fix coverage. The license ID is sha256-hashed
before transmission. No resource names, file paths, or code ever leave the machine.
Enable telemetry? (y/N): The default is no โ pressing Enter declines. Your answer is written to .cdk-insights.json and you can change it any time.
By editing .cdk-insights.json
Add (or remove) the telemetry block at the top level of the config:
{
"output": "table",
"failOnCritical": true,
"telemetry": {
"enabled": true
}
}Set enabled: false (or remove the block entirely) to turn telemetry off. Commit the file to git so the choice is versioned with your project.
What is collected
Every telemetry event is wrapped in the same envelope. The envelope itself plus the event-specific payload are the only things transmitted.
hashedLicenseIdsha256(licenseKey) truncated to 32 hex characters. The backend can count unique customers but cannot reverse-map back to a key.
tierfree, pro, team, or trial. Lets us segment usage analytics by tier without correlating individual users.
cliVersionThe cdk-insights CLI version (e.g. 1.24.0). Used to deprecate old releases responsibly.
platformCoarse OS family โ darwin, linux, win32. No hostnames or usernames.
emittedAtISO 8601 timestamp the CLI generated when sending the event.
eventThe aggregate payload โ see the three event shapes below. Never contains code, resource names, file paths, or fingerprints.
What is NOT collected
The local-first principle still holds with telemetry on. None of the following ever leaves your machine:
- โขResource names (bucket names, table names, function names)
- โขAWS account IDs, region names, or stack ARNs
- โขFile paths or directory structure
- โขCode snippets, props values, or any source content
- โขConstruct paths or fingerprints from .cdk-insights-baseline.json
- โขIP addresses (beyond what API Gateway logs operationally)
- โขLicense keys (only the sha256 prefix)
- โขHostnames, usernames, or git author info
Sensitive-data findings (hardcoded credentials etc.) are already filtered out of the analysis pipeline before any backend call regardless of telemetry โ see the static analysis docs for that boundary.
The three event types
baseline_writtenWhen: After cdk-insights scan --writeBaseline finishes successfully.
{
type: 'baseline_written',
totalFindings: number, // count of fingerprints written
severityCounts: { CRITICAL, HIGH, MEDIUM, LOW },
ruleHistogram: { 'AwsSolutions-S10': 4, 'CDKI-S3-ENCRYPTION': 2, ... },
uniqueServices: number // distinct AWS::<Service>::* counts
}Why we want it: Lets us understand which rules fire most often across the user base and how big a typical baseline is.
diff_runWhen: After cdk-insights scan --diff completes (regardless of exit code).
{
type: 'diff_run',
newFindings: number, // count of issues new since baseline
existingSuppressed: number, // count of issues filtered by baseline
newSeverities: { CRITICAL, HIGH, MEDIUM, LOW },
baselineGeneratedAt?: string, // ISO timestamp of the baseline
failedOnCritical: boolean // true if exit code was 1
}Why we want it: Drives the "ratchet working" trend: how often diff runs fail on new criticals vs pass cleanly, and how stale typical baselines get.
fix_runWhen: After cdk-insights fix completes (dry-run or --apply).
{
type: 'fix_run',
mode: 'dry-run' | 'apply',
applied: number,
skipped: number,
errored: number,
rulesFixed: { 'AwsSolutions-DDB3': 1, 'CDKI-S3-ENCRYPTION': 2, ... },
ruleFilter?: string // value of --rule, if any
}Why we want it: Tells us which rules are landing fixes successfully vs being skipped/errored โ the signal we need to grow auto-fix coverage.
Where things stand today
- Phase 1: collect-only. Events are persisted to a backend table, but there is no dashboard yet. Phase 2 will surface a Pro-tier trend view once we have enough signal.
- Anonymous endpoint. The telemetry route accepts unauthenticated POSTs because the
fixcommand does not run the auth/login flow. The hashed license ID is the only customer identifier on the row. We may tighten this to require a JWT in a later phase. - 365-day TTL. Raw events are auto-deleted after a year. Aggregates derived from them (Phase 2) will be retained longer, but those aggregates are by their nature impossible to attribute to an individual run.
- You can audit the code. The CLI is open on GitHub โ the entire telemetry path is in
src/shared/telemetry/telemetryClient.ts.
Related
Telemetry primarily reflects activity from the diff and auto-fix flows: