我在 Claude Code Web 中发现了什么:一个未 strip 的 Go 二进制、Anthropic 秘密的部署平台,以及一个 AI 原生 PaaS 的完整架构
起点
我们正在开发 ArcBox——一个从 Desktop 到 Platform 的完整平台,类似 Railway 和 E2B 的定位。我们的核心理念是本地与云端的一致性:用完全开源的 ArcBox Desktop 替代 OrbStack,在本地就能提供 Sandbox 能力。最近我们注意到,越来越多的 Coding Agent 平台开始推出 Web 端入口,而出奇一致的是——它们底层几乎都选择了 Firecracker。Claude Code Web 也不例外。作为同赛道的从业者,我对它的运行环境产生了强烈好奇,于是开始挖掘。最初只是随手一个 strace -p 1,最终却演变成一场完整的逆向工程,揭开了 Anthropic 尚未公开的基础设施——包括一个在互联网上完全没有任何信息的应用托管平台。
本文所有发现均通过标准 Linux 工具(strace、strings、objdump、go tool objdump)在我自己的 Claude Code Web 会话中获得。没有利用任何漏洞,没有提权,没有跨越任何网络边界。那个二进制就静静躺在那里,未做 strip,带着完整的调试符号。
第一层:这是一个 Firecracker 微虚拟机
第一个问题: Claude Code Web 到底跑在什么环境里?

$ 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 表的 OEM ID 为 FIRECK,创建者 ID 为 FCAT——这些值硬编码在 Firecracker 的源码中。这正是驱动 AWS Lambda 和 Fargate 的微虚拟机技术。


