What xAI Grok Build CLI actually sends to xAI – a wire-level analysis (grok 0.2.93)
Raw
grok-build-cli-wire-analysis.md
What xAI’s Grok Build CLI Actually Sends to xAI: A Wire-Level Analysis
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.
0. Summary
xAI’s official
Grok Build
coding CLI (
grok
), on a normal consumer login, does three things worth documenting precisely:
It transmits the contents of files it reads — including a
.env
secrets file — to xAI, verbatim and unredacted.
The secret appears in
two
channels: the live model turn (
POST /v1/responses
) and a
session_state
archive uploaded and
accepted (HTTP 200)
via
POST /v1/storage
— the endpoint the binary routes to the
grok-code-session-traces
GCS bucket (see §5).
It uploads the
whole repository
— every tracked file’s content plus git history — independent of what the agent reads.
Grok packages the workspace and uploads it via
POST /v1/storage
. 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
);
git clone
-ing the captured bundle recovers a file the agent was
told not to open
—
src/_probe/never_read_canary.txt
— with its unique marker
verbatim
, plus the full git history (appendix
uploaded_repo.bundle
). And it scales: on a
12 GB
repo of never-read random files,
/v1/storage
moved
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
and one unrelated 404 —
not
a storage size cap.
The storage destination is a Google Cloud Storage bucket,
grok-code-session-traces
(not AWS S3) — named verbatim in the binary and in a captured
metadata.json
(
gs://grok-code-session-traces/…
). 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
still returned
trace_upload_enabled: true
; §6).
None of this proves xAI
trains
on the data — that is a policy question addressed in §6. What is proven is transmission, acceptance, and storage.
1. Subject under test (provenance)
Install: curl -fsSL https://x.ai/cli/install.sh | bash # → ~/.grok/bin/grok
Auth: first launch opens a browser → login to X / SuperGrok (consumer account, not an API key)
Binary identity (repro:
file $(readlink -f ~/.grok/bin/grok); ~/.grok/bin/grok –version; shasum -a 256 $(readlink -f ~/.grok/bin/grok)
):
~/.grok/bin/grok -> ../downloads/grok-macos-aarch64
Mach-O 64-bit executable arm64
grok 0.2.93 (f00f96316d4b)
SHA-256: 2a97ba675bd992aa9b981e2e83776460d94f469b510c0b8efe28b50d236d767c
The upload machinery is a first-party Rust crate.
strings
on the binary yields these source paths and constants (repro:
strings | grep -E ‘xai-data-collector|grok-code-session-traces|storage.googleapis’
):
crates/codegen/xai-data-collector/src/gcs.rs
crates/codegen/xai-data-collector/src/storage_client.rs
crates/codegen/xai-data-collector/src/queue.rs
crates/codegen/xai-data-collector/src/file_access_tracker.rs
crates/codegen/xai-data-collector/src/circuit_breaker_observer.rs
crates/codegen/xai-grok-shell/src/upload/{gcs,turn,trace,manifest}.rs
grok-code-session-traces
storage.googleapis.com
“Uploading bytes to GCS via proxy”
2. Method (reproducible)
Environment: macOS, Apple Silicon,
grok 0.2.93
, July 2026.
brew install mitmproxy
; run once to generate its CA at
~/.mitmproxy/
.
Trust the CA in the
login
keychain (no sudo; Grok does not certificate-pin against it):
security add-trusted-cert -r trustRoot -k ~/Library/Keychains/login.keychain-db \
~/.mitmproxy/mitmproxy-ca-cert.pem
Run Grok routed through the proxy (a
mitmdump
addon logs, per request: method, host, path,
response status
, request byte size; and saves request bodies for xAI hosts):
HTTPS_PROXY=http://127.0.0.1:8080 SSL_CERT_FILE=~/.mitmproxy/mitmproxy-ca-cert.pem \
grok -p “” –cwd
For staged-artifact inspection, race-copy
~/.grok/upload_queue/*
during the run, then
gzip -dc | tar -xO
.
Canary repo: each file carries a unique marker so anything appearing in captured traffic is unambiguously traceable to a file. Secrets file
secrets.env
/
.env
:
API_KEY=CANARY7F3A9-SECRET-should-not-leave
DB_PASSWORD=CANARY7F3A9-DBPASS
3. Finding 1 — File contents, including a secrets file, are transmitted and accepted (200)
Claim:
when Grok reads a file, its contents are transmitted to xAI — serialized into the
POST /v1/responses
model-turn body, and packaged into a
session_state
archive that is uploaded and
accepted (HTTP 200)
via
POST /v1/storage
— with no redaction of the file’s contents. A
.env
is sent like any other file.
Wire artifact
— a decrypted 48,070-byte
POST cli-chat-proxy.grok.com/v1/responses
request body (identifiable as a model turn by its embedded
“messages”:[…]”model”:”grok-4.5″
JSON). It contains the secrets file
verbatim
(appendix:
secrets_responses_body.bin
,
secret_verbatim.txt
):
…API_KEY=CANARY7F3A9-SECRET-should-not-leave\nDB_PASSWORD=CANARY7F3A9-DBPASS\n…”model”:”grok-4.5″…
Repro:
grep -a “CANARY7F3A9-DBPASS” secrets_responses_body.bin
→ matches. All six file markers (source, logic, README, nested JS, API key, DB password) are recoverable from the decrypted
/v1/responses
bodies. (This artifact proves the secret was
transmitted
to the
/v1/responses
endpoint; the raw body file does not carry the response status, so the
acceptance (200)
claim is anchored to the
/v1/storage
channel immediately below, which is status-mapped in
wire_12gb.log
.)
Second channel — persisted to Google Cloud Storage.
The same content is packaged into a
session_state
archive uploaded via
POST /v1/storage
. Proven by decompressing the staged artifact before it drains (appendix:
secrets_session_state.tar.gz
):
gzip -dc secrets_session_state.tar.gz | tar -xO | grep -ao ‘CANARY7F3A9-[A-Z]*’
→ CANARY7F3A9-SECRET, CANARY7F3A9-DBPASS, + all others
So the secret is not only processed in-flight; it is written into an archive destined for storage.
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
) 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
) 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
Channel A
(files the agent reads). It does
not
clear the separate whole-repo
/v1/storage
snapshot in §4 (
Channel B
), which — by the volume evidence there — does sweep in never-read files; I could not decompress the
/v1/storage
codebase 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
.env
/
secrets.env
was git-tracked; I did not separately test whether a
.gitignore
d file is still uploaded, so I make no gitignore claim — the mechanism is read-driven per the
file_access_tracker
crate, but that specific case is untested. (ii) The canary values sat in
API_KEY=
/
DB_PASSWORD=
keys inside a
.env
/
secrets.env
but were not real-format high-entropy tokens; I proved
this
.env
was transmitted unredacted
, not that no redactor exists for, say, an
sk-…
-shaped key.)
4. Finding 2 — The whole repo is uploaded at multi-GB scale; the only ceiling is a model quota, not storage size
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.
Wire-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
); the smaller rows were observed live during the sweep but their logs were not saved (see §7):
Repo size
Upload behavior (observed on the wire)
Status
Artifact
64 MB
single
POST /v1/storage
,
req=50548145b
(48 MB)
200
observed, not retained
~600 MB
POST /v1/storage
in ~7.5 MB chunks (dozens)
all 200
observed, not retained
~3 GB
POST /v1/storage/multipart/init
→
PUT storage.googleapis.com/grok-code-session-traces/multipart/
in 50 MB parts (direct-GCS PUT lines
not preserved
— §7)
all 200
observed, not retained
~12 GB
POST /v1/storage
in 75 MB chunks (
req≈75014840b
); 73 chunks (~5.1 GB) captured before I stopped the run
all 200, 0 failures
wire_12gb.log
Preserved artifact:
wire_12gb.log
(appendix). It contains
83
/v1/storage*
200 responses
:
82
content-upload
POST …/v1/storage
requests — of which
73 are chunks of ~75.0 MB each
(byte sizes min 75,014,811 / max 75,014,871, totaling
5,476,083,317 B = 5.10 GiB / 5.48 GB
) plus
9
smaller POSTs — and
1
/v1/storage/batch_exists
dedup check. Total
/v1/storage*
request 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
≥5.1 GiB uploaded, still climbing when truncated
—
not
that the full 12 GB completed. Repro (three greps, so no count is ambiguous):
grep ‘cli-chat-proxy.grok.com/v1/storage’ wire_12gb.log | grep -c ‘> 200’
→
83
(all
/v1/storage*
);
grep ‘POST cli-chat-proxy.grok.com/v1/storage ‘ wire_12gb.log | grep -c ‘> 200’
→
82
(content POSTs only);
grep req=75014 wire_12gb.log | grep -c ‘> 200’
→
73
(the ~75 MB chunks).
Scope — this is the
whole repo
, not just files the agent read.
Channel A (§3,
/v1/responses
) carries files the agent opens. This §4 upload is a separate
Channel B
: a snapshot of the entire workspace. Two lines of evidence:
(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
/v1/responses
(model turns): 196,705 B = 192 KB total
, across 5 requests, largest single turn 60,394 B.
Channel B
/v1/storage
: 5,476,228,005 B = 5.10 GiB.
That is a
~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
— and across the sweep the
/v1/storage
volume
tracks total repo size
(64 MB → 12 GB). GB-scale bytes leaving a never-read repo can only be a whole-repo snapshot.
(b) The binary’s own paths/strings
corroborate the mechanism:
after_codebase.tar.gz
,
xai-grok-shell/src/upload/{trace,turn}.rs
,
repo_state.upload
,
“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
cereblab_api
codebase
, I race-copied the snapshot Grok staged for upload (appendix:
staged_base_tree_manifest.json
,
staged_metadata.json
). The manifest lists
src/_probe/never_read_canary.txt
— a file I planted and never asked Grok to open — alongside the real sources (
src/apns.ts
,
src/index.ts
, …); and
metadata.json
gives each file a destination
fileId
of the form
gs://grok-code-session-traces/repo_changes_dedup/v2/supplemental/sha256_…
. 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
GB-scale content actually leaves the wire, all 200
.)
(d) DEFINITIVE — a never-read file’s
content
recovered verbatim from an uploaded git bundle (single SuperGrok run).
With the account upgraded (quota lifted), I re-ran on the real
cereblab_api
repo with the explicit prompt
Reply with exactly: OK. Do not read or open any files.
, having planted a unique marker
CANARY-XR47P2-NEVERREAD-UNIQUE
in
src/_probe/never_read_canary.txt
. Grok uploaded the
entire repository as a git bundle
via
POST cli-chat-proxy.grok.com/v1/storage → 200 (req=152102b)
(appendix
uploaded_repo.bundle
, SHA-256
73b9c0af06311bae35c3ed03274d0eec2846e76762828d10b09757ca41bd6024
). Running
git clone uploaded_repo.bundle
reconstructs the repo, and
src/_probe/never_read_canary.txt
contains 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
Cloudflare-Worker repo produced a git-bundle upload (
POST /v1/storage → 200
, 31,743 B) from which
git clone
recovered its own never-read marker
CANARY-AUTH-4T8K2-NEVERREAD
verbatim (appendix
uploaded_repo_auth.bundle
, SHA-256
0ee536538bcd1ee72a258f9977ab69f8a9b1ac240491b91a4e94335b4d83c768
). Two independent repos, same result.
(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.)
(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.)
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
wire_12gb.log
;
/v1/responses
lines also in
model_limit.txt
):
POST /v1/responses -> 402 (Payment Required) ×1
POST /v1/responses -> 429 (Too Many Requests) ×3
POST /v1/sessions//replicas/update -> 404 ×1 (session bookkeeping, not an upload)
and finally, in plain text, on stdout:
You’ve reached your free Grok Build usage limit for now. Get SuperGrok for much higher limits…
The 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
200s occur at or after the first 429) — the codebase upload is independent of whether the model answers.
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
request bodies of 7.5–75 MB in the preserved
wire_12gb.log
; the 3 GB 50 MB
PUT
s to
storage.googleapis.com
were also seen on the wire, but that log was not retained — §7). It does
not
rely on the
~/.grok/upload_queue
draining — 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.)
5. Finding 3 — Destination, telemetry, and what’s not surfaced in the docs
Storage destination is Google Cloud Storage
, bucket
grok-code-session-traces
. This rests on the
preserved
binary strings
grok-code-session-traces
,
storage.googleapis.com
, and
“Uploading bytes to GCS via proxy”
(
crate_strings.txt
),
and on a preserved staged
metadata.json
whose per-file
fileId
s are literally
gs://grok-code-session-traces/repo_changes_dedup/v2/…/sha256_…
(
staged_metadata.json
), corroborated by the direct
storage.googleapis.com
multipart 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
for 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
and
/engage
(Mixpanel), plus
POST grok.com/_data/v1/events
— all 200.
Not surfaced in setup docs (scope-limited claim):
I did not find the
repo_state
/
session_state
upload to
grok-code-session-traces
, or the
~/.grok/upload_queue
staging, 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
stages ~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.
6. Consent and policy — stated honestly
“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
) 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
/
upload_queue
/
grok-code-session-traces
pipeline 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
), Consumer ToS (
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
, the never-read file recovered by
git clone
), and the server’s
/v1/settings
response to the CLI
still
returned
“trace_upload_enabled”: true
,
“upload_enabled”: true
,
“session_registry_enabled”: true
(and
“max_upload_file_bytes”: 1073741824
, 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
continues either way.
Opting out does not stop your repository from leaving the machine.
7. What we did NOT prove (intellectual honesty)
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
storage.googleapis.com/grok-code-session-traces
PUT wire-lines were observed at 3 GB but not preserved in this session
(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
preserved
wire_12gb.log
(
/v1/storage
200s 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
;
git clone
of the preserved
uploaded_repo.bundle
recovers
src/_probe/never_read_canary.txt
— 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
run 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
returned
trace_upload_enabled: true
). 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
reading (<1 MB) — was wrong. PID/host-scoped egress misses (a) a separate upload coordinator process and (b) presigned
PUT
s that go
directly to Google IPs
, never touching the API host. The wire capture (this document) supersedes that inference.
8. Evidence appendix
All artifacts and SHA-256s (
MANIFEST.sha256
). Binary SHA-256:
2a97ba675bd992aa9b981e2e83776460d94f469b510c0b8efe28b50d236d767c
.
Artifact
What it proves
secrets_responses_body.bin
(48 KB)
.env
contents verbatim in a
POST /v1/responses
body
secret_verbatim.txt
the two
.env
lines as extracted
secrets_session_state.tar.gz
(16 KB)
same secret inside the archive uploaded via
/v1/storage
wire_12gb.log
83
/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
model_limit.txt
the
/v1/responses
failures (402×1, 429×3 — model quota)
crate_strings.txt
xai-data-collector
paths +
grok-code-session-traces
+
storage.googleapis.com
binary.sha256
binary provenance
gcs_puts.txt
placeholder (empty capture)
— the 3 GB direct-GCS PUT lines were not retained; note inside explains, re-capture pending quota (§7)
uploaded_repo.bundle
(152 KB, SHA-256
73b9c0af…
)
the 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)
uploaded_repo_auth.bundle
(SHA-256
0ee53653…
)
replication
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
staged_base_tree_manifest.json
real-code run: the codebase snapshot manifest
enumerating
src/_probe/never_read_canary.txt
(never-read) + 30 real
src/*.ts
files
staged_metadata.json
real-code run: per-file destinations
fileId: gs://grok-code-session-traces/repo_changes_dedup/v2/…/sha256_…
(names the GCS bucket)
Repro (condensed):
brew install mitmproxy && mitmdump -q -p 8080 # generates ~/.mitmproxy CA
security add-trusted-cert -r trustRoot -k ~/Library/Keychains/login.keychain-db ~/.mitmproxy/mitmproxy-ca-cert.pem
# capture a run:
HTTPS_PROXY=http://127.0.0.1:8080 SSL_CERT_FILE=~/.mitmproxy/mitmproxy-ca-cert.pem grok -p "read every file" –cwd
# secrets: grep -a CANARY
# staged: gzip -dc | tar -xO | grep CANARY
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
(July 2026); xAI may change behavior at any time.
Sign up for free
to join this conversation on GitHub
.
Already have an account?
Sign in to comment