Skip to content

feat: orchestrate Generic endpoints through heartbeat leases - #23

Open
why-tomato wants to merge 2 commits into
lex-mainfrom
codex/endpoint-connectivity-probe
Open

feat: orchestrate Generic endpoints through heartbeat leases#23
why-tomato wants to merge 2 commits into
lex-mainfrom
codex/endpoint-connectivity-probe

Conversation

@why-tomato

Copy link
Copy Markdown

变更概述

本 PR 增加 endpoint-initiated heartbeat lease、显式 Agent dispatch、authenticated Edge Start/Stop、Room-scoped rollback 与 browser/internal LiveKit URL 分离。云端不配置静态 Orin IP;address 只来自认证 heartbeat 的 source address 和短租约。

主要改动

1. Heartbeat lease 与安全边界

  • /api/endpoint/connectivity 认证固定 identity 并以请求 source address 续租;registry 只保存 bounded identity/instance/address/timestamps。
  • 云不主动发现 endpoint,不保存静态 IP/hostname/SSH/filesystem path;control 从 fresh immutable lease 构造。
  • /api/connection-details 优先 LIVEKIT_BROWSER_URL、fallback LIVEKIT_URL,不从 Host/forwarded Host/origin 推导。

2. Agent、Edge 与 Room lifecycle

  • Session 先 dispatch Agent,再解析 lease,向 endpoint 发送短期 Room token 与 endpoint-reachable URL;server Room operations 保持 internal LIVEKIT_URL
  • Pairing 先 reclaim stale state,再 authenticated Start 并等待两个 input participants 与三条 tracks;failure/cancel 用同一 target rollback。
  • Stop 经 registry/dispatch barrier 顺序清 processor、Edge、Room;failed-start cleanup 不重新解析 Edge,chat Send 绑定 canonical session。

3. 两个部署 token 与 leadership 决策

  • 不提交值。Heartbeat:LEX_REFLEX_GENERIC_CONNECTIVITY_TOKEN + ENDPOINT_CONNECTIVITY_TOKEN;control:LEX_REFLEX_EDGE_MEDIA_CONTROL_TOKEN + EDGE_MEDIA_CONTROL_TOKEN
  • 旧 cache/runtime exports 隐藏 provisioning;Mac/Orin owner-only configs 需独立注入。
  • 请 leadership 决定 managed secret-provisioning、rotation、撤销与审计。

影响说明

  • Fresh lease 为 source-derived 10.2.2.199;LAN advertisement=ws://10.2.77.108:7818,internal=ws://localhost:7818

  • 正式 Room 中 Firefox、唯一 Agent、room_audio_inputroom_video_inputroom_audio/room_video_raw/room_video ready;天气文本完整回复;Stop 三 targets ok 且 failures=[]

  • Secret、runtime artifacts 与 .pnpm-store 不进入 manifest;失败 cleanup 不漂移 endpoint target。

  • pnpm exec tsx --test tests/generic-edge-media.test.mjs tests/session-start-dispatch.test.mjs tests/session-stop.test.mjs

    ✅ passed: 63 tests passed

  • Prettier / ESLint / git diff --check

    ✅ passed: focused quality checks passed

  • Delivery 验证 exact SHA;用户选择 wait_checks=false

@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown

Claude finished @why-tomato's task in 2m 47s —— View job


任务进度

  • 获取 PR diff 与相关文件上下文
  • 阅读 CLAUDE.md / README.md 等仓库约定(未找到 CLAUDE.md)
  • 审查 LiveKit room/track 生命周期、媒体权限失败路径
  • 审查 React hook 清理、Next.js 客户端/服务端边界
  • 审查环境变量、secret 泄露、TypeScript 类型
  • 输出最终 review 结论

Review 结论

有条件通过

问题及建议

  • [P1] README.md:110 vs README.md:158:新增说明改为 pnpm exec next dev --hostname 0.0.0.0 --port 3000,但同一文件后段(158 行)仍写 directly with pnpm dev,两处启动方式互相矛盾,且与本 PR 的 heartbeat lease 主题无关。建议:统一说法,并确认是否真的需要把开发服务器绑定到 0.0.0.0(默认 localhost 更安全,避免本地开发环境暴露到局域网)。

  • [P1] app/api/session/generic-edge-media-pairing.ts:107-120coordinateGenericRoomSession):dependencies.dispatchAgent() 调用在 try 块之外,如果 dispatch 本身在 LiveKit 侧已创建资源后才抛错(例如网络中断发生在返回前),cleanupSession 不会被触发,可能残留未清理的 Agent dispatch。建议确认 dispatchRoomSession/session-registry 是否已经在其他路径(如后续 stop 或过期回收)兜底了这种部分失败场景,否则应把该调用纳入清理范围。

  • [P2] lib/generic-endpoint-lease.ts:readLeaseRecords(约 210-245 行):目录下任意一个 .lease.json 文件权限/属主不对或 JSON 损坏,会直接 throw 并使整个 readLeaseRecords 失败,导致后续所有合法 instance 的心跳续租和 resolveActiveGenericEndpointLease 全部失败(fail-closed),需要人工清理残留文件才能恢复。这是有意的“fail closed”设计,但建议在 README/运维文档中显式说明该单点故障恢复方式(当前 README 未提及)。

  • [P2] app/api/session/generic-edge-media-pairing.ts:56pairGenericEdgeMedia 开头的 reclaim stop 调用):该调用不在 try/catch 内,如果端点因为网络抖动导致 reclaim-stop 失败(而非明确拒绝),会直接中断整次 dispatch,且不会重试。若这是有意的 fail-closed 行为可以不改,但建议在注释或 PR 描述中说明,避免后续维护者误认为是遗漏的异常处理。

  • [P2] app/api/session/stop/route.ts:384 / 160isGenericEndpointPairingEnabled() 在同一次 stopRoomInput 调用中被读取两次(一次在 resolveRoomInputStopUrls,一次在这里),两次都基于 process.env,理论上应保持一致,但建议合并为单次求值传参,减少环境变量竞态窗口(尤其考虑到未来可能支持热更新配置)。

