3 Commits
Author SHA1 Message Date
chick 72b44398cd fix(relay): 每个节点的会话与挑战数量设上限;agent 侧加 fuzz
设计假定节点是敌对方("被攻陷的节点不该能读到另一个节点的流量"),而中继为它
保存两份状态:会话(TTL 30 分钟)与挑战(TTL 60 秒)。两者此前只在**过期**时被
清理,所以一个循环握手的节点可以让它们无界增长——实测 5000ms 中继时间内 5000 次
握手留下 5000 个存活会话,且没有任何东西阻止接下来的 5000 次。

诚实值是 1:每节点只有一个连接,注册表也只保留一个挂起的轮询。所以上限取
每节点 4 个会话、16 个挑战,超出时淘汰最旧的而非拒绝——"最新一次握手获胜"已经是
注册表对连接本身的规则,拒绝会让一次合法的重连被自己早先的尝试挡住。

写这个上限时踩到并修掉了一个自己造的 bug:第一版保留最旧、删掉刚加入的,于是
循环握手的节点会被自己下一次握手淘汰掉最新会话,每次轮询都 401。只断言"表很小"
的测试抓不到它——能抓住它的是"最新会话仍然可用"这条断言。

agent 侧新增 fuzz(agent/internal/agent/fuzz_test.go):Handle 是唯一一个输入来自
被假定敌对一方的函数,而 agent 是单进程,这里的一次 panic 不是一次坏请求而是节点
下线直到 systemd 拉起。断言故意弱——不 panic,且要么返回错误要么返回合法 JSON——
拒绝逻辑由旁边的表驱动测试覆盖。跑 45 秒、134 万次执行,无失败;测试内跑的是种子
语料(Go 的常规做法,fuzztime 只在显式 -fuzz 时生效)。

Node 112 项、Go 全包、Swift 101 项通过。
2026-09-17 01:29:55 +08:00
chick 443af20e0a feat: agent 本机 SimAdmin 登录与会话维持;中继设备侧限流与 agent Retry-After
- runtime.NodeAuth:agent 自查 /api/auth/status,需要时自行 POST /api/auth/login 并持
  simadmin_session Cookie 转发;错误密码进入 60s 冷却(对齐控制台 ensureAuthenticated),
  节点回 401/403 即失效重探,SimAdmin 重启无需重启 agent
- 节点密码进 agent 配置(nodePassword),永不经中继、永不下发到手机;登录路径仍被策略
  挡在电话可达面之外(TestAuthPathsAreUnreachable)
- 新拒绝分类 node_auth_failed:与 upstream_error 区分,手机可提示'该节点需重新配置'
- 中继:/v1/request 每设备固定窗口限流(默认 30/分钟),未鉴权调用不消耗预算
- transport:解析 Retry-After 并入退避;main loop 对 429 直接遵从中继指示
- contracts/envelope.schema.json 补 InnerResponse.outcome 字段
- 设计文档与 README 同步:WSS 改为长轮询的实现修订、响应方向已临时×临时、P2/P3 状态
2026-09-16 09:15:25 +08:00
chick 54d9696a4d baseline: P1 契约、agent 核心与中继投递(既有工作首次入库) 2026-09-16 08:41:13 +08:00