JianFeeeee
344f970353
修死信黑洞:发信前校验收件人可达性
## 事故
发给已彻底删除的 Agent 返回 200:邮件入库、分配 20 个来回预算、
建好会话,而那一端永远不会有人读。发件人看到 200 和一个 session_id,
以为送出去了。
实测(修复前):
POST /me/mail/send to=remotebot@/tmp → 200
mail_id 53e4c9ea… session dc8a41c3… budget_max 20
remotebot 的 agents 行在本会话早前已被 DELETE /admin/agents 删掉。
根因:三个发信入口的检查链只有「地址语法 / 调用权限 / 会话别名」,
从不问「这个名字存在吗」。`AgentDisabled` 那个函数只在注册路径被调用,
发信路径压根不查——它的注释甚至写着「Agent 不存在时返回 false」。
静默丢件比报错严重:报错能立刻改,静默丢件要等对方追问才发现。
这与之前修过的「relay_key 400 被当暂时失败导致静默挂死」同类。
## 修法
`repo.RecipientDeliverable(ctx, name)` 作为唯一判据:
- 人类用户 → 放行(人的收件箱一直在,不受 Agent 停用影响)
- Agent 在册且未停用 → 放行
- Agent 不存在 → ErrRecipientUnknown → 404
- Agent 已停用 → ErrRecipientDisabled → 409
`handler.checkDeliverable` 把它接到三个入口,**收件人与抄送位一起查**:
不查 cc 的话抄送位就成了绕过口,而且因为不是主收件人更不容易被发现。
- me.go MeSendMail (人类发信)
- mail.go SendMail (Agent 发信)
- forward.go ForwardMail(人与 Agent 两条转发路径共用)
停用选择「当场拒收」而非「入库等恢复后补投」:停用的语义就是这个
Agent 现在不干活,让发件人以为信已送达更坏——它会照常等回信。
## 测试
`internal/repo/deliverable_test.go` 8 例:人类 / 在线 Agent / 不存在 /
删除后不可达 / 停用 409 / 恢复后重新可达 / 空名放行 /
同名人类优先于已停用 Agent。
负向对照:让 RecipientDeliverable 无条件 return nil(还原事故前行为),
UnknownName、AfterDelete、Disabled 三例如期失败。
## 线上验证
发给已删除 remotebot → 404「收件人不存在」
发给在线 pi → 200,pi 回信「可达性 OK」
cc 位放已删除 remotebot → 404(绕过口已封)
停用 pi 后发信 → 409「已被管理员停用」
恢复 pi 后发信 → 200
## 顺带
- 部署改用 sqlite3 .backup + install -m 0755(原子 rename,不写坏
运行中进程镜像),来自 git-release-discipline skill 的运维纪律
- 清理本会话测试残留:误登记的 opencode 密钥、remotebot 两把残留密钥、
死信测试邮件与会话
2026-09-05 09:58:36 +08:00
..
2026-09-05 00:28:05 +08:00
2026-09-05 09:58:36 +08:00
2026-09-04 06:27:15 +08:00
2026-09-04 06:27:15 +08:00