亮点

  • lib/generic-endpoint-lease.ts 对心跳 payload、CIDR、目录/文件权限(0700/0600)、属主、符号链接均做了严格校验,安全边界设计扎实。
  • lib/endpoint-connectivity.ts 使用 timingSafeEqual 做 token 比对,避免时序攻击;字段白名单校验(Object.keys 严格匹配)可防止多余字段注入。
  • app/api/connection-details/route.ts 正确移除了从 Host/X-Forwarded-Host 推导 URL 的逻辑,改为显式 LIVEKIT_BROWSER_URL/LIVEKIT_URL,测试 (tests/connection-details.test.mjs) 覆盖了防回归断言。
  • .env.example 未新增任何新 token 明文,符合 PR 描述中"不提交值"的约定。

残余风险:未在本地执行 pnpm test/pnpm lint/tsc --noEmit(工具权限限制导致 pnpm/tsc 命令被拒绝),仅做静态代码审查;PR 描述中列出的测试结果未做二次验证。

@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown

Claude finished @why-tomato's task in 3m 2s —— View job


任务进度

  • 获取 PR diff 与相关文件上下文
  • 阅读仓库约定(README.md,未找到 CLAUDE.md)
  • 审查 LiveKit room/track 生命周期、媒体权限失败路径
  • 审查 React hook 清理、Next.js 客户端/服务端边界
  • 审查环境变量、secret 泄露、TypeScript 类型
  • 输出最终 review 结论

Review 结论

有条件通过

问题及建议

  • [P1] lib/endpoint-connectivity.ts:60-67 + app/api/endpoint/connectivity/route.ts:53:PR 描述称 registry "以请求 source address 续租",但实现中 address 完全来自心跳 JSON body 自报字段,仅校验其为 RFC1918 私网且落在 GENERIC_EDGE_MEDIA_ALLOWED_CIDRS 内,代码中未见任何从 TCP/HTTP 连接实际来源(如反向代理注入的可信 x-forwarded-for)校验或比对该字段的逻辑。也就是说安全边界完全依赖 token 持有,而非"token + 真实来源地址"双重约束。若 token 泄露或同网段内另一主机复用 token,可自报任意允许网段内地址,从而让云端向错误目标下发 start/stop 控制指令。建议:要么按描述真正校验来源 IP,要么修正 README(README.md:75-79)中"以请求 source address 续租"的表述,避免安全模型描述与实现不符。

  • [P2] lib/generic-endpoint-lease.ts:158-199readLeaseRecords):目录下任意一个 .lease.json 文件权限/属主异常或 JSON 损坏,仍会直接 throw 并导致整个函数失败,进而使心跳续租与 resolveActiveGenericEndpointLease 全部 fail-closed,需人工清理残留文件才能恢复。此为已知设计(fail-closed),但截至本次改动仍未在 README/运维文档中说明该单点故障的恢复步骤。

  • [P2] app/api/session/generic-edge-media-pairing.ts:82pairGenericEdgeMedia 开头的 reclaim stop 调用):该调用仍不在 try/catch 内且无重试,端点侧网络抖动会直接中断整次 dispatch。若这是有意的 fail-closed 行为,建议在代码注释中标注,避免被误认为遗漏的异常处理。

  • [P2] app/api/session/stop/route.ts:160:384isGenericEndpointPairingEnabled() 在同一次 stopRoomInput 调用链中被求值两次,均读取 process.env,建议合并为单次求值后传参,减少潜在的环境变量竞态窗口。

残余风险:generic-edge-media-pairing.ts:107-120coordinateGenericRoomSessiondispatchAgent() 未被 try 包裹导致清理遗漏)的问题已在最新提交 74a22c3 中修复并补充测试覆盖,予以确认。未在本地执行 pnpm test/pnpm lint/tsc --noEmit(工具权限限制导致命令被拒绝),仅做静态代码审查。
· branch codex/endpoint-connectivity-probe

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant