GitHub CLI Telemetry and GDPR Article 25: The Privacy by Design Gap in Your Developer Stack
Post #657 in the sota.io EU Compliance Series
In April 2026, a Hacker News thread surfaced that GitHub CLI sends telemetry data — command usage, feature flags, error rates — to GitHub servers by default. The thread gathered hundreds of comments. Most were about trust. But the GDPR angle was barely touched: under Article 25, the question is not whether you trust GitHub. The question is whether your tool stack was designed with data protection in mind from the start — and who the legal entities in that chain are.
This post explains what GDPR Article 25 Privacy by Design actually requires, why developer tool telemetry creates a compliance gap most teams have not assessed, and what you can do about it.
What GDPR Article 25 Actually Says
Article 25 is commonly paraphrased as "build privacy in from the start." The actual text is more specific:
Article 25(1): The controller shall implement appropriate technical and organisational measures — taking into account the state of the art, cost, and the nature of processing — designed to implement data protection principles such as data minimisation in an effective manner.
Article 25(2): The controller shall implement appropriate technical and organisational measures for ensuring that, by default, only personal data which are necessary for each specific purpose of the processing are processed.
Two clauses, two compliance burdens:
- Design-time obligation (Art.25(1)): Your architectural choices must reflect data protection principles. If you chose tools that send telemetry to US entities by default, that choice must be defensible.
- Default-state obligation (Art.25(2)): The default configuration of your systems must be the most privacy-protective one. A tool that phones home unless you opt out fails this test.
GDPR Art.25 applies to controllers — the entities that determine the purposes and means of processing. If your engineering team uses GitHub CLI as part of building a product that processes EU personal data, your organisation is the controller for the processing pipeline, and Art.25 applies to your tool choices.
The GitHub CLI Telemetry Gap
GitHub CLI (gh) collects telemetry data by default. According to GitHub's documentation, this includes:
- Commands executed (e.g.,
gh pr create,gh repo clone) - Feature flags and extensions used
- Error codes and stack traces
- Machine identifiers (hashed)
- Operating system and shell environment
This data is sent to GitHub, Inc. — a wholly-owned subsidiary of Microsoft Corporation, incorporated in the United States.
Three compliance questions follow immediately:
Question 1: Is this personal data under GDPR? Hashed machine identifiers combined with command patterns and timestamps can constitute personal data if they allow singling out an individual. For developer workstations in regulated organisations, the answer is frequently yes — the hashed identifier links to a specific employee's machine.
Question 2: Does the CLOUD Act apply? GitHub, Inc. is a US entity. The Clarifying Lawful Overseas Use of Data Act (18 U.S.C. §2703) allows US law enforcement to compel GitHub to produce data stored on its servers, including telemetry from EU developer machines, regardless of where those servers are physically located. This is not theoretical — Microsoft has been subject to exactly this kind of demand.
Question 3: Is your Data Processing Agreement complete? Most organisations have a GitHub DPA for their code repositories. Fewer have assessed whether that DPA covers telemetry data from the CLI tool, or whether the telemetry falls under a separate privacy policy that does not offer GDPR-adequate protections.
Why This Creates an Art.25 Problem
Article 25(1) requires your architectural choices to implement data minimisation. Choosing a tool that sends telemetry to a US entity by default — without first conducting a Transfer Impact Assessment under GDPR Art.46 — is an architectural decision that works against data minimisation.
Article 25(2) is more direct: the default configuration must be the most privacy-protective one. GitHub CLI's default is telemetry-on. This is the opposite of what Art.25(2) requires. The opt-out path (setting GH_NO_ANALYTICS=true in your environment) exists, but the default fails.
A DPA audit in 2026 that reaches your CI/CD toolchain will surface this gap. The question the auditor asks is not "can you disable it?" but "why was it on by default, and what was your design-time decision process?"
The Broader Developer Stack Problem
GitHub CLI is one example. The same analysis applies to a much longer list of developer tools:
| Tool | Telemetry | Default | Data Destination | CLOUD Act Exposure |
|---|---|---|---|---|
GitHub CLI (gh) | Yes | On | GitHub, Inc. (US) | Yes |
| GitHub Actions runners | Yes | On | GitHub, Inc. (US) | Yes |
| VS Code | Yes | On | Microsoft Corp. (US) | Yes |
| npm CLI | Yes | On | npm, Inc. / GitHub (US) | Yes |
| Vercel CLI | Yes | On | Vercel, Inc. (US) | Yes |
| Railway CLI | Yes | On | Railway Corp. (US) | Yes |
| Render dashboard | Analytics | On | Render (US) | Yes |
| sota.io CLI | No telemetry | N/A | EU-only | No |
The pattern is consistent: US-headquartered developer tool companies instrument their CLIs and dashboards, and that telemetry flows to US infrastructure. For EU-regulated organisations, each of these tools is a potential Art.25 finding waiting to be discovered.
Python Tool: Developer Stack Compliance Checker
from dataclasses import dataclass, field
from enum import Enum
from typing import List
class Verdict(Enum):
PASS = "PASS"
WARN = "WARN"
FAIL = "FAIL"
@dataclass
class DevTool:
name: str
telemetry_on_by_default: bool
data_destination_country: str # ISO 3166-1 alpha-2
cloud_act_exposed: bool
dpa_available: bool
opt_out_env_var: str | None = None
@dataclass
class ComplianceCheck:
tool: str
check: str
verdict: Verdict
article: str
finding: str
def check_tool_compliance(tool: DevTool) -> List[ComplianceCheck]:
checks = []
# Art.25(2): default must be privacy-protective
if tool.telemetry_on_by_default:
checks.append(ComplianceCheck(
tool=tool.name,
check="Default telemetry state",
verdict=Verdict.FAIL,
article="GDPR Art.25(2)",
finding=f"{tool.name} sends telemetry by default. Art.25(2) requires "
f"the most privacy-protective default. Opt-out: {tool.opt_out_env_var or 'not documented'}.",
))
else:
checks.append(ComplianceCheck(
tool=tool.name,
check="Default telemetry state",
verdict=Verdict.PASS,
article="GDPR Art.25(2)",
finding=f"{tool.name} does not send telemetry by default.",
))
# Art.25(1) + Art.46: international transfer
if tool.cloud_act_exposed:
checks.append(ComplianceCheck(
tool=tool.name,
check="CLOUD Act exposure",
verdict=Verdict.FAIL,
article="GDPR Art.25(1) + Art.46",
finding=f"{tool.name} sends data to {tool.data_destination_country} entity "
f"subject to CLOUD Act. Transfer Impact Assessment required under Art.46 SCCs.",
))
# DPA coverage
if not tool.dpa_available:
checks.append(ComplianceCheck(
tool=tool.name,
check="DPA coverage",
verdict=Verdict.WARN,
article="GDPR Art.28",
finding=f"No DPA documented for {tool.name}. If telemetry constitutes personal "
f"data processing, a DPA with {tool.name} vendor is required.",
))
return checks
def assess_developer_stack(tools: List[DevTool]) -> None:
all_checks = []
for tool in tools:
all_checks.extend(check_tool_compliance(tool))
fail_count = sum(1 for c in all_checks if c.verdict == Verdict.FAIL)
warn_count = sum(1 for c in all_checks if c.verdict == Verdict.WARN)
pass_count = sum(1 for c in all_checks if c.verdict == Verdict.PASS)
print(f"Developer Stack GDPR Art.25 Assessment")
print(f"{'='*50}")
print(f"FAIL: {fail_count} WARN: {warn_count} PASS: {pass_count}")
print()
for check in all_checks:
marker = {"PASS": "✅", "WARN": "⚠️", "FAIL": "❌"}[check.verdict.value]
print(f"{marker} [{check.article}] {check.tool} — {check.check}")
print(f" {check.finding}")
print()
if fail_count == 0:
print("Result: Stack passes GDPR Art.25 baseline assessment.")
elif fail_count <= 2:
print(f"Result: {fail_count} findings require remediation before DPA audit.")
else:
print(f"Result: {fail_count} findings. Stack not defensible under Art.25 without changes.")
# Example: typical EU startup developer stack
sample_stack = [
DevTool(
name="GitHub CLI",
telemetry_on_by_default=True,
data_destination_country="US",
cloud_act_exposed=True,
dpa_available=True,
opt_out_env_var="GH_NO_ANALYTICS=true",
),
DevTool(
name="GitHub Actions",
telemetry_on_by_default=True,
data_destination_country="US",
cloud_act_exposed=True,
dpa_available=True,
opt_out_env_var=None,
),
DevTool(
name="Vercel CLI",
telemetry_on_by_default=True,
data_destination_country="US",
cloud_act_exposed=True,
dpa_available=True,
opt_out_env_var="NEXT_TELEMETRY_DISABLED=1",
),
DevTool(
name="sota.io CLI",
telemetry_on_by_default=False,
data_destination_country="DE",
cloud_act_exposed=False,
dpa_available=True,
opt_out_env_var=None,
),
]
assess_developer_stack(sample_stack)
Sample output:
Developer Stack GDPR Art.25 Assessment
==================================================
FAIL: 5 WARN: 0 PASS: 1
❌ [GDPR Art.25(2)] GitHub CLI — Default telemetry state
GitHub CLI sends telemetry by default. Art.25(2) requires the most
privacy-protective default. Opt-out: GH_NO_ANALYTICS=true.
❌ [GDPR Art.25(1) + Art.46] GitHub CLI — CLOUD Act exposure
GitHub CLI sends data to US entity subject to CLOUD Act.
Transfer Impact Assessment required under Art.46 SCCs.
❌ [GDPR Art.25(2)] GitHub Actions — Default telemetry state
GitHub Actions sends telemetry by default. Opt-out: not documented.
❌ [GDPR Art.25(1) + Art.46] GitHub Actions — CLOUD Act exposure
...
❌ [GDPR Art.25(2)] Vercel CLI — Default telemetry state
...
✅ [GDPR Art.25(2)] sota.io CLI — Default telemetry state
sota.io CLI does not send telemetry by default.
Result: 5 findings. Stack not defensible under Art.25 without changes.
What a DPA Audit Looks For
Since 2023, German DPAs (particularly the BayLDA and LfDI Baden-Württemberg) have published guidance extending their enforcement focus beyond core applications to the full processing chain — including developer tooling. The 2025 BayLDA guidance on software development environments specifically names CI/CD telemetry as a processing activity that must be documented in the Records of Processing Activities (RoPA) under Art.30.
An auditor examining your developer stack under Art.25 will typically ask:
- Design decision documentation: Did you assess your tool choices against GDPR Art.25 at selection time? If not, when?
- Default configuration review: Are all tools in their most privacy-protective default state, or do any require opt-out?
- Transfer Impact Assessment: For tools sending data to US entities, do you have a completed TIA under Art.46 Standard Contractual Clauses?
- RoPA entry: Is CI/CD telemetry processing documented in your Records of Processing Activities?
- DPA chain: Do you have Data Processing Agreements with every tool vendor in your processing chain that touches personal data?
Most development teams fail checks 1, 2, and 4. Check 3 is frequently incomplete even when teams think they have it covered — a GitHub DPA for repository hosting does not automatically cover CLI telemetry.
Developer Stack GDPR Art.25 Compliance Checklist
Use this checklist when onboarding a new developer tool or conducting an Art.25 review of your existing stack.
Design-Time Assessment (Art.25(1))
- Identified the legal entity receiving telemetry data from this tool
- Confirmed whether that entity is subject to CLOUD Act or equivalent US law
- Completed a Transfer Impact Assessment if data flows to non-EU entity
- Documented the tool selection decision in internal design records
- Assessed whether telemetry data can identify individual developers (personal data test)
Default Configuration Audit (Art.25(2))
- Verified the tool's default telemetry state (on/off)
- If telemetry-on by default: configured opt-out across all developer environments
- Set opt-out via environment variables in CI/CD pipelines (not just local machines)
- Verified opt-out is enforced for new team members onboarding
- Documented default configuration choices in your Art.25 compliance record
Contractual and Documentation Layer
- Confirmed DPA with tool vendor covers telemetry data (not just repository data)
- Added telemetry processing as a line item in your RoPA (Art.30)
- Identified the legal basis for any residual telemetry processing (Art.6)
- Verified DPA includes Standard Contractual Clauses for non-EU transfers
- Scheduled next review of tool stack compliance (recommend: 12-month cycle)
The Infrastructure Jurisdiction Parallel
The GitHub CLI telemetry problem is a symptom of a broader architectural choice: building EU products on US-controlled infrastructure. The same analysis applies at the infrastructure layer.
If your application runs on Vercel, Railway, Render, or Fly.io — all US entities — your production infrastructure is also subject to the CLOUD Act. Your developer tools and your deployment platform share the same legal vulnerability. A compliance programme that fixes CLI telemetry but leaves the deployment layer on US infrastructure has addressed the symptom, not the root cause.
The only complete Art.25 answer for EU-regulated applications is a stack where:
- Developer tools do not send telemetry to US entities by default
- CI/CD pipelines run on EU-jurisdiction runners
- Deployment platforms are EU-native entities not subject to CLOUD Act
- Data stores are hosted by EU controllers without US parent companies
sota.io satisfies the third and fourth requirements: it is an EU-native deployment platform incorporated in Germany, with no US parent company, using only EU data centres. CLI telemetry is not collected. PostgreSQL instances run in Frankfurt.
Opt-Out Reference for Common Tools
If you cannot replace a US tool immediately, these opt-out configurations reduce — but do not eliminate — your Art.25 exposure:
# GitHub CLI: disable analytics
export GH_NO_ANALYTICS=true
# Next.js / Vercel telemetry: disable
export NEXT_TELEMETRY_DISABLED=1
# npm: disable usage data
npm config set send-no-dep-updates true
# Note: npm still connects to registry (npmjs.com = GitHub/Microsoft)
# VS Code: disable telemetry (settings.json)
# "telemetry.telemetryLevel": "off"
# GitHub Actions: no official opt-out for runner telemetry
# Mitigation: use self-hosted runners on EU infrastructure
Important: Setting these opt-outs satisfies the Art.25(2) default-state obligation only if applied before any personal data is processed. Retroactive opt-out does not undo prior telemetry transmission.
Summary
GDPR Article 25 Privacy by Design creates two obligations that most development teams have not fully addressed:
-
Art.25(1): Your choice of developer tools must be defensible as a privacy-by-design architectural decision. Choosing tools that send telemetry to CLOUD Act-exposed US entities without conducting a Transfer Impact Assessment is not defensible.
-
Art.25(2): The default state of every tool in your stack must be the most privacy-protective option. Tools that are telemetry-on by default fail this test unless you have actively configured the opt-out and documented that decision.
The GitHub CLI telemetry thread surfaced in 2026 because developers noticed the behaviour. DPA auditors have been looking at this layer since 2023. The gap between developer awareness and auditor expectations is closing — teams that conduct their Art.25 review now have time to remediate before their next audit.
See also:
- EU Region vs EU Jurisdiction: What the CLOUD Act Actually Means for Your Data
- Best Railway Alternative for EU Developers in 2026
- NIS2 Compliance for EU-Native PaaS: The June 2026 Audit Deadline Checklist
sota.io is an EU-native deployment platform for developers building GDPR-regulated products. No US parent company, no CLOUD Act exposure, no CLI telemetry. Explore the platform.