AI・機械学習
Claude CodeのMicroVMをリバースエンジニアリング:Anthropicの隠されたAntspaceを解明する
Reverse-Engineering Claude Web's MicroVM: Uncovering Anthropic's Hidden Antspace (aprilnea.me)
要約
この記事では、Claude Codeの実行環境をリバースエンジニアリングした結果、Anthropicが開発した未公開のインフラストラクチャが明らかになった経緯を解説しています。調査により、Claude CodeがAWS LambdaやFargateでも使用されているFirecracker MicroVM上で動作していること、そして独自のアプリケーションホスティングプラットフォーム「Antspace」の存在が判明しました。
全文翻訳
Claude Code Webの内部:未剥離のGoバイナリ、Anthropicの秘密のデプロイメントプラットフォーム、そしてAIネイティブPaaSのアーキテクチャ
出発点
私たちは、RailwayやE2Bに似た位置づけの、デスクトップからプラットフォームまで対応するフルスタックプラットフォームであるArcBoxを構築しています。私たちのコア哲学は、ローカルとクラウドの一貫性です。OrbStackを、ローカルでサンドボックス機能を提供する完全にオープンソースのArcBox Desktopに置き換えます。
最近、Webベースのエントリポイントを持つコーディングエージェントプラットフォームが増加していることに気づきましたが、驚くべきことに、そのほとんどが内部でFirecrackerを採用していました。Claude Codeも例外ではありません。
同じ分野の実践者として、そのランタイム環境に対する好奇心から調査を開始しました。カジュアルなstrace -p 1から始まった調査は、完全にドキュメント化されていないアプリケーションホスティングプラットフォームを含む、未リリースのAnthropicインフラストラクチャを明らかにするフルリバースエンジニアリングセッションへと発展しました。
ここで説明するすべては、Claude Codeセッション内で標準的なLinuxツール(strace、strings、objdump、go tool objdump)を使用して発見されました。エクスプロイト、権限昇格、ネットワーク攻撃は一切行っていません。バイナリは、デバッグシンボルがすべて含まれた状態で、そのままそこに存在していました。
レイヤー1:Firecracker MicroVM
最初の疑問は、この環境は具体的に何なのか?
$ dmesg | grep FIRECK
ACPI: RSDP 0x00000000000E0000 000024 (v02 FIRECK)
ACPI: XSDT ... (v01 FIRECK FCMVXSDT ... FCAT 20240119)
ACPI: FACP ... (v06 FIRECK FCVMFADT ... FCAT 20240119)
ACPI: DSDT ... (v02 FIRECK FCVMDSDT ... FCAT 20240119)
ACPIテーブルは、FirecrackerのソースコードにハードコードされているOEM ID FIRECKとクリエーターID FCATで署名されています。これは、AWS LambdaやFargateを支えるのと同じMicroVMテクノロジーです。
スペック:4 vCPU(Intel Xeon Cascade Lake @ 2.80GHz)、16GB RAM、252GBディスク、Linux 6.18.5。Firecrackerは意図的にゲストからvmx/svmフラグを削除するため、ネストされた仮想化はできません。
プロセスのツリーは驚くほど最小限です:
PID 1: /process_api --firecracker-init --addr 0.0.0.0:2024
└─ PID 517: /usr/local/bin/environment-manager task-run --session cse_...
└─ PID 532: claude (CLI自体)
systemdなし。sshdなし。cronなし。ロギングデーモンなし。
PID 1は、initとWebSocket APIゲートウェイの両方として機能するカスタムバイナリです。
カーネルコマンドラインがそれを裏付けています:rdinit=/process_api init_on_free=1 -- --firecracker-init reboot=k panic=1 nomodule
PID 1に対するstraceは、epollイベントループを実行し、子プロセスを監視するために/proc/*/childrenおよび/proc/*/statusを定期的にチェックしていることを示しています。基本的に、ポート2024(WebSocket API)とポート2025(セカンダリエンドポイント)でリッスンする、最小限のinitスーパーバイザーです。
スナップショットアーキテクチャ
セッションはゼロから起動するのではなく、フリーズされたVMスナップショットから復元されます。dmesgの出力は、テンプレート作成とセッション復元の間に48.5時間のギャップがあることを示しています:
[ 30.731516] Run /process_api as init process ← Template: 2026-03-16 13:53 UTC
~~~ 48.5 HOUR GAP — VM WAS FROZEN AS SNAPSHOT ~~~
[174695.927758] virtio_blk: [vdc] new size: ... ← Restored: 2026-03-18 14:24 UTC
[174695.953952] random: crng reseeded due to virtual machine fork
[174695.980760] tokio-runtime-w: drop_caches: 3
[174695.993628] EXT4-fs (vda): mounted filesystem r/w without journal
復元中、Firecrackerホストはブロックデバイスをホットスワップします:
Device | Template After Restore
-------|-------------------------
vdaplaceholder | 256 GiB ext4 | Session rootfs (Ubuntu 24.04)
vdbplaceholder | 63.7 MB squashfs | /opt/claude-code
vdcplaceholder | 12.1 MB squashfs | /opt/env-runner
initramfsは意図的に最小限に抑えられています:/process_apiのみを含む3.1MBのcpioアーカイブです。実際のUbuntuルートファイルシステムはext4ブロックデバイス(vda)上にあり、復元時に注入されます。ext4はマウントカウント=11を持っており、イメージが11セッションにわたって再利用されていることを示しています。
Snapstart:遅延マウントパターン
テンプレート作成フェーズ:
Firecrackerが起動:カーネル + 3.1MB initramfs
process_apiが最小限のinitを実行:/proc、/sys、/dev、cgroupsをマウント;ネットワーキングを設定(IP=192.0.2.2/24、GW=192.0.2.1、MTU=1400)
ホストにSNAPSTART_READYをシグナル送信
ホストがPUT /snapshot/createを呼び出し → VM全体の状態を保存
セッション復元フェーズ:
ホストがセッション固有のブロックデバイス(vda/vdb/vdc)を準備
ホストが新しいデバイスバックエンドでPUT /snapshot/loadを呼び出し
VMが再開 — カーネルがデバイス変更を検出し、CRNGを再シード
process_apiが復元を検出し、実行:
ページキャッシュをドロップ — 古いテンプレートキャッシュはガベージを返すでしょう
devtmpfsを再マウント — デバイスノードを更新
ext4をマウント → 新しいルートファイルシステムにpivot_root
squashfsオーバーレイ(claude-code、env-runner)をマウント
ウォールクロックを修正 via clock_settime() — さもないとテンプレートのエポックでスタックする
CAP_SYS_RESOURCEをドロップ — セキュリティ強化
接続を受け入れる — WebSocketサーバー準備完了
セキュリティ対策
Measure | Purpose
--------|--------
init_on_free=1 | セッション間で解放されたページをゼロにする
CAP_SYS_RESOURCE drop | init後のPID 1のケーパビリティを制限する
CRNG reseed | スナップショットフォーク全体での暗号化の予測可能性を防ぐ
--block-local-connections | ローカルホストのWebSocketアクセスをブロックする
JWT auth | WebSocket接続の検証
Token scrubbing | セキュリティ強化のため、設定から秘密情報を削除する
process_api:通信プロトコル
PID 1は2つのネットワークインターフェイスを公開しています — プロセス管理用のWebSocket APIと、コンテナ制御用のHTTP APIです。典型的なinitシステムとは異なり、process_apiは完全なリモートプロセススーパーバイザーを実装するRust/tokioバイナリです。
WebSocket API(ポート2024)
接続ハンドシェイク:オプションのJWT → ProcessConnection JSON → プロセス作成または再アタッチ。プロセス作成はCreateProcess構造体を受け入れます:
{
"cmd": "/bin/bash",
"args": ["-l"],
"env": {"KEY": "VALUE"},
"cwd": "/home/user",
"rows": 24,
"cols": 80,
"timeout": 300,
"memory_limit_bytes": 1073741824,
"uid": 1000,
"gid": 1000,
"allow_process_id_reuse": false
}
I/Oは2段階のバイナリプロトコルを使用します:
Stdin: ExpectStdIn (text) → binary frame
Stdout/Stderr: ExpectStdOut/ExpectStdErr (text) → binary frame → StdOutEOF/StdErrEOF
Client | Server
-------|--------
|--- WS Connect ------------------->|
|--- JWT (optional) --------------->|
|--- ProcessConnection JSON ------->|
|<-- ProcessCreated ----------------|
| | |
|<-- ExpectStdOut ------------------|
|<-- [binary: stdout data] ---------|
|--- ExpectStdIn ------------------>|
|--- [binary: stdin data] --------->|
| | |
|--- SendSignal ------------------->| SIGTERM, etc.
|--- Resize ----------------------->| PTY resize
|--- Detach ----------------------->| process keeps running
|--- KeepAlive -------------------->| heartbeat
| | |
|<-- ProcessExited -----------------|
|<-- StdOutEOF --------------------|
プロセスの終了理由には、正常終了、シグナル、プロセスごとのOOM、コンテナレベルのOOM、タイムアウト、サーバーシャットダウンが含まれます。内部的に、process_apiはプロセスごとのcgroup(v1は/sys/fs/cgroup/memory/process_api/、v2は/sys/fs/cgroup/process_api/)を追跡し、孤児プロセス(PID 1への再親化)を処理し、設定可能なOOMポーリングループを実行します。
HTTPコントロールAPI(ポート2025)
6つのエンドポイントがコンテナのライフサイクルを管理します:
Endpoint | Purpose
---------|--------
GET /status | ヘルスチェック
POST /fs_sync | ファイルシステムバッファをフラッシュ
POST /shutdown | ページキャッシュドロップによる正常なシャットダウン
POST /auth_public_key | JWT検証キーを設定
POST /mount_root | ルートファイルシステムをマウント(snapstart復元)
POST /container_name | コンテナ名を設定
/mount_rootエンドポイントは、ネットワーク設定(etc_hosts、resolv_conf)、CA証明書、squashfsマウント、FUSEマウント(VFSキャッシュ設定付き)、およびウォールクロックタイムスタンプを含むMountRootConfigを受け入れます — 空のスナップショットからセッションを初期化するために必要なすべてです。マウント中、ルートはFIFREEZE/FITHAW ioctlを介してフリーズされます。
レイヤー2:未剥離のGoバイナリ
本当の発見は、/usr/local/bin/environment-runner(environment-managerとしてシンボリックリンクされている)でした:
$ file /usr/local/bin/environment-runner
ELF 64-bit LSB executable, x86-64, dynamically linked, Go BuildID=..., with debug_info, not stripped
$ go version -m /usr/local/bin/environment-runner
go1.25.7 path github.com/anthropics/anthropic/api-go/environment-manager mod github.com/anthropics/anthropic/api-go (devel) build -ldflags=-X main.Version=staging-68f0dff496
27MBのGoバイナリ。未剥離。完全なデバッグ情報。完全なシンボルテーブル。github.com/anthropics/ant のAnthropicのプライベートモノレポからビルドされました。