{"slug": "what-xai-grok-build-cli-actually-sends-to-xai-a-wire-level-analysis-grok-0-2-93", "title": "What xAI Grok Build CLI actually sends to xAI - a wire-level analysis (grok 0.2.93)", "summary": "Independent AI safety researcher @cereblab has published a wire-level analysis showing that xAI's Grok Build CLI (version 0.2.93) transmits the entire repository contents, including files the agent was told not to read, to xAI's Google Cloud Storage bucket 'grok-code-session-traces' via POST /v1/storage. The upload occurs independently of what the agent reads, and disabling 'Improve the model' does not stop it. On a 12 GB repo, 5.10 GiB was uploaded while only 192 KB was sent via the model-turn channel.", "body_md": "Update — 2026-07-14:This is the original wire-level analysis (grok 0.2.93). Since publishing: xAI disabled the upload server-side (`disable_codebase_upload: true`\n\n); added a`/privacy opt-out`\n\n— which I wire-tested and found to be a data-retentionsetting, not a block on what'ssent; and Elon Musk publicly committed to deleting all previously-uploaded data (not yet confirmed complete). Full, maintained story + evidence:·[https://cereblab.com][https://github.com/cereblab/grok-build-exfil-repro]\n\n*By @cereblab — Independent AI Safety Checker. Reproduce it yourself: github.com/cereblab/grok-build-exfil-repro*\n\n*A measured, reproducible teardown. Findings are backed by captured artifacts (endpoint, HTTP method, status code, byte size, host) and repro commands; where an observation was seen live but not retained as a file, §7 says so explicitly. Section 8 is an evidence appendix with SHA-256s and a \"what we did not prove\" list. All captures are of my own traffic on my own machine, using a throwaway repository containing fake \"canary\" secrets — no real credentials were exposed.*\n\nxAI's official **Grok Build** coding CLI (`grok`\n\n), on a normal consumer login, does three things worth documenting precisely:\n\n**It transmits the contents of files it reads — including a** The secret appears in`.env`\n\nsecrets file — to xAI, verbatim and unredacted.*two*channels: the live model turn (`POST /v1/responses`\n\n) and a`session_state`\n\narchive uploaded and**accepted (HTTP 200)** via`POST /v1/storage`\n\n— the endpoint the binary routes to the`grok-code-session-traces`\n\nGCS bucket (see §5).**It uploads the** Grok packages the workspace and uploads it via*whole repository*— every tracked file's content plus git history — independent of what the agent reads.`POST /v1/storage`\n\n. Proven directly: on a real codebase, with the prompt*\"reply OK, do not read any files\"*, Grok uploaded the**entire repo as a git bundle**(`POST /v1/storage → 200`\n\n);`git clone`\n\n-ing the captured bundle recovers a file the agent was**told not to open**—`src/_probe/never_read_canary.txt`\n\n— with its unique marker**verbatim**, plus the full git history (appendix`uploaded_repo.bundle`\n\n). And it scales: on a**12 GB** repo of never-read random files,`/v1/storage`\n\nmoved**5.10 GiB, all HTTP 200**(truncated mid-stream), while the model-turn channel moved just** 192 KB**— a**~27,800× ratio** that pins the upload to the codebase, not to what was read. No storage upload failed; the only non-200s were a**model-usage quota**(402/429) on`/v1/responses`\n\nand one unrelated 404 —*not*a storage size cap.**The storage destination is a Google Cloud Storage bucket,**(not AWS S3) — named verbatim in the binary and in a captured`grok-code-session-traces`\n\n`metadata.json`\n\n(`gs://grok-code-session-traces/…`\n\n). I did not find this mechanism surfaced in the CLI's install/quickstart materials (not an exhaustive docs audit — §7), it is active by default, and**disabling \"Improve the model\" does not turn it off**(`/v1/settings`\n\nstill returned`trace_upload_enabled: true`\n\n; §6).\n\nNone of this proves xAI *trains* on the data — that is a policy question addressed in §6. What is proven is transmission, acceptance, and storage.\n\n```\nInstall:  curl -fsSL https://x.ai/cli/install.sh | bash      # → ~/.grok/bin/grok\nAuth:     first launch opens a browser → login to X / SuperGrok (consumer account, not an API key)\n```\n\nBinary identity (repro: `file $(readlink -f ~/.grok/bin/grok); ~/.grok/bin/grok --version; shasum -a 256 $(readlink -f ~/.grok/bin/grok)`\n\n):\n\n``` php\n~/.grok/bin/grok -> ../downloads/grok-macos-aarch64\nMach-O 64-bit executable arm64\ngrok 0.2.93 (f00f96316d4b)\nSHA-256: 2a97ba675bd992aa9b981e2e83776460d94f469b510c0b8efe28b50d236d767c\n```\n\nThe upload machinery is a first-party Rust crate. `strings`\n\non the binary yields these source paths and constants (repro: `strings <binary> | grep -E 'xai-data-collector|grok-code-session-traces|storage.googleapis'`\n\n):\n\n```\ncrates/codegen/xai-data-collector/src/gcs.rs\ncrates/codegen/xai-data-collector/src/storage_client.rs\ncrates/codegen/xai-data-collector/src/queue.rs\ncrates/codegen/xai-data-collector/src/file_access_tracker.rs\ncrates/codegen/xai-data-collector/src/circuit_breaker_observer.rs\ncrates/codegen/xai-grok-shell/src/upload/{gcs,turn,trace,manifest}.rs\ngrok-code-session-traces\nstorage.googleapis.com\n\"Uploading bytes to GCS via proxy\"\n```\n\nEnvironment: macOS, Apple Silicon, `grok 0.2.93`\n\n, July 2026.\n\n`brew install mitmproxy`\n\n; run once to generate its CA at`~/.mitmproxy/`\n\n.- Trust the CA in the\n**login** keychain (no sudo; Grok does not certificate-pin against it):\n\n```\nsecurity add-trusted-cert -r trustRoot -k ~/Library/Keychains/login.keychain-db \\\n  ~/.mitmproxy/mitmproxy-ca-cert.pem\n```\n\n- Run Grok routed through the proxy (a\n`mitmdump`\n\naddon logs, per request: method, host, path,**response status**, request byte size; and saves request bodies for xAI hosts):\n\n```\nHTTPS_PROXY=http://127.0.0.1:8080 SSL_CERT_FILE=~/.mitmproxy/mitmproxy-ca-cert.pem \\\n  grok -p \"<prompt>\" --cwd <repo>\n```\n\n- For staged-artifact inspection, race-copy\n`~/.grok/upload_queue/*`\n\nduring the run, then`gzip -dc | tar -xO`\n\n.\n\nCanary repo: each file carries a unique marker so anything appearing in captured traffic is unambiguously traceable to a file. Secrets file `secrets.env`\n\n/ `.env`\n\n:\n\n```\nAPI_KEY=CANARY7F3A9-SECRET-should-not-leave\nDB_PASSWORD=CANARY7F3A9-DBPASS\n```\n\n**Claim:** when Grok reads a file, its contents are transmitted to xAI — serialized into the `POST /v1/responses`\n\nmodel-turn body, and packaged into a `session_state`\n\narchive that is uploaded and **accepted (HTTP 200)** via `POST /v1/storage`\n\n— with no redaction of the file's contents. A `.env`\n\nis sent like any other file.\n\n**Wire artifact** — a decrypted 48,070-byte `POST cli-chat-proxy.grok.com/v1/responses`\n\nrequest body (identifiable as a model turn by its embedded `\"messages\":[…]\"model\":\"grok-4.5\"`\n\nJSON). It contains the secrets file **verbatim** (appendix: `secrets_responses_body.bin`\n\n, `secret_verbatim.txt`\n\n):\n\n```\n…API_KEY=CANARY7F3A9-SECRET-should-not-leave\\nDB_PASSWORD=CANARY7F3A9-DBPASS\\n…\"model\":\"grok-4.5\"…\n```\n\n**Repro:** `grep -a \"CANARY7F3A9-DBPASS\" secrets_responses_body.bin`\n\n→ matches. All six file markers (source, logic, README, nested JS, API key, DB password) are recoverable from the decrypted `/v1/responses`\n\nbodies. (This artifact proves the secret was **transmitted** to the `/v1/responses`\n\nendpoint; the raw body file does not carry the response status, so the **acceptance (200)** claim is anchored to the `/v1/storage`\n\nchannel immediately below, which is status-mapped in `wire_12gb.log`\n\n.)\n\n**Second channel — persisted to Google Cloud Storage.** The same content is packaged into a `session_state`\n\narchive uploaded via `POST /v1/storage`\n\n. Proven by decompressing the staged artifact before it drains (appendix: `secrets_session_state.tar.gz`\n\n):\n\n```\ngzip -dc secrets_session_state.tar.gz | tar -xO | grep -ao 'CANARY7F3A9-[A-Z]*'\n→ CANARY7F3A9-SECRET, CANARY7F3A9-DBPASS, + all others\n```\n\nSo the secret is not only processed in-flight; it is written into an archive destined for storage.\n\n**Pre-empting \"you told it to read the secrets.\"** A control run with a file the agent was told **not** to open (`untouched_secret.txt`\n\n) and the prompt *\"Reply exactly OK, do not read any files\"* produced **no** occurrence of that file's marker in any captured body. The leak is therefore scoped to files Grok **does** read — but it reads liberally (any file relevant to the task, including a `.env`\n\n) and applied **no** redaction to that file's contents. The defect is that a secrets file was transmitted unredacted, not the act of reading. **Important scope reconciliation:** this control shows the unread file is absent from the ** /v1/responses** bodies — that is\n\n*Channel A*(files the agent reads). It does\n\n**not** clear the separate whole-repo\n\n**snapshot in §4 (**\n\n`/v1/storage`\n\n*Channel B*), which — by the volume evidence there — does sweep in never-read files; I could not decompress the\n\n`/v1/storage`\n\ncodebase chunk to check this specific file. So \"unread file not uploaded\" is true **only for the model-turn channel**, not for the codebase snapshot. (Two further scope notes: (i) in my runs the\n\n`.env`\n\n/`secrets.env`\n\nwas git-tracked; I did not separately test whether a `.gitignore`\n\nd file is still uploaded, so I make no gitignore claim — the mechanism is read-driven per the `file_access_tracker`\n\ncrate, but that specific case is untested. (ii) The canary values sat in `API_KEY=`\n\n/`DB_PASSWORD=`\n\nkeys inside a `.env`\n\n/`secrets.env`\n\nbut were not real-format high-entropy tokens; I proved *this*, not that no redactor exists for, say, an\n\n`.env`\n\nwas transmitted unredacted`sk-…`\n\n-shaped key.)## 4. Finding 2 — The whole repo is uploaded at multi-GB scale; the only ceiling is a model quota, not storage size\n\n**Claim:** Grok uploads a whole-repo snapshot with no storage size wall in the tested range. As the repo grows it switches upload strategy and keeps returning 200; on a 12 GB repo, 73 chunks of ~75.0 MB (5.10 GiB) uploaded with zero failures before the capture was truncated mid-stream.\n\nWire-captured size sweep (incompressible content so the tar cannot shrink; fresh session each step). **Only the 12 GB row was retained as a file** (`wire_12gb.log`\n\n); the smaller rows were observed live during the sweep but their logs were not saved (see §7):\n\n| Repo size | Upload behavior (observed on the wire) | Status | Artifact |\n|---|---|---|---|\n| 64 MB | single `POST /v1/storage` , `req=50548145b` (48 MB) |\n200 |\nobserved, not retained |\n| ~600 MB | `POST /v1/storage` in ~7.5 MB chunks (dozens) |\nall 200 |\nobserved, not retained |\n| ~3 GB | `POST /v1/storage/multipart/init` → `PUT storage.googleapis.com/grok-code-session-traces/multipart/<id>` in 50 MB parts (direct-GCS PUT lines not preserved — §7) |\nall 200 |\nobserved, not retained |\n| ~12 GB | `POST /v1/storage` in 75 MB chunks (`req≈75014840b` ); 73 chunks (~5.1 GB) captured before I stopped the run |\nall 200, 0 failures |\n`wire_12gb.log` |\n\n**Preserved artifact:** `wire_12gb.log`\n\n(appendix). It contains **83 /v1/storage* 200 responses**:\n\n**82** content-upload\n\n`POST …/v1/storage`\n\nrequests — of which **73 are chunks of ~75.0 MB each**(byte sizes min 75,014,811 / max 75,014,871, totaling\n\n**5,476,083,317 B = 5.10 GiB / 5.48 GB**) plus\n\n**9** smaller POSTs — and\n\n**1**\n\n`/v1/storage/batch_exists`\n\ndedup check. Total `/v1/storage*`\n\nrequest bytes: 5,476,228,005 B. **Zero** storage requests failed. The capture was stopped while uploads were still streaming (the last line is another ~75 MB chunk → 200), so this demonstrates\n\n**≥5.1 GiB uploaded, still climbing when truncated**—\n\n*not*that the full 12 GB completed. Repro (three greps, so no count is ambiguous):\n\n`grep 'cli-chat-proxy.grok.com/v1/storage' wire_12gb.log | grep -c '> 200'`\n\n→ **83**(all\n\n`/v1/storage*`\n\n); `grep 'POST cli-chat-proxy.grok.com/v1/storage ' wire_12gb.log | grep -c '> 200'`\n\n→ **82**(content POSTs only);\n\n`grep req=75014 wire_12gb.log | grep -c '> 200'`\n\n→ **73**(the ~75 MB chunks).\n\n**Scope — this is the whole repo, not just files the agent read.** Channel A (§3,\n\n`/v1/responses`\n\n) carries files the agent opens. This §4 upload is a separate **Channel B**: a snapshot of the entire workspace. Two lines of evidence:\n\n**(a) The decisive byte split (load-bearing).** In the*same*captured 12 GB session — a repo of**100 % random files the agent never read**— the two channels moved wildly different volumes:** Channel A**, across 5 requests, largest single turn 60,394 B.`/v1/responses`\n\n(model turns): 196,705 B = 192 KB total**Channel B**`/v1/storage`\n\n: 5,476,228,005 B = 5.10 GiB.- That is a\n**~27,800× ratio**(5,476,083,317 ÷ 196,705). The model** demonstrably never ingested the files**(192 KB cannot carry 5 GiB of content), yet 5.10 GiB of them left via`/v1/storage`\n\n— and across the sweep the`/v1/storage`\n\nvolume**tracks total repo size**(64 MB → 12 GB). GB-scale bytes leaving a never-read repo can only be a whole-repo snapshot.\n\n**(b) The binary's own paths/strings** corroborate the mechanism:`after_codebase.tar.gz`\n\n,`xai-grok-shell/src/upload/{trace,turn}.rs`\n\n,`repo_state.upload`\n\n,*\"collecting workspace files\"*,*\"spawning background coordinator\"*.**(c) A staged codebase manifest enumerates a never-read file and content-addresses it to the GCS bucket.** In a separate run on a**real 298-file**, I race-copied the snapshot Grok staged for upload (appendix:`cereblab_api`\n\ncodebase`staged_base_tree_manifest.json`\n\n,`staged_metadata.json`\n\n). The manifest lists— a file I planted and never asked Grok to open — alongside the real sources (`src/_probe/never_read_canary.txt`\n\n`src/apns.ts`\n\n,`src/index.ts`\n\n, …); and`metadata.json`\n\ngives each file a destination`fileId`\n\nof the form. So the whole tree, unread files included, is enumerated and content-addressed for upload to the named GCS bucket. (That run was quota-blocked before the content blobs uploaded; the 12 GB run above independently proves`gs://grok-code-session-traces/repo_changes_dedup/v2/supplemental/sha256_…`\n\n*GB-scale content actually leaves the wire, all 200*.)**(d) DEFINITIVE — a never-read file's** With the account upgraded (quota lifted), I re-ran on the real*content*recovered verbatim from an uploaded git bundle (single SuperGrok run).`cereblab_api`\n\nrepo with the explicit prompt`Reply with exactly: OK. Do not read or open any files.`\n\n, having planted a unique marker`CANARY-XR47P2-NEVERREAD-UNIQUE`\n\nin`src/_probe/never_read_canary.txt`\n\n. Grok uploaded the**entire repository as a git bundle** via`POST cli-chat-proxy.grok.com/v1/storage → 200 (req=152102b)`\n\n(appendix`uploaded_repo.bundle`\n\n, SHA-256`73b9c0af06311bae35c3ed03274d0eec2846e76762828d10b09757ca41bd6024`\n\n). Running`git clone uploaded_repo.bundle`\n\nreconstructs the repo, and`src/_probe/never_read_canary.txt`\n\ncontains the marker**verbatim**— a file the agent was explicitly told not to open. The bundle also carries the** full git history**(4 commits, 47 files). This is the airtight per-file-content proof: the whole repo — unread files included — left the machine and was accepted (200). The upload mechanism is a**git bundle**, so \"whole repo\" is literal (every tracked file + history).** Replicated on a second, unrelated codebase:**the identical capture on the`cereblab_auth`\n\nCloudflare-Worker repo produced a git-bundle upload (`POST /v1/storage → 200`\n\n, 31,743 B) from which`git clone`\n\nrecovered its own never-read marker`CANARY-AUTH-4T8K2-NEVERREAD`\n\nverbatim (appendix`uploaded_repo_auth.bundle`\n\n, SHA-256`0ee536538bcd1ee72a258f9977ab69f8a9b1ac240491b91a4e94335b4d83c768`\n\n). Two independent repos, same result.\n\n*(Prompt note: the 12 GB session was interactive and I did not log its verbatim prompt, but the 192 KB Channel-A total is dispositive that no bulk read occurred whatever the prompt was; a separate headless control run used the explicit prompt Reply exactly OK, do not read any files and confirmed an unread file is absent from Channel A.)*\n\n*(The earlier \"one gap\" is now closed by evidence (d): a single SuperGrok run where a specific never-read file's content is recovered from a wire-captured, 200-status git-bundle upload. The 12 GB run remains the proof that this scales to GB volumes.)*\n\n**No storage/upload request failed — every one of the 82 /v1/storage calls returned 200.** The only non-200s in the entire capture were on the model endpoint plus one session-bookkeeping call (full set from\n\n`wire_12gb.log`\n\n; `/v1/responses`\n\nlines also in `model_limit.txt`\n\n):\n\n``` php\nPOST /v1/responses                     -> 402   (Payment Required)   ×1\nPOST /v1/responses                     -> 429   (Too Many Requests)  ×3\nPOST /v1/sessions/<id>/replicas/update -> 404                        ×1   (session bookkeeping, not an upload)\n```\n\nand finally, in plain text, on stdout:\n\n```\nYou've reached your free Grok Build usage limit for now. Get SuperGrok for much higher limits…\n```\n\nThe 402/429 are a **model-usage quota**; the lone 404 is unrelated to storage. Notably, storage uploads continued to return 200 *after* the model turn was rate-limited (76 `/v1/storage`\n\n200s occur at or after the first 429) — the codebase upload is independent of whether the model answers.\n\n**Pre-empting \"you're confusing a local disk cache with an upload.\"** This claim rests strictly on **wire-captured 200-status uploads** of file bytes leaving the machine (`/v1/storage`\n\nrequest bodies of 7.5–75 MB in the preserved `wire_12gb.log`\n\n; the 3 GB 50 MB `PUT`\n\ns to `storage.googleapis.com`\n\nwere also seen on the wire, but that log was not retained — §7). It does **not** rely on the `~/.grok/upload_queue`\n\ndraining — queue-drain is ambiguous (it empties on both success and drop) and is explicitly **not** used as evidence here. (An earlier draft that inferred upload from queue-drain was wrong and has been retracted; see §7.)\n\n**Storage destination is Google Cloud Storage**, bucket. This rests on the`grok-code-session-traces`\n\n**preserved** binary strings`grok-code-session-traces`\n\n,`storage.googleapis.com`\n\n, and*\"Uploading bytes to GCS via proxy\"*(`crate_strings.txt`\n\n),**and on a preserved staged**(`metadata.json`\n\nwhose per-file`fileId`\n\ns are literally`gs://grok-code-session-traces/repo_changes_dedup/v2/…/sha256_…`\n\n`staged_metadata.json`\n\n), corroborated by the direct`storage.googleapis.com`\n\nmultipart PUTs observed at 3 GB (observed live; that log was not retained — see §7). It is**not** AWS S3 (the binary links`aws-sdk-s3`\n\nfor an alternate path and AWS STS/SSO for auth, but the destination named in the binary — and seen on the wire at 3 GB — is GCS).**Third-party telemetry:**`POST api.mixpanel.com/track`\n\nand`/engage`\n\n(Mixpanel), plus`POST grok.com/_data/v1/events`\n\n— all 200.**Not surfaced in setup docs (scope-limited claim):** I did not find the`repo_state`\n\n/`session_state`\n\nupload to`grok-code-session-traces`\n\n, or the`~/.grok/upload_queue`\n\nstaging, described in the CLI's install script or quickstart materials I reviewed (this is not an exhaustive audit of all xAI docs — see §7). The mechanism is active by default on the standard consumer login.**Reliability note (separate from privacy):**`~/.grok/upload_queue`\n\nstages ~3 GB snapshots per turn and, under load, can grow to tens of GB and exhaust the disk. This is a real bug, independent of whether uploads succeed.\n\n**\"Cloud AI tools send context; this is normal.\"** True, and conceded: any cloud coding agent must send code to its server to act on it. The**novel deltas** here are (a) a**secrets** file (e.g.`.env`\n\n) is transmitted**unredacted**, (b) the content is** persisted**to a named GCS bucket, not just processed transiently, and (c) the upload mechanism is** not surfaced in the CLI's setup materials**(§7) and on by default.**\"It's in the ToS / opt-in.\"** xAI's consumer policy broadly discloses data use for model improvement with an opt-out (grok.com → Settings → Data → \"Improve the model\"; Private Chat auto-opts-out; opt-out is prospective, not retroactive). But**broad training disclosure ≠ documenting this specific mechanism.** I did not find the`repo_state`\n\n/`upload_queue`\n\n/`grok-code-session-traces`\n\npipeline described in the CLI materials I reviewed (§7 notes this is not an exhaustive docs audit), so — on those materials — a user is not informed of*it*specifically. Sources: xAI Privacy Policy ([https://x.ai/legal/privacy-policy](https://x.ai/legal/privacy-policy)), Consumer ToS ([https://x.ai/legal/terms-of-service](https://x.ai/legal/terms-of-service)).**The \"Improve the model\" toggle makes no difference — ON or OFF, the whole repo is uploaded the same way.** At default settings, Grok uploaded 5.10 GiB of a never-read repo (§4). Then I**turned \"Improve the model\" off** and re-ran: Grok*still*uploaded the entire repo as a git bundle (§4(d),`POST /v1/storage → 200`\n\n, the never-read file recovered by`git clone`\n\n), and the server's`/v1/settings`\n\nresponse to the CLI*still*returned`\"trace_upload_enabled\": true`\n\n,`\"upload_enabled\": true`\n\n,`\"session_registry_enabled\": true`\n\n(and`\"max_upload_file_bytes\": 1073741824`\n\n, a 1 GiB per-file cap). The opt-out governs**training**, not whether your code is uploaded/stored: the codebase upload to`grok-code-session-traces`\n\ncontinues either way.**Opting out does not stop your repository from leaving the machine.**\n\n**We did not prove xAI trains on this data.** Upload/storage ≠ training. That is governed by policy and account tier; we measured transmission only.**The direct**(the log was overwritten during the sweep, and re-capture is currently blocked by the model quota). The multi-GB claim in §4 therefore rests on the`storage.googleapis.com/grok-code-session-traces`\n\nPUT wire-lines were observed at 3 GB but not preserved in this session**preserved**`wire_12gb.log`\n\n(`/v1/storage`\n\n200s at 75 MB chunks) plus the binary strings naming the bucket; the direct-PUT capture is reproducible once quota resets.**Only the 12 GB sweep log was retained.** The 64 MB / 600 MB / 3 GB rows were observed live but their capture logs were not saved (reproducible once quota resets). The 12 GB capture itself was stopped mid-stream (~5.1 GB / 73 chunks captured), so it proves multi-GB upload*succeeds with no failures*, not that an entire 12 GB repo run completes end-to-end.**The \"not documented\" claim is scope-limited.** I based it on the CLI's install script and quickstart, not an exhaustive search of all xAI documentation, help-center articles, or policies. It is possible the mechanism is described somewhere I did not check; the defensible statement is \"not surfaced in the CLI's own setup materials.\"**Channel B (whole-repo) — now CLOSED (was the one open gap).** Evidence §4(d): on a SuperGrok account, a single run wire-captured Grok uploading the**entire repository as a git bundle** via`POST /v1/storage → 200`\n\n;`git clone`\n\nof the preserved`uploaded_repo.bundle`\n\nrecovers`src/_probe/never_read_canary.txt`\n\n— a file the agent was told not to open — with its unique marker**verbatim**, plus the full git history. So the never-read file's*content*(not just its hash) demonstrably left the machine and was accepted. The`wire_12gb.log`\n\nrun remains the proof that the same mechanism scales to GB volumes.**Universal vs. conditional (partially answered):** multi-GB upload succeeds on free-tier; the git-bundle content upload succeeds on SuperGrok**with \"Improve the model\" turned OFF**(`/v1/settings`\n\nreturned`trace_upload_enabled: true`\n\n). I did**not** find a setting that disables the upload in these tests, but I did not exhaustively enumerate every account/config permutation, so I don't claim it can*never*be gated.**One earlier claim was retracted:** an initial \"the multi-GB blobs fail and are deleted locally, not exfiltrated\" conclusion — based on a**PID-scoped**`nettop`\n\nreading (<1 MB) — was wrong. PID/host-scoped egress misses (a) a separate upload coordinator process and (b) presigned`PUT`\n\ns that go**directly to Google IPs**, never touching the API host. The wire capture (this document) supersedes that inference.\n\nAll artifacts and SHA-256s (`MANIFEST.sha256`\n\n). Binary SHA-256: `2a97ba675bd992aa9b981e2e83776460d94f469b510c0b8efe28b50d236d767c`\n\n.\n\n| Artifact | What it proves |\n|---|---|\n`secrets_responses_body.bin` (48 KB) |\n`.env` contents verbatim in a `POST /v1/responses` body |\n`secret_verbatim.txt` |\nthe two `.env` lines as extracted |\n`secrets_session_state.tar.gz` (16 KB) |\nsame secret inside the archive uploaded via `/v1/storage` |\n`wire_12gb.log` |\n83 `/v1/storage*` → 200 on a 12 GB repo = 82 content POSTs (73 chunks ~75.0 MB = 5.10 GiB + 9 small) + 1 `batch_exists` ; Channel-A `/v1/responses` = 196,705 B (192 KB) total (~27,800× less); 0 storage failures; truncated mid-stream |\n`model_limit.txt` |\nthe `/v1/responses` failures (402×1, 429×3 — model quota) |\n`crate_strings.txt` |\n`xai-data-collector` paths + `grok-code-session-traces` + `storage.googleapis.com` |\n`binary.sha256` |\nbinary provenance |\n`gcs_puts.txt` |\nplaceholder (empty capture) — the 3 GB direct-GCS PUT lines were not retained; note inside explains, re-capture pending quota (§7) |\n`uploaded_repo.bundle` (152 KB, SHA-256 `73b9c0af…` ) |\nthe smoking gun — a git bundle wire-captured leaving via `POST /v1/storage → 200` ; `git clone` recovers `src/_probe/never_read_canary.txt` (never-read) verbatim + full git history (real `cereblab_api` repo, SuperGrok, \"Improve the model\" OFF) |\n`uploaded_repo_auth.bundle` (SHA-256 `0ee53653…` ) |\nreplication on a 2nd unrelated repo (`cereblab_auth` Worker) — git bundle via `POST /v1/storage → 200` ; `git clone` recovers its never-read canary `CANARY-AUTH-4T8K2-NEVERREAD` verbatim |\n`staged_base_tree_manifest.json` |\nreal-code run: the codebase snapshot manifest enumerating (never-read) + 30 real `src/_probe/never_read_canary.txt` `src/*.ts` files |\n`staged_metadata.json` |\nreal-code run: per-file destinations `fileId: gs://grok-code-session-traces/repo_changes_dedup/v2/…/sha256_…` (names the GCS bucket) |\n\n**Repro (condensed):**\n\n```\nbrew install mitmproxy && mitmdump -q -p 8080   # generates ~/.mitmproxy CA\nsecurity add-trusted-cert -r trustRoot -k ~/Library/Keychains/login.keychain-db ~/.mitmproxy/mitmproxy-ca-cert.pem\n# capture a run:\nHTTPS_PROXY=http://127.0.0.1:8080 SSL_CERT_FILE=~/.mitmproxy/mitmproxy-ca-cert.pem grok -p \"read every file\" --cwd <repo>\n# secrets: grep -a CANARY <saved /v1/responses body>\n# staged:  gzip -dc <staged session_state> | tar -xO | grep CANARY\n```\n\n**Integrity:** all captures were of my own traffic on my own machine; the \"secrets\" were fake canary strings; no real credentials were exposed. Findings are version-specific to `grok 0.2.93`\n\n(July 2026); xAI may change behavior at any time.", "url": "https://wpnews.pro/news/what-xai-grok-build-cli-actually-sends-to-xai-a-wire-level-analysis-grok-0-2-93", "canonical_source": "https://gist.github.com/cereblab/dc9a40bc26120f4540e4e09b75ffb547", "published_at": "2026-07-10 02:13:12+00:00", "updated_at": "2026-07-27 16:59:33.030881+00:00", "lang": "en", "topics": ["ai-safety", "ai-ethics", "ai-products", "developer-tools"], "entities": ["xAI", "Grok Build", "cereblab", "Google Cloud Storage", "Elon Musk"], "alternates": {"html": "https://wpnews.pro/news/what-xai-grok-build-cli-actually-sends-to-xai-a-wire-level-analysis-grok-0-2-93", "markdown": "https://wpnews.pro/news/what-xai-grok-build-cli-actually-sends-to-xai-a-wire-level-analysis-grok-0-2-93.md", "text": "https://wpnews.pro/news/what-xai-grok-build-cli-actually-sends-to-xai-a-wire-level-analysis-grok-0-2-93.txt", "jsonld": "https://wpnews.pro/news/what-xai-grok-build-cli-actually-sends-to-xai-a-wire-level-analysis-grok-0-2-93.jsonld"}}