硬件规格:4 个 vCPU(Intel Xeon Cascade Lake @ 2.80GHz)、16GB 内存、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用 strace 追踪 PID 1,可以看到它运行着一个 epoll 事件循环,周期性检查 /proc/*/children 和 /proc/*/status 来监控子进程——一个最小化的 init 监控器。它在端口 2024(WebSocket API)和 2025(辅助端点)上监听。
快照架构
会话并非从零启动——而是从冻结的 VM 快照中恢复。dmesg 输出揭示了模板创建和会话恢复之间 48.5 小时的时间间隔:
[ 30.731516] Run /process_api as init process ← 模板创建: 2026-03-16 13:53 UTC
~~~ 48.5 小时间隔 — VM 被冻结为快照 ~~~
[174695.927758] virtio_blk: [vdc] new size: ... ← 会话恢复: 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 宿主机热替换块设备:
| 设备 | 模板阶段 | 恢复后 | 内容 |
|---|---|---|---|
| vda | 占位符 | 256 GiB ext4 | 会话根文件系统(Ubuntu 24.04) |
| vdb | 占位符 | 63.7 MB squashfs | /opt/claude-code |
| vdc | 占位符 | 12.1 MB squashfs | /opt/env-runner |
initramfs 刻意做到最小化:一个 3.1MB 的 cpio 归档,仅包含 /process_api。真正的 Ubuntu 根文件系统在 ext4 块设备(vda)上,在恢复时注入。ext4 的挂载计数为 11,说明该镜像已在 11 个会话中被复用。
Snapstart:延迟挂载模式
模板创建阶段:
- Firecracker 启动:内核 + 3.1MB initramfs
process_api执行最小初始化:挂载/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)
- 修正时钟 —— 通过
clock_settime()校准,否则时间停留在模板创建时刻 - 丢弃 CAP_SYS_RESOURCE —— 安全加固
- 接受连接 —— WebSocket 服务器就绪
安全措施
| 措施 | 目的 |
|---|---|
init_on_free=1 | 会话间清零已释放的内存页 |
| 丢弃 CAP_SYS_RESOURCE | 限制 PID 1 初始化后的能力 |
| CRNG 重新播种 | 防止快照分叉间的密码学可预测性 |
--block-local-connections | 阻止本地 WebSocket 访问 |
| JWT 认证 | WebSocket 连接验证 |
| Token 擦除 | 使用后从配置中移除密钥 |
process_api:线协议
PID 1 暴露了两个网络接口——用于进程管理的 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 使用两阶段二进制协议:
- Stdin:
ExpectStdIn(文本帧)→ 二进制帧 - Stdout/Stderr:
ExpectStdOut/ExpectStdErr(文本帧)→ 二进制帧 →StdOutEOF/StdErrEOF
Client Server
|--- WS Connect ------------------->|
|--- JWT(可选)------------------->|
|--- ProcessConnection JSON ------->|
|<-- ProcessCreated ----------------|
| |
|<-- ExpectStdOut ------------------|
|<-- [binary: stdout 数据] ---------|
|--- ExpectStdIn ------------------>|
|--- [binary: stdin 数据] --------->|
| |
|--- SendSignal ------------------->| SIGTERM 等
|--- Resize ----------------------->| PTY 调整大小
|--- Detach ----------------------->| 进程继续运行
|--- KeepAlive -------------------->| 心跳
| |
|<-- 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)
六个端点管理容器生命周期:
| 端点 | 用途 |
|---|---|
GET /status | 健康检查 |
POST /fs_sync | 刷新文件系统缓冲区 |
POST /shutdown | 优雅关闭,丢弃页缓存 |
POST /auth_public_key | 设置 JWT 验证密钥 |
POST /mount_root | 挂载根文件系统(snapstart 恢复) |
POST /container_name | 设置容器标识 |
/mount_root 端点接受一个 MountRootConfig,包含网络配置(etc_hosts、resolv_conf)、CA 证书、squashfs 挂载指令、FUSE 挂载配置(含 VFS 缓存参数)以及时钟时间戳——从空白快照完整初始化一个会话所需的一切。挂载过程中通过 FIFREEZE/FITHAW ioctl 冻结根文件系统。
第二层:未 Strip 的 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 二进制。未做 strip,保留完整调试信息,保留完整符号表。 构建自 Anthropic 的私有 monorepo github.com/anthropics/anthropic/api-go/environment-manager/。
通过 go tool objdump 和 strings,我提取出了完整的内部包结构:
internal/
├── api/ # API 客户端(会话路由、任务轮询、重试)
├── auth/ # GitHub App Token 提供者
├── claude/ # Claude Code 安装、升级、执行
├── config/ # 会话模式(new/resume/resume-cached/setup-only)
├── envtype/
│ ├── anthropic/ # Anthropic 托管环境
│ └── byoc/ # 自带云(Bring Your Own Cloud)
├── gitproxy/ # Git 凭证代理服务器
├── input/ # 标准输入解析 + 密钥处理
├── manager/ # 会话管理器、MCP 配置、技能提取
├── mcp/
│ └── servers/
│ ├── codesign/ # 代码签名 MCP 服务器
│ └── supabase/ # Supabase 集成 MCP 服务器
├── orchestrator/ # 轮询循环、Hook、身份发现
├── podmonitor/ # Kubernetes 租约管理
├── process/ # 进程执行 + 脚本运行器
├── sandbox/ # 沙箱运行时配置
├── session/ # 活动记录器
├── sources/ # Git 克隆 + 源码分类
├── tunnel/ # WebSocket 隧道 + 动作处理
│ └── actions/
│ ├── deploy/ # ← 重点在这里
│ ├── snapshot/ # 文件快照
│ └── status/ # 状态上报
└── util/ # Git 工具、锁文件、重试、流式日志从二进制中提取的关键依赖:
| 依赖 | 用途 |
|---|---|
github.com/anthropics/anthropic/api-go | Anthropic 内部 Go SDK |
github.com/gorilla/websocket | WebSocket 隧道 |
github.com/mark3labs/mcp-go v0.37.0 | Model Context Protocol |
github.com/DataDog/datadog-go v5 | 指标上报 |
go.opentelemetry.io/otel v1.39.0 | 分布式追踪 |
google.golang.org/grpc v1.79.0 | gRPC(会话路由) |
github.com/spf13/cobra | CLI 框架 |
第三层:Antspace —— Anthropic 隐藏的 PaaS 平台
在 tunnel/actions/deploy/ 包中,我发现了两个 Deploy Client 的函数符号:

VercelClient —— 意料之中:
CreateDeployment→ POST/v13/deploymentsUploadFile→ PUT/v2/files,带x-vercel-digest头WaitForReady→ 轮询直到readyState == "READY"
然后是 AntspaceClient —— 意料之外:
deploy.(*AntspaceClient).Deploy
deploy.(*AntspaceClient).createDeployment
deploy.(*AntspaceClient).uploadTarball
deploy.(*AntspaceClient).streamStatus从二进制中提取的相关字符串揭示了一套完整的部署协议:

