Files
next-tjwater-agent/docs/user-isolation.md
T
jiang 6a49d2209e feat(agent): unify MCP tools and adopt Responses delivery
Move project and host queries into native MCP with host-owned OAuth/context,
while retaining memory, skills, actions and publication plugins. Add nearby-model
queries and explicit chat/task/learning model defaults.

Adopt Responses HTTP/SSE and native steer/queue inbox delivery, preserving scoped
session ownership, pending input edits and investigation communication.

Disable local-provider discovery: Server /health allowed native vLLM to probe
/v1/models on port 8000. A native before/after regression prevents recurrence.

Validation: 170 focused Bun tests; service/plugin typechecks; native discovery,
provider-discovery and input-inbox smoke checks; no paid model inference.
2026-10-19 18:46:58 +08:00

82 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 用户隔离与会话存储
Agent 支持个人会话和项目会话。会话始终属于登录用户,项目归属可选;同一项目中的其他用户不会因此获得聊天记录或计算文件的访问权。
## 身份与范围
| 范围 | 请求凭据 | Server 验证 | 工作空间 |
| --- | --- | --- | --- |
| 个人会话 | Bearer | `/api/v1/access-context` 验证用户 | 用户目录中的 `personal/` |
| 项目会话 | Bearer、`X-Project-Id` | `/api/v1/agent-auth-context` 验证用户与项目权限 | 用户目录中的项目键目录 |
无项目头不等于匿名请求。生产环境始终验证 Bearer,验证失败不回退为本地用户。开发环境的无凭据请求使用同一个本地身份,不能用于多用户部署。
个人会话不取得项目业务凭据,调用依赖这些凭据的 TJWater API 会返回 `PROJECT_CONTEXT_REQUIRED`。任务执行和任务协作仍要求项目权限。个人记忆范围的键包含用户身份,避免所有个人会话共用一个记忆桶。
前端面板的“对话归属”支持个人会话和当前项目。切换会开启新对话,保留旧会话及其原归属,历史缓存按认证来源和范围区分。当前没有把已有会话搬入或移出项目的接口。
## 文件结构
根目录由 `CONVERSATION_WORKSPACE_ROOT` 管理,默认是 `data/session-workspaces/`:
```text
actor-<用户键>/
├── personal/
│ └── session-<会话 ID 的 SHA-256>/
└── project-<项目键>/
└── session-<会话 ID 的 SHA-256>/
├── chat/
│ ├── owner.json
│ ├── messages.json
│ ├── transcript.json
│ └── learning.json
└── workspace/
├── files/
├── tmp/
└── .cache/
```
个人和项目会话具有相同的内部结构。用户和项目键来自经过验证的身份,不从工具参数或浏览器路径直接取值。会话 ID 使用完整 SHA-256 形成目录名,原 ID 保存在 `chat/owner.json` 中。
`messages.json` 保留前端聊天状态,包括消息、工具展示和执行计划;`transcript.json` 是经过持久化策略处理的问答摘要,供历史搜索和学习使用,最多保留配置的轮数。摘要不是完整原生执行轨迹。
`learning.json` 保存该会话的学习检查、复盘进度。它与聊天记录共用归属校验和串行写入保护,位于宿主管理的 `chat/`,不向模型开放。
`data/session-metadata/` 是归属、标题、父会话、用途和任务身份的索引。它为会话 ID 冲突检查、列表及所有会话接口提供同一归属来源。新会话创建也会初始化对应的聊天目录。
历史索引在服务进程内按用户和范围缓存,创建、更新和删除同步维护缓存。同一数据根目录由一个 Agent 进程写入;修改底层索引文件后需要重启,当前不支持多个 Agent 进程并发写入同一目录。
## 隔离边界
| 对象 | 当前边界 |
| --- | --- |
| 会话接口 | 先校验用户、个人或项目范围,再读取状态;无权访问统一返回未找到 |
| 聊天目录 | 校验归属文件和规范路径;拒绝目录、记录文件中的符号链接 |
| 聊天搜索 | 只扫描当前用户与范围的摘要目录 |
| 原生文件工具 | 只允许当前原生会话的 `workspace/`;会话哈希不匹配时拒绝读取 |
| Shell | 在 `workspace/files/` 运行,Landlock/seccomp 限制文件、网络和进程访问 |
| 记忆 | 用户记忆按用户隔离;项目记忆在项目内共享;个人工作空间记忆按用户隔离 |
| Skill | 按部署实例共享,不能当作用户私人文件夹 |
| MCP | 连接配置与上游账号按实例管理,工具权限按会话应用;上游数据授权由 MCP 服务负责 |
| OpenCode 数据库 | 仍是实例共享的原生数据库,访问依赖服务密码和可信 Agent 路由 |
`chat/` 是计算目录的同级目录,目录权限为 `0700`,记录文件为 `0600`。模型和 Shell 不能直接读写聊天记录;需要查询历史时使用受范围约束的 `session_search`。
这是产品用户隔离和会话沙箱,不是为每个用户启动一个 Linux 账号、容器或独立 OpenCode 实例。服务宿主账号和管理员仍可管理全部持久化数据。原生数据库默认位置为 `~/.local/share/opencode/opencode.db`,工作空间不会取代它,部署仍需保留该数据卷。见 [OpenCode 存储说明](https://opencode.ai/v2/docs/troubleshooting/#service-files)。
## 旧记录与保留
启动时依据会话索引迁移旧 UI 状态和学习进度,并依据摘要的用户、范围与会话身份迁移旧摘要。旧文件经逐字节备份核对后归入 `PERSISTENCE_BACKUP_DIR/legacy-chat/actor-*/<范围>/session-<sha256>/chat/`,文件名包含内容哈希;源文件只在备份核对成功后移除。重复迁移不会覆盖已存在的新记录。`legacy-chat/migration.json` 保存版本、来源目录时间戳与迁移结果,来源未变化时跳过重复导入。缺少归属索引的 UI 状态和学习进度保留在旧目录,不猜测归属。历史匿名记录保留原本的本地用户范围,不自动划归某个登录用户。
旧计算目录在该会话继续执行前迁入对应工作空间,使用原生 Session API 更新目录绑定。文件保留,目标冲突停止迁移,绑定更新失败可重试。聊天目录的迁移不需要模型推理。
迁移核对完成后,已迁入工作空间的旧记录备份可以清理,保留 `legacy-chat/migration.json` 作为完成记录。当前本机的 139 份迁移备份及三个空迁移目录已清理,现行聊天和学习记录仍在对应的 `chat/` 中。使用工作空间存储时,初始化不再创建旧迁移目录。
归档会话保留记录。删除会话先写入索引删除状态和 `chat/deleted.json`,会话立即从列表、读取和搜索中隐藏;随后停止聊天与学习任务,移除该原生会话、消息、摘要、学习进度、计算目录及其旧记录和迁移备份。保留最小归属和删除标记,拒绝迟到写入、会话 ID 再使用以及旧数据重新导入。清理未完成时返回 `503 SESSION_CLEANUP_PENDING`,重复同一用户、范围的删除请求可继续清理。
审计日志、已经形成的用户或项目记忆、共享 Skills、Server 业务报告及部署级离线备份有各自的保留职责,不随会话删除。工作空间仍没有自动过期清理,不依据目录为空就删除历史目录。
标题生成、学习检查和复盘在成功、失败及取消后释放自己的原生辅助会话和计算目录;清理失败会记录日志。它们不作为用户聊天长期保留。
持久化默认 `data/` 可同时保存 Agent 索引、工作空间和记忆;Skill 目录与 OpenCode 数据库仍需各自的数据卷。自定义工作空间根目录也需要单独挂载。