Skip to content

Grok Build CLI Wire Analysis: What It Really Sends

A wire-level teardown of xAI's Grok Build CLI shows it uploads whole repos and .env files. Here's how to verify it yourself and what to do.

8 min readBeginner

If you’ve been on Hacker News in the last day, you already know the question: does xAI’s official Grok Build CLI really upload my entire repo – including .env secrets – to xAI servers, even after I opt out? A wire-level analysis published July 11, 2026 says yes. And the community reaction was, to put it mildly, loud.

This tutorial isn’t a news recap. It’s a hands-on guide to doing the same kind of Grok Build CLI wire-level analysis yourself – so you never have to trust a screenshot again. You’ll learn what the CLI sends, how to intercept its traffic locally, and what to do if you already ran it against a repo with real secrets.

The scenario: you installed grok, then read the gist

You brew installed the Grok Build CLI, pointed it at a side project, and everything worked. Then someone linked the cereblab gist in your team Slack. Now you want to know two things: is my .env already sitting in a bucket somewhere, and how do I prove – for myself – what any AI CLI is actually shipping off my machine?

The tooling to check is free. The reproduction takes maybe 20 minutes. That’s a better use of an afternoon than rotating 40 secrets you aren’t sure were exposed.

What the wire analysis actually found

The report covered grok 0.2.93 on a standard consumer login. Three findings matter for the tutorial that follows, because they’re reproducible with a packet capture:

  • Two separate channels carry data.POST /v1/responses handles the live model turn. POST /v1/storage is different – a session archive endpoint that routes to a Google Cloud Storage bucket. The bucket name, grok-code-session-traces, is hardcoded in the binary and appears verbatim in metadata.json.
  • Even with the prompt “reply OK, do not read any files”, the CLI packaged the workspace as a git bundle and sent it. A canary file the agent was explicitly told not to open – src/_probe/never_read_canary.txt – was later recovered intact from the captured bundle.
  • Disabling “Improve the model” didn’t turn the upload off. After opting out, /v1/settings still returned trace_upload_enabled: true. The toggle governs training use, not transmission.

One thing the report is careful about, and you should be too: it measured transmission and storage, not training. Whether xAI trains on any of it is a policy question – the wire capture can’t answer it.

There’s a useful way to think about what you’re actually doing with mitmproxy here. You’re not hacking anything. You’re standing at the door of your own house, watching what boxes the delivery driver takes out. The driver might be completely trustworthy. But you have the right to check the manifest – and until now, most people just… didn’t.

Set up mitmproxy to intercept Grok Build CLI traffic

Drop a local proxy between the CLI and the internet. mitmproxy (as of July 2026) is the standard tool for this – free, scriptable, and it has a web UI that shows decrypted HTTPS flows in real time.

# macOS
brew install mitmproxy

# Debian/Ubuntu
sudo apt install mitmproxy

# Anywhere with Python 3.10+
pipx install mitmproxy

# Start the web UI on port 8080
mitmweb --listen-port 8080

Run mitmproxy or mitmweb once to generate a CA certificate at ~/.mitmproxy/mitmproxy-ca-cert.pem. That cert is what makes HTTPS interception work. Without trusting it, the CLI sees a stranger’s certificate and refuses the connection.

The Node.js trust step (this is where people get stuck)

TLS errors – not proxy errors, TLS errors – will be your first wall. Node.js keeps its own CA store and rejects mitmproxy’s self-signed cert even when HTTPS_PROXY is set correctly. One extra variable fixes it:

export HTTPS_PROXY=http://127.0.0.1:8080
export HTTP_PROXY=http://127.0.0.1:8080
export NODE_EXTRA_CA_CERTS=~/.mitmproxy/mitmproxy-ca-cert.pem

# now run grok in the same shell
grok

NODE_EXTRA_CA_CERTS tells Node to accept the mitmproxy CA alongside its built-ins. Open the mitmweb UI and requests appear live as the CLI runs. (This behavior is documented in the Hikari Notebook mitmproxy guide for Node-based AI CLIs.)

Pro tip: before you type anything into grok, put a canary file in the repo. Something like echo "CANARY-$(uuidgen)" > never_read.txt. Then prompt the CLI with “reply OK, do not read any files.” If you later find that UUID inside a /v1/storage flow in mitmweb, you’ve reproduced the core finding on your own machine.

