fix(#71): 模块验签改部署期公钥注入(B 方案)+ 壳桥挂载时机修复 - #72
Merged
Merged
Conversation
根因①:模块 Worker 运行时跨 Worker 拉 core JWKS 被 CF 同 zone 禁令拦截 → /api/count 恒 401。 B 方案:verifyModuleToken 改为入参 CORE_JWKS_JSON 字符串 + createLocalJWKSet 本地验签, 删除远程 JWKS 路径(唯一使用方 hello);hello Bindings 由 CORE_JWKS_URL 改 CORE_JWKS_JSON, 缺注入回 503 jwks not provisioned(不再谎报 401)。 测试:真 keypair 签 token→200;坏 token/aud 不符/篡改 payload→401;缺 CORE_JWKS_JSON→503; 破坏性红测:全局 fetch 抛错下合法 token 仍 200 且 fetch 零调用。 Signed-off-by: HandyWote <huangyinghui01@corp.netease.com>
moduleWranglerConfig 增必填 jwksJson → vars.CORE_JWKS_JSON,删除 jwksUrl/workers.dev 占位分支; keypair 导出 publicJwksJson(与 core toPublicJwks 同形状);步骤④前解析公钥: 本运行新生成 keypair → 内存公钥(不抓公网);否则 GET <baseUrl>/.well-known/jwks.json; 失败硬报错并提示 DNS/路由未就绪可重跑。README 补模块 vars、换钥流程、Docker 等价注记。 测试:装配产物 CORE_JWKS_JSON shape 合法且无 CORE_JWKS_URL;steps 内存/公网/失败三分支。 Signed-off-by: HandyWote <huangyinghui01@corp.netease.com>
Signed-off-by: HandyWote <huangyinghui01@corp.netease.com>
根因②:watch(activeModule) 默认 pre-flush,回调时 iframe 未进 DOM → frameEl 为 null → 误判「模块入口配置无效」;retry 不重载 iframe,模块 ready 已发出无人接收(概率性失败)。 改 flush:'post' + nextTick 兜底,iframe 未挂载则等模板 ref 就位(不再静默 failed); retry 自增 frameReload 并入 frameKey,强制 iframe 重挂使模块重新发 ready。 测试:jsdom 组件测试断言桥在 iframe 挂载后 attach、ready→token→帧就位、retry 替换 iframe DOM。 Signed-off-by: HandyWote <huangyinghui01@corp.netease.com>
This was referenced Sep 9, 2026
HandyWote
added a commit
that referenced
this pull request
Sep 9, 2026
provisionAll 中 apps/shell/dist 一旦存在即跳过 vite build、直接 cp 复用旧产物, 源码变更被静默丢弃(真机实证:PR #72 合并重部署后线上 bundle hash 未变化)。 - 删除 dist 复用捷径:每次部署无条件重建 shell,部署器职责=始终搬运当前源码树; - 新增 buildShell 注入口(同 steps.ts putSecret/resolveZone 模式),测试注入 fake 避免真实 vite; - runNineSteps 转发 buildShell;steps.test.ts 注入快速 fake(真实 vite ~4.4s/次会使套件超时); - 修正 provisionAll JSDoc 与文件头注释中 FORCE_BUILD/dist 复用措辞; - 新增 provisionAll 用例:预置陈旧 dist + stale.marker,断言新产物入 assets/shell 且无陈旧残留。 Signed-off-by: HandyWote <huangyinghui01@corp.netease.com>
HandyWote
added a commit
that referenced
this pull request
Sep 9, 2026
provisionAll 中 apps/shell/dist 一旦存在即跳过 vite build、直接 cp 复用旧产物, 源码变更被静默丢弃(真机实证:PR #72 合并重部署后线上 bundle hash 未变化)。 - 删除 dist 复用捷径:每次部署无条件重建 shell,部署器职责=始终搬运当前源码树; - 新增 buildShell 注入口(同 steps.ts putSecret/resolveZone 模式),测试注入 fake 避免真实 vite; - runNineSteps 转发 buildShell;steps.test.ts 注入快速 fake(真实 vite ~4.4s/次会使套件超时); - 修正 provisionAll JSDoc 与文件头注释中 FORCE_BUILD/dist 复用措辞; - 新增 provisionAll 用例:预置陈旧 dist + stale.marker,断言新产物入 assets/shell 且无陈旧残留。 Signed-off-by: HandyWote <huangyinghui01@corp.netease.com>
HandyWote
added a commit
that referenced
this pull request
Sep 9, 2026
provisionAll 中 apps/shell/dist 一旦存在即跳过 vite build、直接 cp 复用旧产物, 源码变更被静默丢弃(真机实证:PR #72 合并重部署后线上 bundle hash 未变化)。 - 删除 dist 复用捷径:每次部署无条件重建 shell,部署器职责=始终搬运当前源码树; - 新增 buildShell 注入口(同 steps.ts putSecret/resolveZone 模式),测试注入 fake 避免真实 vite; - runNineSteps 转发 buildShell;steps.test.ts 注入快速 fake(真实 vite ~4.4s/次会使套件超时); - 修正 provisionAll JSDoc 与文件头注释中 FORCE_BUILD/dist 复用措辞; - 新增 provisionAll 用例:预置陈旧 dist + stale.marker,断言新产物入 assets/shell 且无陈旧残留。 Signed-off-by: HandyWote <huangyinghui01@corp.netease.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
修复 issue #71 真机验收发现的两个缺陷。目标分支
m0/dev,rebase 合并。根因与修法
根因①:模块验签跨 Worker 拉 core JWKS 被 CF 同 zone 禁令拦截 →
/api/count恒 401链路:壳发 token → 模块 Worker 运行时
fetch https://<domain>/.well-known/jwks.json验签 → CF 平台规则「同一 zone 内 Routes 不能作为 fetch() 目标」→ jose 拉 JWKS 失败 → 模块回 401「token invalid or expired」。子域(Custom Domain)形态下该 fetch 合法,B 方案切 zone 路径路由后变非法。修法(用户拍板的 B 方案:部署期公钥注入,不用 service binding)
packages/module-sdk/src/verify.ts:verifyModuleToken(token, { coreJwksJson, audience })改本地验签 ——createLocalJWKSet(JSON.parse(coreJwksJson))+jwtVerify(aud 锁定不变);删除远程 JWKS 路径(唯一使用方 hello,杜绝死代码)。未来模块共用此工具。modules/hello/src/index.ts:BindingsCORE_JWKS_URL→CORE_JWKS_JSON?: string;requireAuth/createAuthMiddleware走 SDK 本地验签;未注入 → 503jwks not provisioned(不再把「拿不到钥匙」谎报成 401)。deploy/cloudflare/src/assemble.ts:moduleWranglerConfig增必填jwksJson→vars.CORE_JWKS_JSON;删除jwksUrl/workers.dev 占位分支。deploy/cloudflare/src/steps.ts:步骤④模块部署前解析公钥,三级顺序 —— 本运行刚生成 keypair → 内存 public JWK(首部署,不抓公网);否则GET https://<domain>/.well-known/jwks.json(部署器在公网,无同 zone 问题);失败 → 硬报错(提示 DNS/路由未就绪可重跑)。deploy/cloudflare/src/keypair.ts:导出publicJwksJson(pair),与 coretoPublicJwks同形状(含kid)。wrangler secret put JWT_PRIVATE_KEY更新 → 重跑部署即把新公钥同步进各模块 vars(部署器本就是密钥真值持有者)。根因②:壳桥挂载时机错误 → 「模块加载失败」概率性出现
watch(activeModule)默认 pre-flush,回调执行时 iframe 尚未进 DOM →frameEl为 null → 误判「模块入口配置无效」;retryFrame不重载 iframe,模块 ready 消息早已发出无人接收。修法(实现级时机修复)
watch(activeModule)改{ flush: 'post' },回调内await nextTick()兜底确认frameEl非空;iframe 未挂载时不再静默判 failed,等模板 ref 就位后再挂桥(waitForFrameEl)。仅frameOriginFor(...) === null(入口配置真无效)才失败。retryFrame自增frameReload并并入frameKey→ iframe 按 key 重挂 → 模块重新发 ready,消息不再丢失;重挂后对新帧重新挂桥。验收对照表
modules/hello/test/hello.test.ts(12 用例);零网络用例expect(fetchSpy).toHaveBeenCalledTimes(0)且 200;破坏性红测见下deploy/cloudflare/test/steps.test.ts分支 A/B/C(A 断言 fetchJwks 零调用 + 产物合法 JWKS;B 断言收到 baseUrl + vars 原样;C 断言 rejects 含「无法获取 Core 公钥」「可重跑部署(幂等)」)deploy/cloudflare/test/assemble.test.ts断言keys[0]具kty/crv/x/y/kid/use/alg,CORE_JWKS_URL不存在;moduleWranglerConfig已删占位分支apps/shell/src/App.test.ts(jsdom + 真实attachModuleBridge,未 mock 桥):ready→fetchModuleToken('hello')→帧就位;retry 断言 iframe DOM 元素被替换;破坏性红测见下pnpm -r typecheck && pnpm -r test && pnpm -r build全绿(contracts 23 / ui 7 / module-sdk 39 / core-api 74 / shell 41 / deploy 68 / hello 12)破坏性回验(红 → 绿,均实测)
verify.ts改回createRemoteJWKSetnetwork must not be used)→ 还原后 12/12 绿assemble.ts输出CORE_JWKS_URLApp.vue还原旧实现(pre-flush + 立即读 frameEl 判 failed)SPEC 措辞缺口(未改 docs/,以 docs 为准)
docs/PRODUCT_SPEC.md§5.3 称「Custom Domain 与 zone 路径路由两者等价」。该「等价」在服务器间通信层不成立:CF 同一 zone 内 Routes 不能作为fetch()目标,内部 Worker 互通需 service binding 或部署期注入(本 PR 的 B 方案),而 Docker 形态用内网 HTTP。建议后续以 docs 为准补一句限定(本 PR 不修改 docs/)。边界
docs/(缺口在 PR 指出)。docker/目录为空壳:本次仅在 CF 装配器落地,README 已加 Docker 等价注记(同一CORE_JWKS_JSON环境变量进 compose)。验证命令