- 即时通讯
- 后端
【免费下载链接】WuKongIM
More than just IM 不只是即时通讯(IM)
导读
本文围绕 WuKongIM 仓库中的架构决策记录 ADR-0037:Run Codex locally with an encrypted Analysis Session 展开,完整讲解其核心设计:云仿真(Cloud Simulation)的分析环节全部在操作者本地机器完成——本地 Codex CLI 通过 ChatGPT 订阅登录,云端只充当"身份中介"(cloud-identity broker),GitHub 与云厂商不保存、不执行任何 OpenAI API Key、Codexauth.json或 ChatGPT 会话。读完后你将掌握:Analysis Token 的签发与 RSA-OAEP 加密交接协议、本地 Codex 最小权限沙箱的构建方式、analyze.sh/finalize.sh的完整分析流程、结构化 Diagnosis Result 的语义,以及可选的独立修复工作流(Draft PR)如何与云资源清理解耦。
为什么要把 Codex 从云端拉回本地
在 ADR-0037 之前,仓库先后采纳过 0003、0005、0018、0019、0023、0027、0029、0030 等多条 ADR,它们共同构成了"云上分析、云上诊断、GitHub 持有 API 凭据"的早期方案。ADR-0037 一次性取代了这些决策,确立了新的边界:
- GitHub 不持有OpenAI API Key、Codex
auth.json或 ChatGPT 会话; - 云厂商不执行分析过程,只承载被分析的工作负载本身;
- 本地机器负责推理、诊断与(可选的)代码修复;
- 云端只做一件事:证明身份与存活证据,并把一次性的 Analysis Token 加密交给请求方。
这套设计的直接收益是:无论代码库还是云端流水线泄露,攻击者都无法拿到可复用的 AI 凭据;AI 分析消耗的是操作者自己的 ChatGPT 订阅额度,不依赖任何专用 API 凭据的托管与轮换。
Analysis Session 工作流:云端只当"身份中介"
ADR-0037 把整个分析会话定义为一条严格的六步流水线:
- Run Locator 校验:云端基于 provider inventory(云厂商资源清单)证明操作者请求的那个精确 Run(保留的 Run Locator)真实存在;
- GitHub OIDC 交换:用 GitHub Actions OIDC 换取一个短期、run 作用域的 Analysis Token;
- RSA-OAEP 加密交接:云端把该 Token 用操作者本地生成的临时公钥按RSA-OAEP SHA-256加密后交付;
- 准入窗口:云端只在 Token 过期前,向请求方客户端的公开
/32地址开放 MCP 入口; - 本地持有私钥:私钥始终留在本地进程内,Token 密文只有本地能解开;
- 精确校验:本地钉住 run 证书、检出实际部署的源 SHA,通过白名单只读 MCP 调用该版本的 Analysis Skill。
加密交接的源码落点
Token 交换协议的 HTTP 适配器位于 internal/access/cloudanalysismcp/token.go:NewTokenExchangeHandler只接受POST,先经Verify(GitHub OIDC 校验)再调Issue,返回access_token、token_type与expires_at。它的测试 token_test.go 验证了方法限制、缺失/非法身份返回 401、会话不可用返回 409 的 fail-closed 语义。
会话存储与签发在 cmd/wkanalysis/session.go:
- Token 为 32 字节加密随机数,Base64 URL 编码;
- 默认有效期45 分钟,且被"Run Lease 到期前 5 分钟"这一上限钳制;
- 若 Run Lease 剩余不足 30 分钟则拒绝签发(
errInvalidAnalysisSession); - 服务端只保存 token 的 SHA-256 摘要,签发时顺手清理已过期条目;
- Token不可续期(non-renewable),
Verify只接受仍存活、长度 ≥32 的摘要。
TestAnalysisSessionStoreIssuesNonRenewableLeaseBoundToken(session_test.go)直接验证了"租约 40 分钟 → Token 35 分钟"的钳制行为与过期即失效。
OIDC 身份校验的严格性
OIDC 校验器在 cmd/wkanalysis/oidc.go 中实现了精确声明匹配:除了 RS256 签名、Issuerhttps://token.actions.githubusercontent.com、Audience 与过期时间外,还逐一比对repository、ref、job_workflow_ref、environment以及两种 subject 之一。配置端(oidc.go)只接受两类被评审过的会话方言:
- 云仿真:Audience
wukongim-cloud-sim:<run_id>,工作流cloud-sim-analyze.yml@refs/heads/<ref>,环境cloud-sim-analysis; - Chat Lifecycle 云租约:Audience
wukongim-cloud-lease:<run_id>,工作流cloud-lease-analyze.yml@refs/heads/<ref>,环境cloud-lease-provision。
测试 oidc_test.go 专门验证了"交叉环境""未评审工作流"等组合都会被拒绝,JWKS 公钥加载则遵循 fail-closed 且带 10 分钟缓存(oidc.go)。
网关进程:wkanalysis
Analysis 网关由 cmd/wkanalysis/main.go 组装:启用 OIDC 时创建analysisSessionStore并把sessions.Verify挂到网关的 MCP Token 校验器上,同时暴露POST /analysis/token、GET /healthz与GET /self-check(后者会主动探测 Prometheus、三个节点与 Manager 的就绪状态)。其运行期配置全部来自环境变量,核心参数如下:
| 环境变量 | 默认值 | 说明 |
|---|---|---|
WK_ANALYSIS_RUN_ID | 必填 | Run 身份,缺失即启动失败 |
WK_ANALYSIS_MCP_TOKEN/WK_ANALYSIS_GITHUB_OIDC_* | 二选一 | 32 字节 MCP Token,或 GitHub OIDC 配置组 |
WK_ANALYSIS_SCENARIO_PATH | 必填 | 生效场景文件,要求wkbench/v1且随机种子非零 |
WK_ANALYSIS_SCENARIO_DIGEST | 可选 | 场景摘要校验,不匹配即拒绝启动 |
WK_ANALYSIS_RUN_LOCATOR_PATH | 可选 | Run Locator,须与WK_ANALYSIS_FAKE_INVENTORY_PATH成对出现 |
WK_ANALYSIS_RUN_EXPIRES_AT | 当前时间 + 2h | Run Lease 到期,须距当前 ≥30 分钟 |
WK_ANALYSIS_TOKEN_EXPIRES_AT | 45 分钟(受租约钳制) | 仅非 OIDC 模式可用,不得超过租约前 5 分钟 |
WK_ANALYSIS_LISTEN_ADDR | 0.0.0.0:19092 | 监听地址(MCP 端口 19092;Chat Lease 为 19444) |
WK_ANALYSIS_TLS_CERT_FILE/WK_ANALYSIS_TLS_KEY_FILE | 空 | 云 OIDC 模式强制要求 TLS 证书密钥 |
WK_ANALYSIS_NODE_API_URLS | 三个默认节点 | JSON 对象,必须同时包含节点 1、2、3 |
WK_ANALYSIS_PROVIDER/WK_ANALYSIS_REGION/WK_ANALYSIS_SOURCE_SHA | local-compose/local/ 空 | 若提供 Run Locator,须与 Locator 完全一致 |
WK_ANALYSIS_MANAGER_BASE_URL | http://wk-node1:5301 | Manager 地址 |
WK_ANALYSIS_PROMETHEUS_BASE_URL | http://prometheus:9090 | Prometheus 地址 |
配置解析的边界校验(如节点 URL 个数、过期时间先后、租约与 Locator 一致性)集中在 cmd/wkanalysis/config.go,并有 runtime_contract_test.go 逐项覆盖"缺场景、摘要不匹配、租约过短、节点 URL 非法"等拒绝路径。
本地编排入口:analyze.sh 与 finalize.sh
本地侧的核心编排脚本都位于 scripts/cloud-sim/,它们共享 local-runtime.sh 提供的有界进程组执行器wk_run_bounded:每条本地命令都落在独立进程组、带墙钟截止时间,超时统一返回 124,并在退出前强杀整个孤儿组——这是本地编排的安全地基。
analyze.sh:一次精确 Run 的完整分析
analyze.sh 的用法:
./scripts/cloud-sim/analyze.sh RUN_ID [options] # --repository OWNER/REPO 指定 GitHub 仓库(默认从当前 checkout 探测) # --chat-request-id REQUEST_ID 分析精确的 chat-lifecycle Cloud Lease 交接 # --diagnostic-focus TEXT 可选的受限诊断关注点(仅作为主题数据,不是指令) # --allow-fix-pr 兼容标志:修复延后到 provider 资源释放之后 # --result-file ABSOLUTE_PATH 原子写出结构化分析结果一次本地前提:gh auth login+codex login(选择ChatGPT),无需任何 OpenAI API Key。Codex 版本要求 ≥ 0.140.0,脚本会从 PATH 与 ChatGPT App 内置路径中挑选最高兼容版本(WK_CODEX_BIN可显式覆盖),并校验登录态必须是Logged in using ChatGPT(analyze.sh 与 #L726-L750)。
主流程按顺序执行以下阶段:
- Inspect(preflight):通过 GitHub Actions 派发
cloud-sim-analyze.yml的inspect操作,下载并校验 preflight 描述符。若云厂商已确认released(资源为空),直接输出"已自动销毁"并成功退出;unknown_run与insufficient_evidence则是不同的显式失败(analyze.sh); - 生成临时密钥:
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:3072生成本地私钥(analyze.sh); - 确认本机公网 IPv4:通过公网 echo 服务或同主机 Cloud View 状态接口探测,随后把
client_ipv4与client_public_key作为prepare操作的输入派发——云端据此只向该/32开放入口。若同主机探测发现出口 IP 不一致,脚本会执行一次"close → rebind → prepare"重绑定(analyze.sh); - 准备会话并解密 Token:下载
session.json、encrypted-token.bin、pinned-ca.pem,校验 MCP URL 精确匹配https://<公网IP>:19092/mcp、CA 指纹为sha256:...,然后用私钥执行pkeyutl -decrypt -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256解密 Token,解密成功后立即删除私钥与密文文件(analyze.sh); - 健康探测:绕开代理直连(
--noproxy '*')并使用钉住的 CA 证书,12 次尝试确认healthz返回.status=="ok"且run_id匹配(#L795-L825); - 本地桥接:
go build编译 cmd/wkcloudanalysisbridge,在127.0.0.1随机端口发布一个仅回环的 MCP 反向代理(#L827-L847); - 检出精确源:确保本地已有
source_sha(缺失则无凭据提示地静默 fetch 一次),git worktree add --detach检出部署版本,然后拒绝项目级 Codex 覆盖——若 worktree 中存在.codex/config.toml或.codex/hooks.json直接失败(#L849-L856,函数见 #L171-L179); - 启动隔离的 Codex:
env -i清空调用方环境,仅注入PATH、私有HOME、CODEX_HOME与WK_ANALYSIS_MCP_TOKEN;--ephemeral --ignore-user-config --ignore-rules --strict-config,并通过严格权限配置把文件系统限制为:minimal=read+ workspace 根只读、network.enabled=false、shell_environment_policy.inherit="none",MCP 服务器只允许 14 个白名单只读工具(#L863-L900); - Schema 校验:用
wkclouddiagnosis validate对 JSON Schema 做严格校验,并核对run_identity(run_id/source_sha/scenario_digest)与会话一致(#L934-L947),随后显式 close 会话、原子写出--result-file。
# 关键 Codex 调用(节选自 analyze.sh,字段语义不变) env -i PATH="$PATH" HOME="$analysis_home" CODEX_HOME="$local_codex_home" \ WK_ANALYSIS_MCP_TOKEN="$analysis_token" \ "$codex_bin" exec --ephemeral --ignore-user-config --ignore-rules --strict-config \ -C "$analysis_worktree" \ -c 'default_permissions="cloud-analysis"' \ -c 'permissions.cloud-analysis.filesystem={":minimal"="read",":workspace_roots"={"."="read"}}' \ -c 'permissions.cloud-analysis.network.enabled=false' \ -c 'shell_environment_policy.inherit="none"' \ -c 'shell_environment_policy.set={PATH=...,HOME=...}' \ -c 'mcp_servers.wukongim_cloud_analysis.url="http://127.0.0.1:PORT/mcp"' \ -c 'mcp_servers.wukongim_cloud_analysis.bearer_token_env_var="WK_ANALYSIS_MCP_TOKEN"' \ -c 'mcp_servers.wukongim_cloud_analysis.required=true' \ -c 'mcp_servers.wukongim_cloud_analysis.startup_timeout_sec=10' \ -c 'mcp_servers.wukongim_cloud_analysis.tool_timeout_sec=70' \ -c 'mcp_servers.wukongim_cloud_analysis.enabled_tools=[...14 个只读工具...]' \ --output-schema "$REPO_ROOT/.github/cloud-sim/diagnosis.schema.json" \ -o "$diagnosis_json" "$prompt"本地桥接为何需要"钉住证书"
cmd/wkcloudanalysisbridge 的职责是让 Codex 只需连本地回环地址,由桥接转发到云端精确端点。它做了四重收紧:
- 只接受 HTTPS 上游,且主机必须是一个全局公网 IPv4(拒绝私网、回环、链路本地、多播),端口只能是 19092 或 19444(main.go);
- 证书精确钉住:TLS 握手时用 SHA-256 指纹比对叶子证书、校验有效期窗口与 IP SAN,而不是依赖系统信任链(main.go);
- 剥离转发头:清除
Forwarded/X-Forwarded-*,防止放大攻击面; - 回环监听:仅绑定
127.0.0.1:0,不接收任何远程绑定参数。
其边界测试 main_boundary_test.go 验证了"指纹不匹配、IP SAN 错误、证书过期"分别失败,且错误响应绝不泄露上游细节(#L182-L206)。FLOW 文档 cmd/wkcloudanalysisbridge/FLOW.md 对边界做了补充说明。
Diagnosis Result:有结构、有身份、语义明确
Codex 的输出必须落到 .github/cloud-sim/diagnosis.schema.json 定义的wukongim/cloud-simulation-diagnosis/v1结构里,字段完全封闭(additionalProperties: false)。关键语义(由 analyze.sh 的 prompt 强制):
| verdict | severity | root_cause_scope | 是否可修复 |
|---|---|---|---|
healthy | none | none | 否 |
insufficient_evidence | none | unknown | 否 |
product_defect | 非 none | product | 是(唯一可修复) |
infrastructure_interrupted | 非 none | infrastructure | 否 |
scenario_invalid | 非 none | scenario | 否 |
observation_references中只有workload_inspect工具可携带state/status,且遵循"completed必须complete=true;其他工具一律为null"。这份结果是已完成命令(completed command)——无论 verdict 如何,只要身份匹配并通过 schema 校验就算成功完成;只有 Session preflight 失败、超时、schema 非法、身份不匹配才构成非零退出。健康与否只由结构化 verdict 确立。
Schema 校验器是 cmd/wkclouddiagnosis/main.go(wkclouddiagnosis validate FILE),底层走 internal/usecase/cloudanalysis 的DecodeDiagnosisResult。
finalize.sh:等待、重试、然后证明"账单已停"
Provision(供给)阶段还会发布一份最小的非诊断型 Finalization Schedule(wukongim/cloud-simulation-finalize/v1,含active_until/analysis_ready_at/expires_at三个有序截止时间)。finalize.sh 的流程:
./scripts/cloud-sim/finalize.sh RUN_ID [--repository OWNER/REPO] [--diagnostic-focus TEXT] [--allow-fix-pr] # 前提:gh auth login;全程无需 OpenAI API Key;保持本命令在本地运行- 下载并校验 Finalization Schedule,
wait_until等待第一个有界的分析时间窗; - 复用同一个加密 Analysis Session 调用
analyze.sh尝试终态分析; - 若校验后的
workload_inspect状态仍是in_progress且租约还能安全接纳下一次会话,则保留 run 并在 60 秒后重试(minimum_retry_lease_seconds=2100,即重试前必须给后续清理留足时间余量); - 无论分析成功与否,随后必定执行精确 Cleanup:派发
cloud-sim-cleanup.yml(带run_id与finalize-<hex>关联 ID)停止计费,再通过analyze.sh的 released 形态做独立 provider 释放验证,要求结构化结果满足.state=="released" && .provider.state=="released" && .provider.resources==[]; - 若 provider 已确认
released(自动销毁),则打印终态提示并在 Codex 运行前成功退出——unknown_run与"活证据不可达"仍是两种不同的失败; - 诊断失败或可选修复失败不会抑制清理;而 provider 已确认释放的 run 则直接终止、不再发起第二次清理请求。
中途 Ctrl-C(HUP/INT/TERM)也不会跳过计费兜底:信号处理器会把"精确清理"标记为必需,EXIT 陷阱确保清理与释放验证在退出前完成(finalize.sh 与 #L256-L277)。即使本地清理全部失败,远端租约与定时 sweeper 仍是最后一道账单防线。
可选修复:与诊断完全隔离的独立进程
修复只在访问确认关闭之后才开始,并且运行在:
- 独立的本地 worktree 与独立的 Codex 进程;
- 隔离的
HOME,且没有 GitHub 凭据、云身份、MCP 配置或 Analysis Token; - 严格权限:只写修复 worktree 与隔离的 Go cache/temp 根,只读测试所需的 Go 工具链/模块缓存,禁用工具网络访问。
修复的门槛是高置信度的、可归因于仓库的产品缺陷 + 回归覆盖(对应 Diagnosis Result 中的remediation_eligibility:repository_attributable与testable都为真)。本地编排绝不以操作者主机权限直接执行生成测试:它先推送受保护分支,等待该精确 commit 通过仓库既有的只读 CI Workflow,然后才打开一个正确标注的 Draft PR,PR 正文不含任何模型生成的诊断文本,且自动合并始终被禁止。这也是--allow-fix-pr被标为"兼容标志"的原因:修复天然延后到付费资源清理完成之后。
安全模型小结与可验证依据
把 ADR-0037 与实现对照,可以归纳出四层防护:
- 凭据不落盘:云端只经 RSA-OAEP 交付一次性 Token,本地解密后立即删除私钥与密文;GitHub 无 API Key、无
auth.json、无 ChatGPT 会话; - 访问窗口有界:Token ≤45 分钟且受租约钳制、只对请求方
/32开放、不可续期、有独立 close 操作与窗口过期兜底; - 分析沙箱最小权限:
env -i+ 只读 worktree + 禁网 + 只读工具白名单 + 拒绝项目级.codex配置覆盖(fail-closed); - 清理不可绕过:诊断/修复失败不抑制清理,Interrupt 也不抑制清理,最终以 provider 确认的空资源清单作为账单已停的证据。
想深入验证上述事实,可以直接阅读:
- 决策记录:docs/adr/0037-run-codex-locally-with-an-encrypted-analysis-session.md
- 网关与会话:cmd/wkanalysis/main.go、cmd/wkanalysis/session.go、cmd/wkanalysis/oidc.go、cmd/wkanalysis/config.go
- Token 交换:internal/access/cloudanalysismcp/token.go
- 本地编排:scripts/cloud-sim/analyze.sh、scripts/cloud-sim/finalize.sh、scripts/cloud-sim/local-runtime.sh
- 证书钉住桥接:cmd/wkcloudanalysisbridge/main.go 与其测试 main_boundary_test.go
- 结果契约:.github/cloud-sim/diagnosis.schema.json、cmd/wkclouddiagnosis/main.go
- 工具链固定:.github/cloud-sim/toolchain.env(Go 1.25.11、GitHub CLI 2.96.0 等,均带 SHA-256 校验)
相关 ADR 时间线可参考 docs/adr/0037 的 supersedes 列表,以及同目录下的 0040-separate-deployment-from-cloud-lease-lifecycle.md、0044-use-separate-deployment-and-codex-ssh-identities.md、0047-retain-one-lease-across-deployment-repair.md 等后续演进。
- 即时通讯
- 后端
【免费下载链接】WuKongIM
More than just IM 不只是即时通讯(IM)
相关推荐
WuKongIM 云仿真分析边界:只分析"仍在运行"的 Simulation Run 及其释放证据机制
WuKongIM 云仿真分析边界:只分析"仍在运行"的 Simulation Run 及其释放证据机制 导读 本文围绕 WuKongIM 仓库中的架构决策记录
即时通讯后端WuKongIM 云仿真诊断架构解析:Analysis MCP 与 Analysis Skill 的职责分离设计
WuKongIM 云仿真诊断架构解析:Analysis MCP 与 Analysis Skill 的职责分离设计 云仿真压测(Cloud Simulation)
即时通讯后端WuKongIM 云端仿真 Analysis MCP:run-scoped 窄工具契约的设计与实现
WuKongIM 云端仿真 Analysis MCP:run scoped 窄工具契约的设计与实现 导读 本文围绕 WuKongIM 仓库中已接受的架构决策记录
即时通讯后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考