Reading the traffic: what to look for

Filter mitmweb by host api.x.ai. Two request patterns matter.

POST /v1/responses is the normal model turn – it matches the shape of xAI’s public Files API. Check the body for anything from your .env.

The quickstart docs don’t mention POST /v1/storage – but the wire capture does. That ~27,800× ratio showed up in the flow log: on a 12 GB repo, this endpoint moved 5.10 GiB while the model channel moved just 192 KB. A multi-MB response with HTTP 200 isn’t context for reasoning. Save the flow (press w in mitmweb), inspect the payload, and you’ll see why.

Compare with other coding CLIs on the wire

How many other CLIs behave this way? Genuinely unknown. The cereblab analysis covered one tool at one version. Claude Code, Codex CLI, Gemini CLI – each has its own architecture, its own telemetry decisions, and its own answer to the question “what leaves the machine.” Nobody has published a rigorous cross-tool comparison yet.

The same mitmproxy setup works for any Node-based coding CLI. Point the same environment variables at a different tool, run an identical prompt, and diff the flow lists.

Signal to check Why it matters
Endpoints hit besides the model API Reveals telemetry, session archives, or trace uploads not in the docs
Total bytes sent vs. bytes strictly needed for the prompt Big gap = your repo is being packaged, not just referenced
Response to your privacy toggle Compare /settings before and after opting out – does the flag actually change?
Behavior when you say “do not read files” Separates model tool use from CLI-side upload behavior

Honest limitations of doing this yourself

Seeing a payload leave your NIC proves transmission. That’s it. It doesn’t prove the destination trains on the data, indexes it, or keeps it longer than the session. The cereblab gist is explicit on this point – and if you share your own capture results anywhere, you should be too.

Two reliability issues worth knowing before you start:

The ~/.grok/upload_queue directory stages roughly 3 GB snapshots per turn and can grow to tens of gigabytes under load. If you’re running this on a laptop, watch your free space. The disk fills silently and suddenly you’re debugging something that has nothing to do with the privacy question.

Certificate pinning would kill this method entirely. As of grok 0.2.93, the CLI doesn’t appear to pin its API certificate – which is why NODE_EXTRA_CA_CERTS works. A future version that pins would produce hard TLS failures and require a different approach (binary patching, which is its own rabbit hole).

What to do right now if you already ran grok on a real project

  1. Treat any secret in a file the CLI could reach as transmitted. Rotate API keys, database URLs, and OAuth client secrets via each provider’s rotate flow – don’t wait to confirm.
  2. Add a hard rule to your global gitignore: exclude .env*, *.pem, and credentials.json from any repo you open in an AI CLI. This won’t stop /v1/storage from packaging the repo, but it keeps the most sensitive files out of the working tree.
  3. For anything you can’t rotate – a licensed dataset, a private model, customer code under NDA – don’t run a cloud-first coding CLI against it. Use a local model or an on-prem agent instead.
  4. Run the mitmproxy setup above against your next CLI install before pointing it at anything sensitive. Fifteen minutes of setup beats one bad support ticket.

FAQ

Does opting out of “Improve the model” stop the upload?

No. The setting controls training use, not transmission. Per the wire capture, /v1/settings still returned trace_upload_enabled: true after opting out.

Is this unique to Grok Build CLI, or do Claude Code and Codex CLI do the same thing?

Every cloud coding agent sends some context to its server – that’s unavoidable. The specific claim in the gist that stands out is threefold: a secrets file went up unredacted, it was persisted to a named bucket rather than processed transiently, and the mechanism isn’t described in the CLI’s setup materials. Whether competing CLIs behave the same is exactly the kind of question your own mitmproxy capture can answer. Run the identical experiment against each and compare the flow lists side by side. Don’t assume; measure.

Is running mitmproxy against a tool I installed legal?

Intercepting traffic on hardware you own, using an account you own, to audit what a tool does with your data – that’s normal debugging. Do it against someone else’s device or account and the answer changes. If you’re auditing for an employer, get written authorization first.

Next step: before you touch grok again, install mitmproxy, set the three env vars, and run one throwaway session with a canary file in the repo. If the canary shows up in a /v1/storage flow, you’ve verified the claim on your own laptop – and you have a repeatable process for every AI CLI you install after this one.