阶段一 —— 创建部署
POST 请求到 antspaceControlPlaneURL
Content-Type: application/json
Authorization: Bearer {antspaceAuthToken}
Body: { 应用名称, 元数据 }阶段二 —— 上传构建产物
POST multipart/form-data
文件: dist.tar.gz(构建后的应用)
大小限制: "project exceeds %dMB limit"阶段三 —— 流式部署状态
响应: application/x-ndjson(流式)
状态流转: packaging → uploading → building → deploying → deployed
错误处理: "Streaming unsupported"(客户端不支持 NDJSON 时)我在整个公开互联网上搜索了 "Antspace"——Anthropic 官网、GitHub、博客、文档、LinkedIn、招聘信息、会议演讲、专利申请。零结果。 这个平台从未在任何公开场合被提及。
命名推测来源:"Ant"(据说是 Anthropic 员工的内部昵称)+ "Space"(托管空间),与 Heroku、Vercel 等平台的命名逻辑一致。
Antspace vs. Vercel:架构差异
| 维度 | Vercel | Antspace |
|---|---|---|
| 文件上传 | 基于 SHA 去重,逐文件上传 | 整个 tar.gz 打包上传 |
| 构建方式 | 远端构建(Vercel 负责) | 本地 npm run build,上传产物 |
| 状态获取 | 轮询 | 流式 NDJSON |
| 认证方式 | Vercel API Token + Team ID | Bearer Token + 动态控制面 URL |
| 公开 API | 有,已文档化 | 无,完全内部 |
Anthropic 没有选择简单包装 Vercel API 了事,而是从零构建了一套完整的部署协议——这表明 Antspace 是一项战略性的平台投资,而非临时集成。
第四层:Baku —— Web 应用构建器
"Baku" 是 claude.ai 上 Web 应用构建器体验的内部代号。当你在网页版 Claude 上要求"帮我做一个 Web 应用"时,后台启动的就是 Baku 环境。
从二进制中嵌入的资源提取到的信息:
项目模板:
- 来源:
/opt/baku-templates/vite-template - 技术栈:Vite + React + TypeScript
- 通过 supervisord 自动管理开发服务器,日志写入
/tmp/vite-dev.log
Supabase 自动配置:
自动提供六个 MCP 工具:
provision_database—— 按需创建 Supabase 项目execute_query—— 执行 SQL 查询apply_migration—— 带自动类型生成的版本化 Schema 变更list_migrations—— 列出已应用的迁移generate_types—— 从数据库 Schema 重新生成 TypeScript 类型deploy_function—— 部署 Supabase Edge Function
环境变量自动写入 .env.local:
SUPABASE_URL, SUPABASE_ANON_KEY,
VITE_SUPABASE_URL, VITE_SUPABASE_ANON_KEY停止 Hook(嵌入的 Shell 脚本):
Baku 环境有一个预停止 Hook,在以下情况下阻止会话结束:
- 存在未提交或未推送的 git 变更
- Vite 开发服务器日志中包含错误
tsc --noEmit报告 TypeScript 类型错误
默认部署目标:Antspace——而不是 Vercel。Vercel 作为替代选项存在,但 Baku 的原生部署路径走的是 Anthropic 自己的平台。
内部组织方式:
- 草稿存储在
.baku/drafts/ - 探索项目存储在
.baku/explorations/ - Git 提交使用
[email protected]作为作者 - 未配置 git remote(仅本地版本控制)
第五层:BYOC —— 自带云环境
envtype/ 包包含两种环境实现:
anthropic—— Anthropic 托管(Firecracker 微虚拟机,即我当前所在的环境)byoc—— 自带云(Bring Your Own Cloud)
BYOC 允许企业客户在自己的基础设施中运行 environment-runner,而会话编排仍由 Anthropic API 控制。关键特征:
- 默认会话模式:
resume-cached(最快重启,复用现有状态) - 自定义认证:
containProvideAuthRoundTripper注入容器级凭证 - 智能 Git 处理:在 fetch 前检查任务分支是否存在于远端
- 子类型:
antspace(Anthropic 内部)和baku(Vite 项目构建器) - Kubernetes 集成:
podmonitor包实现租约管理
BYOC 的 API 接口包含 7 个端点:
| 端点 | 用途 |
|---|---|
/v1/environments/whoami | 身份发现 |
| 任务轮询 + 确认 | 工作队列 |
| 会话上下文 | 配置获取 |
| 代码签名 | 二进制验证 |
| Worker WebSocket | 实时隧道 |
| Supabase 数据库查询代理 | 数据库访问中继 |
战略全景
我们看到的是一个垂直整合的 AI 应用平台:
用户用自然语言描述需求
↓
Claude 在 Baku 环境中生成应用
↓
Supabase 数据库自动配置(MCP 工具)
↓
应用部署到 Antspace(Anthropic 的 PaaS)
↓
应用上线,用户全程未离开 Anthropic 生态这不仅是一个 AI 编程助手,而是一个 AI 原生 PaaS 的完整架构——用户从想法到生产的全部旅程都在 Anthropic 的基础设施中完成。
竞争格局的意义重大。这使 Anthropic 直接面对:
- Vercel / Netlify —— 在托管和部署领域
- Replit / Lovable / Bolt —— 在 AI 应用生成领域
- Supabase / Firebase —— 在托管后端领域(通过深度集成)
但 Anthropic 拥有一个其他竞品都不具备的结构性优势:他们拥有从大语言模型到运行时再到托管平台的完整技术栈。
方法论
所有发现均通过在我自己的 Claude Code Web 会话中运行标准 Linux 工具获得。以下是逐步方法:
第一步——识别 Hypervisor。 dmesg | grep FIRECK——ACPI 表携带的 OEM ID 立即指纹识别出 Firecracker。
第二步——识别 PID 1。 cat /proc/1/cmdline——揭示 /process_api 是自研 init,而非 systemd。--firecracker-init 标志进一步确认。
第三步——提取 Go 构建元数据。 对 environment-runner 二进制执行 go version -m,得到 Go 版本、模块路径(github.com/anthropics/anthropic/api-go)、(devel) monorepo 标记以及完整依赖列表。
第四步——从符号表恢复包结构。 由于二进制未做 strip,objdump -t 给出完全限定的函数名。过滤 environment-manager/internal/ 并提取唯一路径,即可生成完整的架构树。
第五步——定向字符串提取。 对 Go 二进制的朴素 strings | grep 噪声很大(链接器会将所有字符串字面量拼接在一起)。两种更有效的方法:
- 已知模式搜索:扫描特定字节序列(
dist.tar.gz、application/x-ndjson等) - 结构体标签提取:Go 将结构体字段标签作为字符串字面量嵌入,搜索
json:"status"等模式可揭示线协议格式
第六步——符号表驱动的功能映射。 objdump -t binary | grep 'deploy\.' 可得到每个组件的方法清单——Vercel 和 Antspace 客户端的完整方法签名正是通过这种方式发现的。
第七步——运行时观察。 strace -p 1 -e trace=epoll_pwait,read,write -f 确认了 epoll 事件循环、子进程监控和 WebSocket 通信模式。
| 技术 | 揭示的信息 |
|---|---|
dmesg ACPI OEM ID | Hypervisor 身份 |
go version -m | 工具链、依赖、monorepo 结构 |
符号表(objdump -t) | 包布局、类型名、方法签名 |
| 结构体标签字符串 | 线协议 / JSON 格式 |
| 定向字节搜索 | 错误消息、状态字符串、流程逻辑 |
strace PID 1 | 运行时行为、IPC 模式 |
为什么这一切能够实现:(1)二进制未做 strip——这是最关键的因素,strip 后就需要 Ghidra 来反编译。(2)Go 的构建元数据嵌入提供了免费的依赖清单。(3)Go 的字符串拼接模型保留了结构体标签和错误字符串。(4)VM 内的 root 权限允许对 PID 1 执行 strace。
结语
将一个未 strip、带完整调试符号的二进制发布到生产环境……是一个有趣的选择。这使得整个分析变得异常简单——通常需要 Ghidra 和数小时反编译工作的内容,用 go tool objdump 和 grep 就完成了。
Antspace 显然仍处于早期或内部阶段(版本号前缀为 staging-),但其部署协议已经成熟且达到生产级别。Anthropic 是计划将其作为公开产品发布,还是仅作为 Claude 网页端体验的内部基础设施,有待观察。
但有一点很清楚:Anthropic 的野心远不止于做一家大模型和 AI Agent 公司。他们正在构建一个应用可以被"说"出来的世界所需的基础设施——而他们希望拥有这个技术栈的每一层。
所有分析于 2026 年 3 月 18 日在 Claude Code Web 会话中完成,运行环境为 Firecracker MicroVM,内核版本 6.18.5,environment-runner 版本 staging-68f0dff496。
完整的原始分析文件(线协议规范、快照架构细节、反编译代码)已开源在 github.com/AprilNEA/reverse-engineering-claude-code-antspace。