Files
openclaude/docs/free-code-main-local-system-info-removal-report.md
YoVinchen 5af8acb2bb Checkpoint the full local bridge and audit work before telemetry removal
You asked for all local code to be committed before the broader telemetry-removal pass. This commit snapshots the current bridge/session ingress changes together with the local audit documents so the next cleanup can proceed from a stable rollback point.

Constraint: Preserve the exact local worktree state before the telemetry-removal refactor begins
Constraint: Avoid mixing this baseline snapshot with the upcoming telemetry deletions
Rejected: Fold these staged changes into the telemetry-removal commit | Would blur the before/after boundary and make rollback harder
Confidence: medium
Scope-risk: moderate
Reversibility: clean
Directive: Treat this commit as the pre-removal checkpoint when reviewing later telemetry cleanup diffs
Tested: Not run (baseline snapshot commit requested before the next cleanup pass)
Not-tested: Runtime, build, and typecheck for the staged bridge/session changes
2026-04-09 14:09:44 +08:00

424 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# `free-code-main` 本地系统信息外发移除实现报告
- 分析时间: 2026-04-03
- 对照文档: `docs/local-system-info-egress-audit.md`
- 分析对象: `/Users/yovinchen/Downloads/free-code-main`
- 对照基线: `/Users/yovinchen/project/claude`
- 分析方式: 静态代码审计 + 关键链路比对 + 同名文件差异核查
- 说明: 本报告只基于源码静态分析,不包含运行时抓包或服务端验证。
## 结论摘要
结论是: **`free-code-main` 只“部分移除”了审计文档里的本地系统信息外发链路。**
更准确地说,它做的是:
1. **把 telemetry / analytics / OTel 相关外发出口失活了**
- Datadog
- Anthropic 1P event logging
- OTel 事件与 metrics/tracing 初始化
- GrowthBook 远程评估链路也被间接短路
2. **但没有把“所有本地信息外发”都移除**
- 模型请求里的环境/项目上下文注入仍在
- Feedback 上传仍在
- Transcript Share 仍在
- Remote Control / Bridge 上传 `hostname`、目录、分支、git remote URL 的链路仍在
- Trusted Device 注册仍在
- `/insights` 的 ant-only 上传逻辑仍在
3. **移除方式不是“彻底删代码”,而是“保留兼容接口 + 启动链路短路 + sink/no-op stub 化”**
- 这意味着仓库里仍然保留了不少采集/导出代码。
- 但默认运行时,关键出口函数已经被改成空实现,导致这些链路无法真正发出请求。
因此,如果问题是:
> `free-code-main` 是否已经把 `docs/local-system-info-egress-audit.md` 中描述的“本地系统信息外发”整体移除?
答案是:
**没有整体移除,只移除了其中“遥测/观测”这一类外发;产品主链路里的上下文外发和若干用户触发上传链路仍然存在。**
## 对照矩阵
| 审计项 | `free-code-main` 状态 | 结论 |
| --- | --- | --- |
| F1 模型请求 system prompt / user context | 未移除 | 默认仍会把 cwd、git 状态、CLAUDE.md、日期以及 prompts 里的平台/壳层/OS 版本注入到模型请求 |
| F2 Datadog analytics | 已移除 | Datadog 初始化与上报函数被 stub 成 no-op |
| F3 Anthropic 1P event logging | 已移除 | 1P logger 整体改为空实现,启用判断恒为 `false` |
| F4 GrowthBook remote eval | 实际已失活 | 依赖 `is1PEventLoggingEnabled()`,而 1P 已被硬关,默认不会创建 GrowthBook client |
| F5 Feedback | 未移除 | 用户触发后仍会 POST 到 `claude_cli_feedback` |
| F6 Transcript Share | 未移除 | 用户触发后仍会 POST 到 `claude_code_shared_session_transcripts` |
| F7 Remote Control / Bridge | 未移除 | 仍会采集并上送 `hostname`、目录、分支、git remote URL |
| F8 Trusted Device | 未移除 | 仍会注册 `Claude Code on <hostname> · <platform>` |
| F9 OpenTelemetry | 已移除 | telemetry 初始化与 `logOTelEvent()` 都被改成 no-op |
| F10 `/insights` 内部上传 | 未移除 | ant-only S3 上传逻辑仍保留 |
## 关键判断
这次比对里最重要的判断有两个:
1. **`README.md` 里的 “Telemetry removed” 只覆盖了“遥测/观测”语义,不等于“所有本地信息外发已删除”。**
2. **`free-code-main` 的移除策略主要是“切断出口”,而不是“删除所有采集代码”。**
这也是为什么你会看到:
- `src/services/analytics/metadata.ts` 这类环境信息构造代码还在
- `src/utils/api.ts` 里上下文统计代码还在
- `src/services/analytics/firstPartyEventLoggingExporter.ts``src/utils/telemetry/bigqueryExporter.ts` 这类导出器文件也还在
但是:
- 事件 sink
- telemetry bootstrap
- OTel event logging
- Datadog / 1P logger 初始化
都已经被改成空实现或被前置条件短路掉了。
## 已移除部分: 实现方式分析
### 1. Analytics 公共入口被改成 compatibility boundary + no-op
`/Users/yovinchen/Downloads/free-code-main/src/services/analytics/index.ts:4-40` 明确写到:
- “open build intentionally ships without product telemetry”
- 保留模块只是为了不改动现有调用点
- `attachAnalyticsSink()``logEvent()``logEventAsync()` 都是空实现
这意味着:
- 各业务模块里仍然可以继续 `import { logEvent }`
- 但这些调用不会再入队、不会再挂 sink、也不会再向任何后端发送
对照 `/Users/yovinchen/project/claude/src/services/analytics/index.ts`,当前工作区版本还保留:
- 事件队列
- `attachAnalyticsSink()` 的真实绑定
- `logEvent()` / `logEventAsync()` 的真实分发
所以这里是非常明确的“出口 stub 化”。
### 2. Datadog 被直接 stub 掉
`/Users/yovinchen/Downloads/free-code-main/src/services/analytics/datadog.ts:1-12` 中:
- `initializeDatadog()` 直接返回 `false`
- `shutdownDatadog()` 空实现
- `trackDatadogEvent()` 空实现
而对照 `/Users/yovinchen/project/claude/src/services/analytics/datadog.ts:12-140`,基线版本仍然保留:
- Datadog endpoint
- 批量缓冲
- `axios.post(...)`
因此 F2 可以判定为**已移除**。
### 3. 1P event logging 被整体空实现
`/Users/yovinchen/Downloads/free-code-main/src/services/analytics/firstPartyEventLogger.ts:1-48` 中:
- `is1PEventLoggingEnabled()` 恒为 `false`
- `logEventTo1P()` 空实现
- `initialize1PEventLogging()` 空实现
- `reinitialize1PEventLoggingIfConfigChanged()` 空实现
这和基线 `/Users/yovinchen/project/claude/src/services/analytics/firstPartyEventLogger.ts:141-220` 中真实存在的:
- `getEventMetadata(...)`
- `getCoreUserData(true)`
- OTel logger emit
形成了直接对照。
需要注意的是:
- `src/services/analytics/firstPartyEventLoggingExporter.ts` 文件仍然存在
- 里面仍保留 `/api/event_logging/batch` 的完整实现
但由于 logger 初始化入口已经空了,这个 exporter 在默认路径上已经不会被接上。
因此 F3 的移除方式属于:
**保留 exporter 源码,但把“上游 logger/provider 初始化”整体切断。**
### 4. Analytics sink 初始化被清空,启动调用点保留
`/Users/yovinchen/Downloads/free-code-main/src/services/analytics/sink.ts:1-10` 中:
- `initializeAnalyticsGates()` 空实现
- `initializeAnalyticsSink()` 空实现
但启动链路并没有删调用点:
- `/Users/yovinchen/Downloads/free-code-main/src/main.tsx:83-86,416-417` 仍然 import 并调用 `initializeAnalyticsGates()`
- `/Users/yovinchen/Downloads/free-code-main/src/setup.ts:371` 仍然调用 `initSinks()`
这说明作者的思路不是“到处改业务调用点”,而是:
**保留启动顺序与依赖图,统一在 sink 层面把行为变空。**
### 5. OTel 初始化被显式短路
`/Users/yovinchen/Downloads/free-code-main/src/entrypoints/init.ts:207-212` 直接把:
- `initializeTelemetryAfterTrust()`
改成了立即 `return`
同时:
- `/Users/yovinchen/Downloads/free-code-main/src/utils/telemetry/instrumentation.ts:1-24`
- `bootstrapTelemetry()` 空实现
- `isTelemetryEnabled()` 恒为 `false`
- `initializeTelemetry()` 返回 `null`
- `flushTelemetry()` 空实现
- `/Users/yovinchen/Downloads/free-code-main/src/utils/telemetry/events.ts:1-12`
- `logOTelEvent()` 空实现
- 用户 prompt 内容默认只会被 `redactIfDisabled()` 处理成 `<REDACTED>`
而调用点仍保留:
- `/Users/yovinchen/Downloads/free-code-main/src/main.tsx:2595-2597` 仍会调用 `initializeTelemetryAfterTrust()`
- 多个业务模块仍会调用 `logOTelEvent(...)`
所以 F9 的移除方式也是:
**不删调用点,只把 telemetry bootstrap 和 event emit 统一改成 no-op。**
### 6. GrowthBook 不是“彻底删文件”,而是被前置条件短路
`/Users/yovinchen/Downloads/free-code-main/src/services/analytics/growthbook.ts:420-425`:
- `isGrowthBookEnabled()` 直接返回 `is1PEventLoggingEnabled()`
而 1P 在 `firstPartyEventLogger.ts:26-27` 中已经被硬编码为 `false`
继续往下看:
- `growthbook.ts:490-493` 在 client 创建前就会因为 `!isGrowthBookEnabled()` 返回 `null`
- `growthbook.ts:685-691``748-750` 会在取 feature value 时直接返回默认值
这意味着从当前源码推断:
- 默认路径不会创建 GrowthBook client
- 默认路径不会执行 remote eval 网络请求
- 默认路径不会把 `deviceID/sessionId/platform/org/email` 发出去
所以 F4 应该判定为:
**远程评估外发链路实际上已失活。**
这里有一个值得单独记录的点:
- `README.md:58-64` 写的是 “GrowthBook feature flag evaluation still works locally but does not report back”
- 但从当前代码看,更准确的说法应该是:
- **默认的远程评估链路已经被短路**
- 留下的是兼容性结构和本地 override/cache 框架
这条判断是**基于源码的推断**。
### 7. 本地采集代码仍有残留,但最终不会出网
这部分很关键,容易误判。
`free-code-main` 不是把所有采集逻辑都删掉了。典型例子:
- `/Users/yovinchen/Downloads/free-code-main/src/services/analytics/metadata.ts:574-740`
- 仍会构造 `platform``arch``nodeVersion``terminal`、Linux distro、`process.memoryUsage()``process.cpuUsage()`、repo remote hash 等元数据
- `/Users/yovinchen/Downloads/free-code-main/src/utils/api.ts:479-562`
- 仍会收集 `gitStatusSize``claudeMdSize`、项目文件数、MCP tool 数量
- 最后仍调用 `logEvent('tengu_context_size', ...)`
- `/Users/yovinchen/Downloads/free-code-main/src/main.tsx:2521-2522`
- 启动时仍会执行 `logContextMetrics(...)`
但由于 `src/services/analytics/index.ts:28-38``logEvent()` 已经是空实现,这些数据虽然可能仍在本地被计算,但不会从该链路继续发出。
所以更准确的评价是:
**移除的是 egress不是所有 collection 语句。**
## 未移除部分: 逐项核对
### F1. 默认模型请求上下文外发未移除
这部分在 `free-code-main` 里仍然存在,而且关键文件与基线高度一致。
直接证据:
- `/Users/yovinchen/Downloads/free-code-main/src/constants/prompts.ts:606-648`
- `computeEnvInfo()` 仍拼接:
- `Working directory`
- `Is directory a git repo`
- `Platform`
- `Shell`
- `OS Version`
- `/Users/yovinchen/Downloads/free-code-main/src/constants/prompts.ts:651-709`
- `computeSimpleEnvInfo()` 仍拼接:
- `Primary working directory`
- `Platform`
- `Shell`
- `OS Version`
- `/Users/yovinchen/Downloads/free-code-main/src/context.ts:36-109`
- `getGitStatus()` 仍读取:
- 当前分支
- 默认分支
- `git status --short`
- 最近 5 条提交
- `git config user.name`
- `/Users/yovinchen/Downloads/free-code-main/src/context.ts:116-149`
- `getSystemContext()` 仍把 `gitStatus` 放入上下文
- `/Users/yovinchen/Downloads/free-code-main/src/context.ts:155-187`
- `getUserContext()` 仍把 `CLAUDE.md` 内容和日期放入上下文
- `/Users/yovinchen/Downloads/free-code-main/src/utils/api.ts:437-474`
- `appendSystemContext()` / `prependUserContext()` 仍会把这些内容拼进消息
- `/Users/yovinchen/Downloads/free-code-main/src/query.ts:449-451,659-661`
- 查询时仍将这些上下文交给模型调用
- `/Users/yovinchen/Downloads/free-code-main/src/services/api/claude.ts:1822-1832`
- 最终仍通过 `anthropic.beta.messages.create(...)` 发送
补充比对:
- `src/constants/prompts.ts`
- `src/context.ts`
- `src/utils/api.ts`
- `src/query.ts`
与基线仓库对应文件比对时,未看到针对这条链路的“移除性改造”。
因此 F1 在 `free-code-main` 中**没有被移除**。
### F5. Feedback 上传未移除
`/Users/yovinchen/Downloads/free-code-main/src/components/Feedback.tsx:523-550` 仍会在用户触发时:
- 刷新 OAuth
- 取 auth headers
- POST 到 `https://api.anthropic.com/api/claude_cli_feedback`
这个文件与基线对应文件比对无差异。
因此 F5 **未移除**
### F6. Transcript Share 上传未移除
`/Users/yovinchen/Downloads/free-code-main/src/components/FeedbackSurvey/submitTranscriptShare.ts:37-94` 仍会收集:
- `platform`
- `transcript`
- `subagentTranscripts`
- `rawTranscriptJsonl`
并 POST 到:
- `https://api.anthropic.com/api/claude_code_shared_session_transcripts`
这个文件与基线对应文件比对无差异。
因此 F6 **未移除**
### F7. Remote Control / Bridge 未移除
`/Users/yovinchen/Downloads/free-code-main/src/bridge/bridgeMain.ts:2340-2435` 仍会采集:
- `branch`
- `gitRepoUrl`
- `machineName = hostname()`
- `dir`
随后:
- `/Users/yovinchen/Downloads/free-code-main/src/bridge/bridgeApi.ts:142-178`
仍会把这些字段 POST 到:
- `/v1/environments/bridge`
上传体中明确包含:
- `machine_name`
- `directory`
- `branch`
- `git_repo_url`
`src/bridge/bridgeApi.ts` 与基线对应文件比对无差异。
因此 F7 **未移除**
### F8. Trusted Device 未移除
`/Users/yovinchen/Downloads/free-code-main/src/bridge/trustedDevice.ts:142-159` 仍会向:
- `${baseUrl}/api/auth/trusted_devices`
提交:
- `display_name: Claude Code on ${hostname()} · ${process.platform}`
这条链路虽然会受 `isEssentialTrafficOnly()` 影响,但代码并未被删除。
`src/bridge/trustedDevice.ts` 与基线对应文件比对无差异。
因此 F8 **未移除**
### F10. `/insights` ant-only 上传未移除
`/Users/yovinchen/Downloads/free-code-main/src/commands/insights.ts:3075-3098` 仍保留:
- `process.env.USER_TYPE === 'ant'` 分支
- 使用 `ff cp` 上传 HTML report 到 S3
这条链路不是默认外部版路径,但它在源码里仍然存在。
因此 F10 **未移除**
## 与基线仓库的“未改动区域”总结
以下文件经对比未看到差异,说明 `free-code-main` 没有在这些链路上做“移除”改造:
- `src/constants/prompts.ts`
- `src/context.ts`
- `src/utils/api.ts`
- `src/query.ts`
- `src/components/Feedback.tsx`
- `src/components/FeedbackSurvey/submitTranscriptShare.ts`
- `src/bridge/bridgeApi.ts`
- `src/bridge/trustedDevice.ts`
- `src/commands/insights.ts`
这也是为什么报告结论是“部分移除”,而不是“整体移除”。
## 最终结论
如果把 `docs/local-system-info-egress-audit.md` 中的链路拆开看,`free-code-main` 的状态可以总结为:
1. **遥测类默认外发**
- Datadog: 已移除
- 1P event logging: 已移除
- OTel: 已移除
- GrowthBook remote eval: 默认已失活
2. **产品主链路或用户触发上传**
- 模型 system/user context 外发: 未移除
- Feedback: 未移除
- Transcript Share: 未移除
- Remote Control / Bridge: 未移除
- Trusted Device: 未移除
- `/insights` ant-only 上传: 未移除
因此,`free-code-main` 的真实定位更适合表述为:
**它移除了“遥测/观测型外发实现”,但没有移除“产品功能本身依赖的本地信息外发”。**
如果后续目标是做“彻底版本地信息外发移除”,还需要继续处理至少这些区域:
- `src/constants/prompts.ts`
- `src/context.ts`
- `src/utils/api.ts`
- `src/components/Feedback.tsx`
- `src/components/FeedbackSurvey/submitTranscriptShare.ts`
- `src/bridge/*`
- `src/commands/insights.ts`