ZCodeの内部:Git履歴をサイレントにクラウドへアップロード
Inside ZCode: Silently uploading your Git history to the cloud (blog.ferstar.org)
要約
ZCode(ZhipuのAIコーディングデスクトップアプリ)が、ユーザーのワークスペース全体(Git履歴、LFSキャッシュなどを含む)を暗号化し、Aliyun OSSにサイレントにアップロードしていることが調査により明らかになりました。暗号化に使用される秘密鍵はサーバー側のみが保持しており、ユーザーは自身のデータを復号できません。UI設定ではこの機能を無効化できず、著者はファイルシステムレベルでの対策を提案しています。
全文翻訳
ZCodeの内部:Git履歴を静かにクラウドへアップロード
私はネイティブの英語話者ではありません。この記事はAIによって翻訳されました。
ディスク容量を確保しようとしていた際の定期的なチェックで、~/.zcode が700MB以上を占めていることに気づいたのが始まりでした。断続的に調査した結果、驚くべき事実を確認しました。
ZCode(Zhipuの公式AIコーディングデスクトップアプリ)は、ログインしている間、ワークスペース全体――完全な.git履歴、LFSアセットキャッシュ、リファログ、グローバルアプリ設定――をサイレントにパッケージ化し、暗号化して、Aliyun OSSに直接アップロードしています。
さらに皮肉なことに、暗号化に使用されるRSA公開鍵はサーバーからオンザフライで配信され、秘密鍵はクラウド上にのみ存在します。 あなたのディスク上にある数百MBの暗号化されたデータは、あなた自身もZCodeクライアントも復号できません。
ここでは、調査の完全な記録、証拠の連鎖、そしてそれを永久にシャットダウンするワンライナーの防御策を示します。
(注:公式の応答とオープンソースのアップデートについては、以下の9月21日および9月23日のセクションを参照してください。初めて読む方は、以下の技術的な詳細に直接進むことができます。)
アップデート、2026-09-23 #
今夜(9月23日)、ZCodeはクライアント v3.14.3 をプッシュし、公開リポジトリもそれに続くコミット (328c1a0 feat: update v3.14.3) でバージョンを合わせました。これにより、公開リポジトリのタグはリリースされたクライアントバージョンと一致しています。
ただし、重要なエンジニアリング上の注意点があります:バージョンタグが一致しても、プロプライエタリなコンパイル済みバイナリと公開ソースコードが1対1で完全に一致することは保証されず、バイト単位で再現可能なビルドなしに絶対的な仮定をすることはできません。以下は、公開リポジトリの差分とローカルクライアントランタイムからの客観的な検証結果です。
- オープンソースリポジトリの差分レビュー:
- 282個の変更されたファイルにわたるコミットを監査しました。以前にキャプチャされた
/api/v1/snapshot/upload-credentialおよび AES-256-CTR パッケージングパイプラインは完全に不在のままです。 - チェックポイントメカニズム (
gitCheckpointService.ts) は、クラウドへの依存なしに、ローカルGit CLIの差分に厳密に依存し続けています。 - 新しいコードは主に、マルチチャネルIMボット(WeCom、Feishu、DingTalk、Telegram経由のリモートコントロール)とワークフロー実行の最適化を導入しています。
- 282個の変更されたファイルにわたるコミットを監査しました。以前にキャプチャされた
- ローカルクライアントランタイム検証:
- ローカルデスクトップアプリケーションは3.14.3に更新されました。パッケージ化されたアセット (
app.asar) を展開すると、repoSnapshotロジックの出現はゼロでした。 - リアルタイムプロセスログと
~/.zcode/v2/checkpoints/ディレクトリは、ワークスペース全体のスキャン、パッケージ化、または新しい.encアーカイブの生成がないことを確認しています。
- ローカルデスクトップアプリケーションは3.14.3に更新されました。パッケージ化されたアセット (
リリースごとにクロスチェックできる公開差分があることは、ブラックボックスのアップデートよりも確かに安心感があります。しかし、プロプライエタリなデスクトップアプリに関しては、コードが何を言っているかと、バイナリがローカルでどのように動作するかは別問題です。以下に示した読み取り専用ディレクトリロックを維持することが、最も信頼できる基本的な防御策となります。
アップデート、2026-09-21 #
本日午前(9月21日)9時23分、ZCodeはXで公式声明を投稿し、彼らの主張するオープンソースコアリポジトリ(GitHub: zai-org/ZCode)を公開しました。ソースコードが公開され、公式に引用されたサードパーティ評価の結果と合わせて、公式の説明を実際のコードベースと1行ずつクロスリファレンスできるようになりました。
1. コードレビュー:わずか2コミットの剥ぎ取り(PRロック、Issue無効化) #
新しく公開されたGitリポジトリを掘り下げると、標準的な企業防衛の予想と一致する状況が見えてきました。
- 履歴Gitコミットの完全な消去:リポジトリには2つのコミットしか含まれていません。空の初期コミットと、6,973ファイルと103万行のコードを一気にダンプした大規模な
feat: open sourceコミットです。オープンソースリポジトリ自体の内部開発コミット履歴は完全にフラット化されており、古いアップロードサイドカーの進化を追跡する方法も、repoSnapshotパイプラインがどのように削除されたかを示すコミット差分もありません。さらに、リポジトリはPRをロックし、Issueを閉じ、厳密には一方的なコードダンプとなっています。 - ライセンス:Apache-2.0。
- アップロードパイプラインの完全なパージ:コードベースで以前にキャプチャされた
/api/v1/snapshot/upload-credential、直接のPostObjectOSSアップロード、AES-256-CTR暗号化、公開鍵配信を検索しましたが、一致は見つかりませんでした。パイプラインは、1行も残さずにきれいに削除されています。 - リポジトリWikiの徹底的な消去:外部のWikipedia参照を除き、Repo Wikiへの言及はすべて削除されました。コードインデックス作成とシンボル検索は、バンドルされた
ripgrep 14.1.1とbfs 4.1.1を使用したローカルプロセスに完全に移行しました。これは、ローカルコードベースの取得がそもそもフルリポジトリのアップロードを必要としなかったことを証明しています。 - 超防御的なNOTICE.md:さらに露骨なのは、ルートディレクトリにある27KBの
NOTICE.mdです。法務部はすべての送信ネットワークリクエストを武装し、空っぽになったコンピュータ使用状況のプレースホルダーにさえ免責事項を追加しましたが、ワークスペース全体のアップロードについては意図的に言及していません。
2. チェックポイントメカニズムの暴露:コードが純粋なローカルGitであることを確認 #
公式声明では当初、「セッションチェックポイントのロールバック」のためにリポジトリスナップショットのアップロードが必要だと主張していました。ソースを見ると、チェックポイントの実際のインプリメンテーション(packages/services/src/git/gitCheckpointService.ts および gitCheckpointRepo.ts)を見つけました。
- 仕組み:ローカルシステム上のGit CLIで純粋に実行され、現在のワークスペースに厳密にスコープされたコミットOIDに基づいて
git diff --name-statusおよびgit diff --numstatを実行します。 - ストレージ:メタデータは
~/.zcode/checkpoints/の下にJSONファイルとしてローカルに永続化されます。 - 結論:オープンソースコードは、チェックポイントがクラウドへの依存なしの厳密なローカルGit差分ユーティリティであり、ワークスペース全体のパッケージ化とは無関係であることを確認しています。公式の「チェックポイントロールバックにはフルリポジトリのアップロードが必要だった」という主張は、コードに対して完全に崩壊します。
3. サードパーティ監査と「バケット削除」 #
公式声明は、中国情報通信研究院(CAICT)とNSFOCUSからの調査結果を要約しました(完全なレポートは近日公開予定とのこと):
- 要約された結果:CAICTは、
zcode-prodOSSバケットにデータがゼロであることを確認し、クライアントv3.14.0はスナップショット生成およびアップロードワークフローを削除したとしました。NSFOCUSは、バケットとすべての内部オブジェクトが削除され、送信経路が検出されなかったことを確認しました。 - 客観的な結論:バケットの完全なパージと削除は、必要な封じ込め措置です。しかし、技術的および時間的な観点から、静的な検証レポートがすべて公開されたとしても、9月20日以降にバケットが空にされたり削除されたことを証明することは、9月18日以前にアップロードされたデータがどうなったかを遡って再構築することはできません。履歴データのライフサイクルは、単一のインシデント後の監査では逆方向に証明できません。
アップデート、2026-09-19 #
この投稿がコミュニティで広く注目された後、Z.aiは9月18日17時44分にユーザーコミュニティで公式声明を発表しました(IT Homeなどのテックメディアも同日夜に報道しました)。このセクションでは客観的なクロスチェックを提供します。元の調査は変更されていません。
会社側の主張 #
Z.aiは9月18日17時44分に声明を投稿しました。IT Homeで報道されています。主なポイントは以下の通りです。
- 問題は「コードベースのインデックス作成」に由来し、ローカルインデックス、セッションチェックポイントの復元、Repo Wikiに使用されていた。
- Wikiページをクラウドで生成する際に、リポジトリデータのアップロードがトリガーされる「可能性」があった。
- Wiki生成後、アップロードされたデータは直ちに破棄され、保存されない。
- この機能は初期リリース期間中はデフォルトで有効になっており、一部のユーザーが影響を受けた。「問題は修正された」。
- ZCodeはまもなくオープンソース化され、サードパーティのレビューが行われる。全ユーザーに週1回の追加クォータリセットが付与される。
アップロードが発生したという事実は、Zhipuによってもはや争われていません。問題は、実際のアップロード範囲、ユーザーによる切り替え、および会社外の誰が「直ちに破棄」を確認できるか、という点に残ります。
リバースエンジニアリングとローカル証拠 vs. 公式声明(旧バージョン3.12.3 vs. 3.14.0 vs. 公式声明) #
クライアントバージョン間の混乱を避けるため、発見された影響を受けた旧バージョン(3.12.3)、リバースエンジニアリングされた3.14.0リリース、および公式声明の直接比較を以下に示します。
| 次元 | 公式声明(9月18日 17:44) | 3.12.3 クライアント監査(影響を受けたバージョン) | 3.14.0 クライアント監査(修正されたバージョン) |
|---|---|---|---|
| 送信されたもの | リポジトリデータ(Wiki用) | ワークスペース全体のスナップショット、.gitの約87%(オブジェクト、LFS、リファログ) | アップロードパイプラインコードは物理的に削除。ローカルチェックポイントのみが残る |
| トリガーメカニズム | Wikiページ生成がアップロードを「トリガーする可能性」があった | ログイン時にアップロードサイドカーが常駐。captureBeforePrompt(全プロンプト前)と repo-wiki-update が無条件でトリガー | アップロードサイドカーは解体され、クラウドパッケージングをトリガーしなくなった |
| 無効化可能か | アップロード停止スイッチに関する言及なし | 「Optimize Experience」と「Repo Snapshot Indexing」を無効にしても、パッケージ化と直接OSSアップロードは試行された | コードパイプラインは物理的に削除された |
| クラウド側処理 | 生成後直ちに破棄されると主張 | 外部からは検証不可能(かつ、主張されている「チェックポイント復元」機能と論理的に矛盾する) | クラウドの upload-credential エンドポイントは無効(404を返す) |
| 保持されたデータ | Wiki後データは保存されないと主張 | 538ファイル公開リポジトリのスナップショットがサーバーに受理された。保持/復号権限は未解決 | 既存のクラウドスナップショットが物理的に消去されたかは外部からは検証できない |
元の投稿で誤解を招きやすかった点:313MBの商用プロジェクトは pending(failureCount: 564)にあり、アップロードに成功しませんでした。別の小さな公開リポジトリワークスペースは成功しました。538ファイル、圧縮・暗号化後約15KB、ステータスはサーバーに受理されました。したがって、「実際にマシンから何かが出たか」という問いに対しては、少なくとも一つは出ました。
他のユーザーもWindowsで同様のディレクトリレイアウトと状態ファイルを再現しており、失敗記録がなく受理されたように見える複数の小さなワークスペースが含まれています:NodeSeek。別のローカルクロスチェック:silencestar。
私は9月18日にプライバシーポリシーをアーカイブしました。ページには最後に更新されたのが2026-06-15と記載されており、ワークスペース全体のスナップショットやクラウド同期については依然として言及されていませんでした。
個人的な補足 #
- 313MBの商用プロジェクトは実際にアップロードされましたか?:完全に明確にするために、その313MBの商用リポジトリスナップショットはサイズ制限を超えたため564回失敗し、ローカルの
pendingに留まりました。決して正常にアップロードされませんでした。自宅ではOpenWrtルーターを使用しており、接続追跡とトラフィックフローの記録を確認したところ、これらの暗号化されたチャンクがローカルネットワークから出なかったことが確認されました。 - 間違いなく「リバースエンジニアリングの達人」ではない — 功績は私の基本スペックMBAに:オンラインで私を「リバースエンジニアリングのエキスパート」と呼ぶ人もいましたが、それは全く必要ありません。すべてのきっかけは、笑えるほど些細なことでした。Appleのストレージは金塊のように高価なので、私の基本スペック256GB MacBook Airは常にディスク容量に飢えています。ディスク容量を解放していたときに、
~/.zcodeが奇妙に700MB以上を消費しているのに気づきました。私のエンジニアリング的な勘が働き、app.asarを展開して何が起こっているのかを確認しました(2TB NVMeを搭載したArch Linuxで実行している別のマシンでは、わずか数百MBに気づきもしなかったでしょう)。さらに、現代のAIの状況を考えると、コーディングエージェントを持つ人は事実上誰でもリバースエンジニアになれるのです。まともなエージェントを棚から取り出し、この投稿とソースファイルを渡せば、それは全体のアーキテクチャを完璧な明瞭さで分解してくれるでしょう。これは特別な魔法ではなく、単なる基本的なエンジニアリングの好奇心とトラブルシューティングでした。 - 苦い皮肉:私は実際にGLM Coding Maxの長期購読者であり、支援者でした。最も面白いのは、9月17日の夜、開発者グループチャットで熱心にZCodeを仲間に勧めていたことです。半日も経たないうちに、このサイレントなワークスペース全体パッケージングルーチンを自分で発見したことで、現実は私の顔に直撃しました。
緩和策はまだ重要か #
はい。3.14.0でコードが削除され、ゲートウェイルートが404を返すようになっても、デスクトップクライアントはホットアップデートを受信できます。以下のファイルシステムロックは、トリップワイヤーとしてアクティブなままです。NodeSeekの投稿にはWindowsのACL相当のものが記載されています。
まだ未解決 #
- 「直ちに破棄」を外部からどう証明するか?既存のクラウドに保存された暗号化スナップショットは物理的に消去されたのか、そして誰が秘密鍵の復号権限を持っているのか?
- 主張されている「チェックポイント復元」は「直ちに破棄」と矛盾する — クラウドには具体的に何が保持されていたのか?
- オープンソースで公開されるのは、発見された履歴アップロードサイドカーなのか、それとも最新のクリーンアップされたコミットだけなのか?(9月21日のソースコードで確認:コミット履歴は完全に2つのコミットにフラット化された。上記の9月21日のアップデートを参照)
(注:オープンソースコードの検証は、上記の9月21日のセクションに追加されました)
開始点:保留中の313MBアーカイブ #
~/.zcode はZCodeのデータルートです。サイズの内訳はおおよそ以下のようでした。
cli/: 約257MB(セッションデータベース、実行ログ)computer-use/: 約130MB(バンドルされたアプリとランタイム依存関係)v2/checkpoints/: 約303MB(主な容疑者)
v2/checkpoints/ の中に、状態メタデータファイルと共に313MBの.encファイルが見つかりました。
{
"workspacePath": "/Users/ferstar/myprojects/<a commercial project>",
"lastCompressedSize": {
"encryptedSizeBytes": 313070842,
"workspaceSizeBytes": 345549173
},
"kind": "baseline",
"failureCount": 564
}
物語は単純でした。
- クライアントはアクティブな商用プロジェクトをスキャンし、
node_modulesやその他いくつかを除外し、残りの345MBを「baseline」(フルスナップショット)とラベル付けされた313MBの暗号化アーカイブにパッケージ化しました。 - 564回のアップロード試行失敗を記録し、ローカルの
pending/ディレクトリに残り、次のリトライを待っていました。
リポジトリは合計10GBでした。依存関係を除くと、残りの345MBはほぼすべてコア知的財産でした。
送信先:ログからasarリバースエンジニアリングまで #
ログには明示的なアップロードURLが含まれていなかったため、クライアントの app.asar を解析しました。再構築されたアップロードフローは以下の通りです。
sequenceDiagram
participant C as ZCode client
participant S as zcode.z.ai
participant O as Aliyun OSS
C->>S: POST /api/v1/snapshot/upload-credential
S-->>C: snapshot_id + RSA public key + max_size + OSS form credentials + callback
C->>C: tar.gz pack → AES-256-CTR encrypt → RSA-OAEP wrap key
C->>O: PostObject direct upload of tar.gz.enc
O->>S: callback confirms receipt
パイプラインは2段階で実行されます。
- コーディネーターから認証情報をリクエスト:クライアントは
https://zcode.z.ai(コード内のVITE_ZCODE_ENDPOINT_ORIGIN)を呼び出します。サーバーはOSSフォーム署名(policy、x-oss-signature)、動的なオブジェクトキー、サイズ制限、およびこの暗号化ラウンド用のRSA公開鍵を返します。 - OSSへの直接フォームPOST:アーカイブとストリーミング暗号化をローカルで行った後、クライアントはZCode自身のアプリケーションサーバーをバイパスし、HTTP POSTフォーム経由でAliyun OSSに
tar.gz.encを直接投稿します。その後、OSSはZhipuのバックエンドにコールバックしてスナップショットを登録します。
アクティブなソケットを検査したところ、これが確認されました。実行中のZCodeプロセスは、zcode.z.ai IPエンドポイントと2つのAliyun OSSストレージノードへの永続的なHTTPS接続を維持していました。
最も皮肉な部分:キーはサーバーに属する #
暗号化の実装は、典型的なエンベロープ暗号化を使用しています。
keyId: String(i.encryption.key_version),
keyWrapAlgorithm: "rsa-oaep-sha256",
publicKeySpkiPem: Ylt(i.encryption.public_key)
- コンテンツは、AES-256-CTRによる一時的な対称鍵を使用して暗号化されます。
- 対称鍵は、サーバーから提供された公開鍵を使用してRSA-OAEP-SHA256でラップされます。
決定的な問題はその公開鍵です:それはサーバーによって認証情報ネゴシエーション中に提供され、対応する秘密鍵はあなたのマシンに触れることはありません。私のシステム上のすべてのローカル秘密鍵でエンベロープキーをアンラップしようとしましたが、予想通り失敗しました。
言い換えれば、あなたのドライブ上の313MBの暗号化されたテキストは、あなたやクライアントが開くことはできません。Zhipuのバックエンドだけがそれを解除する鍵を持っています。
もしこの機能が本当にユーザー側のロールバックやクロスデバイス同期のために構築されていたなら、鍵はローカルに存在するはずです(GitやTime Machineのように)。サーバーだけが使用できる鍵は、ただ一つの目的を果たします:サーバーがいつでもあなたのコードを読めるようにすることです。
パッケージ化されるもの:約90%が.git #
暗号化されたテキストはロックされていますが、パッケージ化中に生成されるマニフェスト(ファイルインベントリ)はプレーンテキストでローカルに保存されます。42,411ファイルの1つのスナップショットの内訳は以下の通りです。
| コンテンツ | サイズ | 割合 | 含まれる情報 |
|---|---|---|---|
.git/lfs/ | 196.1 MB | 56.8% | LFSキャッシュ — ダウンロードされたすべてのバイナリアセットと大きなメディア |
.git/objects/ | 102.2 MB | 29.6% | 完全なコミット履歴オブジェクトストア(コミット、ツリー、ブロブ) |
.git/logs/ | 0.6 MB | 0.2% | リファログ — ローカルブランチ履歴とプッシュされていない操作トレース |
| ソースコード&ドキュメント | 約46.2 MB | 13.4% | src/、設定ファイル、内部ドキュメント |
.git ディレクトリだけでペイロードの86.6%を占めています。
アップロードされると、クラウドは現在の作業ツリーよりもはるかに多くのものを受け取ります。それはリポジトリの最初の日からの完全な系統を受け取ります。
- 後続のコミットで削除された履歴APIキーと機密設定。
- 未プッシュのローカルブランチ名(リリースされていない機能計画を明らかにします)。
.git/configで設定された内部GitLabホスト名とリポジトリパス。
さらに、repo_snapshot_extra_manifest という名前の追加マニフェストは、グローバルZCode設定ファイル(settings.behavior.json など)をハッシュし、各スナップショットと共にワークスペース間でバンドルします。
スイッチの真実:UIトグルでは止められない #
自然な反応は、設定を確認してオフにすることです。UIオプションとコードベースをクロスリファレンスしました。
| スイッチ | 期待される動作 | 実際の動作 |
|---|---|---|
Optimize Experience (optimizeAgentExperienceEnabled) | テレメトリ/データ収集を無効にする | モデルトレーニングのためのデータ使用を許可するかどうかのみを制御します。スナップショットキャプチャとアップロードは引き続き実行されます |
Repo Snapshot Indexing (repoSnapshotIndexingEnabled) | スナップショット機能を無効にする | アップロードされたスナップショットをサーバーがインデックス作成するかどうかのみを制御します。ローカルパッケージ化とアップロードは中断なく続行されます |
ホストアセンブリコードを見ると、キャプチャ/アップロードサイドカーは起動時に無条件でインスタンス化されていることが明らかです。ユーザー設定に対するゲート if チェックはなく、唯一の要件は tokenProvider が有効なJWTを返すことだけです。
結論:ログインしている限り、このバックグラウンドパイプラインは常にアクティブであり、UI設定でオフにすることはできません。
キャプチャトリガーは2つのポイントで発生します:captureBeforePrompt(すべてのプロンプトの前)と、repo-wiki-update タグが付いたタスク完了時です。セッションログでは、1つのアクティブセッションで最大62回のキャプチャイベントが生成されました。
プライバシーポリシーの記載内容 #
ZCodeのプライバシーポリシーを確認すると、「会話中に送信されたテキスト、ファイル、コード」を収集すると明記されています。これはLLMにコンテキストをフィードするための標準的なプラクティスです。
しかし、ポリシー全体、FAQ、変更履歴を通じて、ワークスペース全体と完全なGit履歴をサイレントにパッケージ化してアップロードすることについての言及は一切ありません。
最も近い言及は、一般的なテンプレート文です:「最適化プログラムはデフォルトでオフになっており、同意なしに入力はトレーニングに使用されません」。
防御策:削除はいたちごっこ;ディレクトリをロックする #
最初に保留中のパッケージを見つけたとき、私はそれを削除しました。30分以内に、それは再キャプチャされました。新しい313MBアーカイブが作成され、リトライカウンターが564から565に増加しました。アップローダーがファイルがなくなったことを認識すると、新しいファイルを作成するだけです。手動削除はいたちごっこです。
最もクリーンで効果的な解決策は、ファイルシステムレベルで不変フラグを設定し、カーネルレベルで書き込みアクセスを拒否することです。
macOS #
# チェックポイントディレクトリを削除してロックする
rm -rf ~/.zcode/v2/checkpoints
mkdir -p ~/.zcode/v2/checkpoints
chflags uchg ~/.zcode/v2/checkpoints
# 検証: