Inside ZCode: Silently uploading your Git history to the cloud
Recorded: Sept. 18, 2026, 8 p.m.
| Original | Summarized |
Inside ZCode: Silently Uploading Your Entire Git History to the Cloud · Code is cheap, let's talk ↓ Code is cheap, let’s talk Code is cheap, let’s talk Archive Tags EN 简体中文 English Archive Tags EN 简体中文 English Code is cheap, let's talk/ Archive/ Inside ZCode: Silently Uploading Your Entire Git History to the Cloud/ Inside ZCode: Silently Uploading Your Entire Git History to the Cloud Sep 18, 2026·7 mins· · · #Security #Privacy #AI Coding #Reverse Engineering Copy Markdown Table of Contents The Starting Point: A 313MB Archive Stuck in Pending macOS Closing Thoughts Table of Contents The Starting Point: A 313MB Archive Stuck in Pending macOS Closing Thoughts I am not a native English speaker; this article was translated by AI. # ~/.zcode is the data root of ZCode. The size breakdown looked roughly like this: cli/: ~257MB (session databases, execution logs) Inside v2/checkpoints/, I found a 313MB .enc file alongside a state metadata file: The client scanned my active commercial project, excluded node_modules and a few others, and packaged the remaining 345MB into a 313MB encrypted archive labeled baseline (full snapshot); The repository totaled 10GB; minus dependencies, the remaining 345MB was almost entirely core intellectual property. # The logs contained no explicit upload URLs, so I cracked open the client’s app.asar. The reconstructed upload flow: sequenceDiagram The pipeline runs in two stages: Request credentials from coordinator: The client calls https://zcode.z.ai (VITE_ZCODE_ENDPOINT_ORIGIN in code). The server returns OSS form signatures (policy, x-oss-signature), a dynamic Object Key, size limits, and the RSA public key for this encryption round; Inspecting active sockets confirmed this: the running ZCode process maintained persistent HTTPS connections to zcode.z.ai IP endpoints plus two Aliyun OSS storage nodes. # The encryption implementation uses textbook envelope encryption: Content is encrypted using an ephemeral symmetric key via AES-256-CTR; The critical catch is that public key: it is handed down by the server during credential negotiation, and the corresponding private key never touches your machine. Unwrapping the envelope key with all local private keys on my system failed, as expected. # Even though the ciphertext is locked, the Manifest (file inventory) generated during packaging is saved locally in plaintext. Breaking down a snapshot of 42,411 files: Content .git/lfs/ .git/objects/ .git/logs/ Source code & docs The .git directory alone accounts for 86.6% of the payload. Historical API keys and sensitive configs that were deleted in later commits; Furthermore, an extra manifest named repo_snapshot_extra_manifest hashes your global ZCode configuration files (such as settings.behavior.json) and bundles them across workspaces with every snapshot. # The natural reaction is checking settings to toggle it off. I cross-referenced the UI options with the codebase: Switch Optimize Experience (optimizeAgentExperienceEnabled) Repo Snapshot Indexing (repoSnapshotIndexingEnabled) Looking at host assembly code makes it crystal clear: the capture/upload sidecar is instantiated unconditionally at startup. There are no gating if checks on user preferences; the only requirement is that tokenProvider can return a valid JWT. # Checking ZCode’s privacy policy, it explicitly states that it collects “text, files, and code submitted during conversations” — standard practice for feeding context to LLMs. # When I first found the pending package, I simply deleted it. Within half an hour, it re-captured — a fresh 313MB archive with the retry counter ticking from 564 to 565. When the uploader sees the file is gone, it just packs a new one. Manual deletion is whack-a-mole. # # Wipe and lock the checkpoints directory # Verify: should output "Operation not permitted" # # Wipe and lock the checkpoints directory # Verify: should output "Operation not permitted" # Result: The capture logic gets blocked by the kernel whenever it attempts disk I/O. Without local artifacts, the upload pipeline has nothing to send; Closing Thoughts # When using AI tools, model inference inevitably needs code context — everyone accepts that going in. But this behavior clearly crosses the line in two ways: Related 1Password Raised Its Price Again, So I Moved to Self-Hosted Vaultwarden Aug 8, 2026·8 mins #1Password #Vaultwarden #Bitwarden #Self-Hosting #Security ← Notes on a Batch of Demo Logs: Why 'Wrapping Everything in an Agent to Make a SaaS' Is an Absurd Illusion Sep 3, 2026 ↑ © 2026 ferstar · CC BY-NC-SA 4.0 Unless noted otherwise, posts are AI-drafted and approved for publication by ferstar. |
The investigation into ZCode revealed that the application silently packages and uploads the user's entire workspace, including the complete Git history, to cloud storage, which the author details through an examination of local data and client reverse engineering. The initial discovery involved a local archive stuck in a pending state, leading to an analysis of the ~/.zcode directory, which contained substantial encrypted data. This analysis demonstrated that the client bundles significantly more than just the current working tree; the .git directory alone accounted for approximately eighty-six point six percent of the payload size, containing the complete lineage of the repository, including commit history, large file assets from LFS, and internal configuration files. The mechanism of this data transfer was dissected by examining the application's internal structure, revealing a two-stage upload pipeline. The client first communicates with the ZCode backend to obtain necessary credentials, including an ephemeral RSA public key, size limits, and policy signatures. Subsequently, the client performs local archiving, encryption using AES-256-CTR, and wraps the resulting symmetric key using the server-provided RSA public key. The resulting encrypted archive is then posted directly to Aliyun OSS, bypassing the application servers. This process confirmed that the system establishes persistent HTTPS connections to both the backend coordination service and the object storage nodes, indicating an active background pipeline for data exfiltration whenever the user is logged in. A critical aspect of the security posture involves the encryption scheme. The system employs envelope encryption where the symmetric session key is wrapped using RSA-OAEP-SHA256, and the encryption public key is supplied by the server, while the corresponding private key is retained exclusively by the server. This architectural choice means the ciphertext on the user's disk is computationally inaccessible to the user or the client, placing control over the data exclusively with the server. The author argues that a feature designed for user-side rollback or synchronization would logically require local key storage, not server-held encryption keys, suggesting the system’s design serves collection rather than genuine backup functionality. Furthermore, the investigation addressed the discrepancy between user interface controls and actual functionality. Attempts to disable snapshotting or telemetry through settings proved ineffective, as the capture and upload processes are instantiated automatically upon application startup, regardless of user preferences or UI toggles. Capture events occur before every prompt and upon task completion, confirming a continuous data collection mechanism. Although the published privacy policy does not explicitly mention the silent mass uploading of repositories and Git histories, the author suggests this behavior crosses ethical and architectural boundaries. The final defense proposed against this persistent background activity is to prevent the capture process entirely by utilizing operating system kernel features. By applying filesystem immutability flags, such as chflags on macOS or chattr on Linux, to the checkpoints directory, the system is effectively blocked from performing necessary disk input or output operations. This prevents the capture logic from accessing local artifacts, thus stopping the upload pipeline. The author concludes that while context is necessary for AI inference, unchecked exfiltration of entire repository histories represents a design choice that necessitates robust user-imposed containment measures using low-level system controls. |