I ran the wire capture on Grok Build CLI. It ships your whole repo, git history and all.
Grok Build CLI v0.2.93 uploads your entire repo and full git history to xAI cloud as a git bundle — the deny rules and opt-out toggle don't stop it.
Short version: xAI's Grok Build CLI (v0.2.93, July 2026) does not just send the model the files it reads. It also uploads your **entire workspace — every tracked file plus your full git history — as a git bundle** to a GCS bucket, on a separate background channel. Your `--deny` rules don't block it. The "improve the model" opt-out doesn't block it. I put mitmproxy in front of it and watched a canary file I explicitly told the agent never to read leave my box anyway.
This isn't a leak from the model reading too much. It's a second pipe.
What actually ships off your box?
There are two independent channels, and that's the whole story.
**Channel 1 — the model turn.** The agent reads files, stuffs them into context, sends them to the API. This is the part you can reason about. Deny a path, the agent won't read it, it won't land in the prompt. Fine.
**Channel 2 — the background bundle.** Independently of what the model reads, the CLI packs your whole workspace into a git bundle and does a `POST /v1/storage` into a GCS bucket named `grok-code-session-traces`. The bundle is trivially identifiable — the request body opens with the `# v2 git bundle` marker. Pull that body out of the capture, save it, `git clone bundle.pack ./recovered`, and you've got the repo back. Full working tree. Full history.
The researchers' canary repo made this impossible to hand-wave. They dropped a file named `CANARY-XR47P2-NEVERREAD` and set a deny rule so the agent would never touch it. It never entered the model channel. It came back **verbatim from the bundle**. The deny rule did exactly what it says — kept it out of the chat — and did nothing about the second pipe.
If you want the buttoned-up version of the finding without my editorializing, [the straight news writeup on the capture](/posts/researchers-grok-build-cli-ships-your-entire-repo-and-git-history-to-xai-cloud-d/) lays out exactly what the researchers claim and how they claim it.
The numbers
The task the model was doing needed a handful of files. The bundle shipped the entire repo and history. The researchers clocked roughly a **27,800× ratio** between what the model needed as context and what actually egressed. That's not a rounding error or a chatty prompt. That's "we uploaded your repo and also let the agent do its job."
For context on why that ratio matters: a normal coding-agent turn sends kilobytes of relevant context. A git bundle of a mature repo is tens to hundreds of megabytes, and it includes every blob that ever got committed — including the ones you deleted three months ago and thought were gone.
Why the mitigations don't work
Here's the part that'll bite people, laid out plainly.
**`--deny "Read(file)"` is a false sense of security.** Settings-file denials and `--deny` flags govern the *agent's read tool* — Channel 1 only. They are access-control on what the model sees in chat. They are not egress control. Nothing about a deny rule touches the bundle upload. If your mental model is "I denied it, so it can't leave," you're wrong, and the canary proves it.
**The opt-out is a training-consent toggle, not a network toggle.** The "improve the model" / "help improve Grok" setting controls whether your data trains the model. In the 0.2.93 test, turning it off did **not** stop the bundle upload. You opted out of training. You did not opt out of the bytes leaving your machine and sitting in xAI's bucket. Those are different consents, and the UI conflates them. If you want that distinction pulled apart carefully, there's a good [skeptic's breakdown of what that opt-out toggle really governs](/posts/grok-s-cli-ships-your-whole-repo-the-opt-out-toggle-governs-something-else/).
**`.gitignore` is the only thing that actually keeps a file out — with a catch.** An untracked, gitignored file isn't in the bundle, because it isn't in git. Good. But the bundle is *git history*. Anything you ever committed is in there regardless of your current `.gitignore`. If you committed a `.env` in 2024, added it to `.gitignore` in 2025, and deleted it — that blob is still reachable in history and still ships in the bundle today.
The `.env` nightmare case
This is the one that actually keeps me up. The researchers seeded a canary env file:
API_KEY=CANARY7F3A9-SECRET-should-not-leave
plus some mock DB creds. Both showed up **verbatim in the request bodies**. Not hashed, not truncated — the literal secret string, sitting in a captured `POST`.
Now think about your own history. How many repos have a secret in an early commit that got rotated but never scrubbed? For most teams the honest answer is "several." Every one of those is in the bundle. Point Grok Build CLI at a repo with a dirty history and you have functionally emailed those secrets to a bucket you don't control, and no deny rule or opt-out you touched will have stopped it.
The two channels, drawn
Deny only blocks the top arrow. The bundle path (green) ignores it.
Reproduce it yourself
Don't take my word or theirs. This is genuinely a five-minute mitmproxy job:
- Start `mitmproxy` and point Grok Build CLI's HTTPS traffic through it (proxy env vars + trust the mitmproxy CA).
- Seed a repo with a canary file and a canary `.env`, and set a deny rule on the canary.
- Run any trivial agent task. Don't let it read the canary.
- Filter the flows for `POST /v1/storage`. Look for the `# v2 git bundle` marker in the body.
- Dump that body, `git clone` the bundle, and grep for your canary strings.
The researchers published a repro harness and a canary repo, so you can bit-for-bit match what I saw. If your capture differs on a newer build, that's worth knowing too — post it.
Being fair about this
This is a wire capture of **v0.2.93**, done by the cereblab researchers, and I reproduced their setup rather than discovering it fresh. xAI has not publicly responded as of this writing, and the behavior could change in a later build — an upload channel is a one-line config away from being gated or dropped. I'm not claiming malice. Uploading a session trace bundle for debugging/replay is a plausible, even boring, engineering decision.
The problem isn't intent. The problem is the **default**, and the fact that the controls a security-conscious dev would reach for — deny rules, the "improve the model" opt-out — don't govern this path. That's the trust failure.
Should you use it? (my verdict)
Not pointed at anything with real secrets. Not on 0.2.93, not by default.
The trust model of "agentic coding tool that also phones home the entire repo and its history on a side channel" is broken until egress is something you can actually control. A deny toggle that doesn't deny the thing you care about is worse than no toggle — it manufactures false confidence. Same for an opt-out that only opts you out of training.
If you want to use it anyway, treat it as if the **whole repo egresses**, because it does:
- Assume every committed byte, ever, leaves the machine.
- Scrub secrets out of history for real — `git-filter-repo` or BFG — not just delete-and-commit. Then rotate them, because they've already shipped if you ran the tool.
- Keep real secrets out of git entirely: `.gitignore` + a never-commit discipline + a secrets manager. Gitignored-and-never-committed is the only state the bundle can't reach.
- Or the simple move: run it on scratch repos and toy projects, and don't aim it at your production monorepo.
I'll re-run the capture when a newer build lands. Until then, on this version, the honest default posture is: the repo left the building.