Privacy-First Telemetry: How gitaiflow Proves It's Not Sending Your Code Anywhere
Inside the opt-in telemetry design — exactly what's collected, what's permanently excluded, and how the local usage log keeps you in control before anything ever leaves your machine.
CB### 1. A Tool That Talks to AI Providers Has One Job to Be Honest About
gitaiflow already sends something to a third party on every run: your diff, handed to whichever AI provider you've configured, so it can write your commit summary. That part is unavoidable — it's the whole point of the tool. What isn't unavoidable is whether gitaiflow also quietly reports back on you while it's at it.
That's the trust question this post actually answers. Not "is my code safe" in the abstract, but specifically: separate from the AI call you already agreed to, does gitaiflow send anything else, to anyone, ever — and if so, exactly what?
The short answer is: only if you say yes, and only thirteen fixed fields, none of which describe your code.
2. Two Logs, Kept Deliberately Separate
gitaiflow actually keeps two different records, and conflating them is the easiest way to misunderstand the design:
- The local usage log (
~/.gitaiflow/usage.jsonl) — written on every run, no exceptions, no opt-in. It never leaves your machine. - Remote telemetry — sent to a server only if you've explicitly opted in. Off by default.
The local log is allowed to be more detailed, because it never goes anywhere: it exists purely so you can answer "how much have I used this tool today" for yourself, and optionally enforce your own soft budget (more on that in section 8). Remote telemetry is a much smaller, fixed subset of that — built specifically to never include anything the local log is allowed to have.
Keeping them as two separate mechanisms, rather than one log that sometimes gets uploaded, is what makes the "never sent" guarantee possible to state precisely instead of vaguely.
3. The Consent Flow, Step by Step
The first time telemetry status actually matters and you're sitting at an interactive terminal, gitaiflow asks:
Enable anonymous usage telemetry? [y/N]
A bare Enter, Ctrl-C, or EOF is treated as no — this is a real opt-in, not a dismissible banner you can accidentally click through. Your answer is saved locally so you're only asked once per machine, and every run after that follows the same path:
Notice where the branch happens: the local usage record gets written before gitaiflow even checks your telemetry answer. That ordering isn't an accident — it means the "did telemetry send" question is answered independently, every time, by a single boolean check, not buried inside whatever code path happens to be generating your summary that run.
In a non-interactive context — CI, piped output, a redirected shell — there's no terminal to prompt, so gitaiflow never blocks waiting for one. It defaults to off for that run and asks again the next time a human is actually at the keyboard.
4. What Actually Gets Sent — All Thirteen Fields, No More
If you do say yes, here is the complete payload. Not a sample, not "fields like these" — this is every key the code is allowed to put in the request body:
| Field | Example |
|---|---|
install_id | random UUID, generated once locally |
event | "run" |
timestamp | 2026-08-19T14:32:07Z |
gitaiflow_version | "1.0.5" |
ai_provider | "gemini" |
model_name | "gemini-flash-lite-latest" |
target_type | "file" or "directory" |
files_changed_count | 4 |
tokens_estimated_in / tokens_estimated_out | 1832 / 210 |
duration_ms | 2140 |
success | true |
os | "linux" |
That's operational shape, not content: how many files changed, not which files; how long the run took, not what it produced. Laid out against what's deliberately excluded, the boundary is easier to see at a glance than in a table:
5. What Never Leaves Your Machine, Even With Telemetry On
To say it as plainly as the code does: your repository name, file paths, file contents, diff content, Git author or branch, commit messages, and the AI-generated summary text are never sent, under any telemetry setting. Also excluded: API keys, provider credentials, and anything from your .env file.
This isn't a filtering step applied to a bigger payload before it goes out — the function that builds the telemetry payload simply never has access to any of that data in the first place. There's no diff, no file path, and no summary text anywhere in scope when that payload gets assembled, so there's nothing to accidentally leak by a future bug in a filter. The separation is structural, not procedural.
6. Three Ways to Control It
GITAIFLOW_TELEMETRY is the fast, session-only override — handy for CI, where you want to flip telemetry per-job without touching your saved answer:
export GITAIFLOW_TELEMETRY=true # or false
It always wins for the process it's set in, but it never writes anywhere — close that shell and your durable answer is exactly what it was before.
For a durable answer that sticks across terminals and reboots, use the --telemetry subcommands instead:
gitaiflow --telemetry enable # turn telemetry on, saved locally gitaiflow --telemetry disable # turn it back off gitaiflow --telemetry status # show the current saved answer gitaiflow --telemetry history # every recorded answer, with version + timestamp
Each of these exits immediately — none of them run a summary or touch git. history is worth calling out on its own: every consent change is appended, never overwritten, so you get a full local audit trail of your own decisions — enabled on this date, disabled on that one — not just today's current answer.
One deliberate detail: the saved consent file lives as a sibling of ~/.gitaiflow, not inside it. That means rm -rf ~/.gitaiflow clears your local usage log without silently resetting your telemetry decision back to "unanswered." Changing your mind is a decision you make on purpose, through --telemetry, not a side effect of cleaning up a directory.
7. Why `install_id` Isn't a Fingerprint
The one identifier in the payload is install_id — a random UUID generated the first time telemetry is actually confirmed enabled, and reused after that so events from the same installation can be grouped over time. It's deliberately disconnected from anything that could identify you: not your Git email, not your machine hostname, not your repository path. It's generated after consent, too — say no, and one is never created for you at all.
If you want a clean break, deleting it is a single file:
rm ~/.gitaiflow/install_id
A new random one is generated the next time telemetry needs it. This has no effect on your consent setting either way — it's a separate axis entirely.
8. The Local Guardrails Are a Courtesy, Not Enforcement
Because the local usage log is written on every run regardless of telemetry, it can double as a self-imposed budget check:
export GITAIFLOW_MAX_RUNS_PER_DAY=20 export GITAIFLOW_MAX_TOKENS_PER_DAY=50000
Cross either threshold and gitaiflow prints a warning. That's all it does — it's a plain local file, readable and deletable like any other, so treat it as a nudge against an accidental cloud-model bill, not a license or quota server watching over you. Nothing about this check involves the network, and nothing about it involves the remote telemetry payload either; it reads only your own local history.
Want the Full Picture?
This post covers the trust boundary specifically. For the complete configuration reference, every provider's exact setup, and the telemetry server side of things, see the gitaiflow documentation ↗.
What I'd Tell You If You're Building a Dev Tool That Talks to a Third Party
- Separate "local record" from "remote send" as two different code paths, not one path with a flag. If the function that builds your outbound payload structurally cannot see your sensitive data, you don't have to trust a filter to catch it every time.
- Make the default the safe one, and make silence mean no. A bare Enter or a closed terminal should never be interpreted as consent.
- Publish the exact field list, not a description of it. "We collect anonymous usage stats" invites doubt. A thirteen-row table that matches the code line for line doesn't.
- Let people audit their own answer. A durable, appendable consent history costs almost nothing to build and answers "wait, did I actually agree to this?" without anyone having to ask you.