会话边界(INV-SESS-01~03)
wesgine 中会话(Session)的 Actor 边界设计,确保用户只能访问自己的会话和检查点。
核心问题
Session 是 Actor 边界的第二个面。归属键曾是 Tags["actor"],现已提列为 wes_sessions.actor 独立列。
三条不变量
INV-SESS-01:变更走 cell:chat
会话变更的授权面是 cell:chat 不是 cell:read。
/sessions 曾整段缺席 defaultCellRequiredScope,每个变更落到只读默认值。
Cell 级批量操作(interrupt-all / mark-interrupted / delete-by-prefix)不指名 session、无归属可查,门只能是 admin token。
INV-SESS-02:归属单点 AssertActor
归属只有一个执行点 SessionHandle.AssertActor,且 HTTP 门开在路由漏斗 dispatchCellSessionByID 而非各 handler。
// 漏斗入口——所有 /sessions/{sid}/* 子路由的唯一入口
func dispatchCellSessionByID(w, r, cellHandle, sessionID) {
if err := cellHandle.Sessions().AssertActor(ctx, sessionID, actor); err != nil {
http.Error(w, "not found", 404)
return
}
// 分发到具体 handler
}
为什么用漏斗不用各 handler:在漏斗上判据使"新增子路由忘了检查"不可表达。在 handler 上判据产出的正是 4/12 的覆盖率。
INV-SESS-03:行身份 (session_id, id)
Checkpoint / Branch 的行身份是 (session_id, id),SQL 与方法签名都必须带 session。
-- ✅ 正确
DELETE FROM wes_checkpoints WHERE id = ? AND session_id = ?
-- ❌ 危险
DELETE FROM wes_checkpoints WHERE id = ?
-- 归属门只能为 session 那一半作证,id 那一半是调用方给的
攻击场景:DELETE /sessions/我的/checkpoints/受害者的 会通过一个回答正确的门("这个会话是你的")然后删掉别人的行。
归属提列
actor 从 Tags["actor"] 提成 wes_sessions.actor 独立列:
ddlSessionsV10:加列ddlSessionsV11:从 tags 回填AssertActor与ListSessionsFiltered读列CreateSession是唯一写入点且不从 Tags 兜底
不从 Tags 兜底的理由:Tags 是调用方给的审计元数据,从它读归属会让忘填 Actor 的写入方静默成功。
删除的幂等性
| 操作 | 跨 session | 不存在 |
|---|---|---|
| Delete checkpoint | 幂等成功 | 幂等成功 |
| Restore checkpoint | ErrCheckpointNotInSession → 404 | 404 |
| Fork checkpoint | ErrNotInSession → 404 | 404 |
区分"不存在"与"存在但不是你的"= 存在性预言机,所以删除不区分。
只有 Restore / Fork 硬失败——它们的成功会返回或复制内容。
消息查询
GET /cells/{id}/sessions/{sid}/messages 支持分页:
?before=<message_id>&limit=50
消息列表在 AssertActor 通过后才执行查询。
会话中断
| 端点 | 权限 | 说明 |
|---|---|---|
POST /sessions/{sid}/interrupt | cell:chat + actor 归属 | 中断单个会话的 Run |
POST /sessions/interrupt-all | cell:admin | 中断 Cell 内所有 Run |
POST /sessions/mark-interrupted | cell:admin | 崩溃后批量标记 |
与其他面的关系
| 面 | 归属判据 | 执行点 |
|---|---|---|
| 记忆 | ActorVisible | saveCore / 读查询 |
| 会话 | AssertActor | dispatchCellSessionByID |
| 观测 | actorClause | SQL 谓词 |
| Cron | jobVisible / ownedJob | Manager 方法 |