docs: design multi-user rbac skill governance
This commit is contained in:
@@ -0,0 +1,414 @@
|
||||
# Boss 多用户权限与 Skill 治理设计
|
||||
|
||||
目标:把当前偏单管理员视角的 Boss 控制台升级为多用户、多设备、多权限的协作控制平台,让超级管理员可以安全地把不同电脑、项目线程、主 Agent 能力和 Skill 分配给不同子账号。
|
||||
|
||||
## 背景与现状
|
||||
|
||||
当前 Boss 已经具备几块可复用基础:
|
||||
|
||||
- 登录会话与账号体系已经存在,角色包含 `member / admin / highest_admin`。
|
||||
- 设备模型已有 `account` 字段,可表达设备归属。
|
||||
- 会话、项目、线程、设备和 Skill 清单都已经在 `BossState` 里统一持久化。
|
||||
- 设备 Skill 已可由 `local-agent` 从本机 `~/.codex/skills` 上报到 Boss。
|
||||
- 主 Agent 已能读取设备、项目、线程、记忆和任务状态,并通过本地节点执行对话与开发任务。
|
||||
|
||||
当前缺口不是“新增一个用户列表页面”,而是缺少正式的授权模型:
|
||||
|
||||
- API 多数只校验是否登录,没有统一过滤当前用户可见设备和项目。
|
||||
- 子账号是否能看某台电脑、某个项目、某条线程,当前主要靠设备 `account` 粗略判断。
|
||||
- Skill 只有设备上报清单,没有“谁可以使用哪个 Skill”的授权层。
|
||||
- 主 Agent 构造上下文时还没有强制按当前用户权限裁剪,存在未来多用户场景下越权总结的风险。
|
||||
- 设备执行任务时还没有把 `userAccount + deviceId + projectId + skillId` 作为统一授权输入。
|
||||
|
||||
## 产品目标
|
||||
|
||||
### 超级管理员
|
||||
|
||||
超级管理员可以:
|
||||
|
||||
- 创建、禁用、修改子账号。
|
||||
- 分配子账号可见和可控的电脑。
|
||||
- 查看所有电脑上的项目、线程和进展。
|
||||
- 通过主 Agent 汇总任意授权范围内的项目状态。
|
||||
- 给用户或设备分配可用 Skill。
|
||||
- 查看权限变更、Skill 执行、Computer Use 等高风险操作的审计日志。
|
||||
|
||||
### 子账号
|
||||
|
||||
子账号只能:
|
||||
|
||||
- 看到自己被授权的电脑。
|
||||
- 看到这些电脑下被授权的项目和线程。
|
||||
- 在授权范围内与线程或主 Agent 对话。
|
||||
- 使用被授权的 Skill。
|
||||
- 在获得 `computer.control` 权限时,才能通过主 Agent 或线程触发电脑控制能力。
|
||||
|
||||
### 主 Agent
|
||||
|
||||
主 Agent 必须:
|
||||
|
||||
- 只看当前用户有权访问的设备、项目、线程、Skill 和进展事件。
|
||||
- 在用户权限范围内回答问题、汇总进展或协调开发。
|
||||
- 不能因为超级管理员存在就默认给普通子账号展示全局上下文。
|
||||
- 在任务执行前带上当前用户身份,方便服务端和 local-agent 双重校验。
|
||||
|
||||
## 角色与能力模型
|
||||
|
||||
继续保留三类角色,但角色只定义默认上限,真实能力由授权决定:
|
||||
|
||||
- `highest_admin`:全局最高权限,可管理所有账号、设备、项目、Skill 和审计日志。
|
||||
- `admin`:范围管理员,只能管理被分配范围内的设备、项目和成员。
|
||||
- `member`:普通成员,只能使用被授予的能力。
|
||||
|
||||
第一阶段定义以下权限能力:
|
||||
|
||||
- `device.view`:查看设备状态。
|
||||
- `device.manage`:修改设备名称、执行模式、备注和授权。
|
||||
- `project.view`:查看项目、线程、项目目标、版本记录和进展。
|
||||
- `thread.chat`:向授权线程发送消息。
|
||||
- `master_agent.ask`:向主 Agent 提问,让主 Agent 在授权范围内总结或答疑。
|
||||
- `master_agent.takeover`:让主 Agent 接管授权线程或项目。
|
||||
- `computer.control`:触发 CLI、GUI、Browser、Computer Use 等电脑控制能力。
|
||||
- `skill.view`:查看设备上的 Skill 清单。
|
||||
- `skill.use`:在授权范围内调用指定 Skill。
|
||||
- `skill.manage`:给设备安装、更新或撤销 Skill。
|
||||
- `account.manage`:管理子账号和授权。
|
||||
- `audit.view`:查看权限与执行审计日志。
|
||||
|
||||
权限默认规则:
|
||||
|
||||
- `highest_admin` 默认拥有全部能力。
|
||||
- `admin` 默认没有全局能力,只能管理授权范围内资源。
|
||||
- `member` 默认无设备、项目、Skill 权限,必须显式授权。
|
||||
- 用户被授权某台设备后,默认继承该设备下项目和线程的只读可见权。
|
||||
- 线程聊天、主 Agent 接管、电脑控制、Skill 调用必须显式授权,不随只读可见自动开启。
|
||||
|
||||
## 授权数据模型
|
||||
|
||||
第一阶段继续使用 `data/boss-state.json` 文件存储,不立即引入数据库迁移。新增模型放入 `BossState`:
|
||||
|
||||
```ts
|
||||
type BossPermission =
|
||||
| "device.view"
|
||||
| "device.manage"
|
||||
| "project.view"
|
||||
| "thread.chat"
|
||||
| "master_agent.ask"
|
||||
| "master_agent.takeover"
|
||||
| "computer.control"
|
||||
| "skill.view"
|
||||
| "skill.use"
|
||||
| "skill.manage"
|
||||
| "account.manage"
|
||||
| "audit.view";
|
||||
|
||||
interface AccountDeviceGrant {
|
||||
grantId: string;
|
||||
account: string;
|
||||
deviceId: string;
|
||||
permissions: BossPermission[];
|
||||
grantedBy: string;
|
||||
grantedAt: string;
|
||||
expiresAt?: string;
|
||||
note?: string;
|
||||
}
|
||||
|
||||
interface AccountProjectGrant {
|
||||
grantId: string;
|
||||
account: string;
|
||||
projectId: string;
|
||||
deviceId?: string;
|
||||
permissions: BossPermission[];
|
||||
inheritFromDeviceGrant?: boolean;
|
||||
grantedBy: string;
|
||||
grantedAt: string;
|
||||
expiresAt?: string;
|
||||
note?: string;
|
||||
}
|
||||
|
||||
interface AccountSkillGrant {
|
||||
grantId: string;
|
||||
account: string;
|
||||
skillId: string;
|
||||
deviceId?: string;
|
||||
projectId?: string;
|
||||
permissions: BossPermission[];
|
||||
grantedBy: string;
|
||||
grantedAt: string;
|
||||
expiresAt?: string;
|
||||
note?: string;
|
||||
}
|
||||
|
||||
interface SkillCatalogEntry {
|
||||
skillId: string;
|
||||
name: string;
|
||||
description: string;
|
||||
sourceType: "gitea" | "skillhub" | "local";
|
||||
sourceUrl?: string;
|
||||
version?: string;
|
||||
checksum?: string;
|
||||
category?: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
interface PermissionAuditLog {
|
||||
auditId: string;
|
||||
actorAccount: string;
|
||||
action:
|
||||
| "grant.created"
|
||||
| "grant.updated"
|
||||
| "grant.revoked"
|
||||
| "skill.assigned"
|
||||
| "skill.revoked"
|
||||
| "task.authorized"
|
||||
| "task.denied";
|
||||
targetAccount?: string;
|
||||
deviceId?: string;
|
||||
projectId?: string;
|
||||
skillId?: string;
|
||||
permissions?: BossPermission[];
|
||||
detail?: string;
|
||||
createdAt: string;
|
||||
}
|
||||
```
|
||||
|
||||
## 权限判断规则
|
||||
|
||||
新增统一权限模块,例如 `src/lib/boss-permissions.ts`,所有 API、投影、主 Agent 和任务路由都调用它:
|
||||
|
||||
- `isHighestAdmin(session)`:最高管理员直接通过。
|
||||
- `canAccessDevice(state, session, deviceId, permission)`:判断设备权限。
|
||||
- `canAccessProject(state, session, projectId, permission)`:判断项目/线程权限。
|
||||
- `canUseSkill(state, session, skillId, { deviceId, projectId })`:判断 Skill 使用权限。
|
||||
- `filterDevicesForSession(state, session)`:设备列表过滤。
|
||||
- `filterProjectsForSession(state, session)`:会话和项目列表过滤。
|
||||
- `buildAuthorizedMasterAgentScope(state, session)`:主 Agent 上下文裁剪。
|
||||
|
||||
项目继承规则:
|
||||
|
||||
- 如果用户有某设备的 `device.view`,则默认可见该设备下项目。
|
||||
- 如果某项目绑定多个设备,只要用户有其中一台设备的 `device.view`,即可看到该项目的基础摘要。
|
||||
- 多设备群聊项目中,用户只能看到自己授权设备相关的参与线程和消息;全量群聊汇总需要项目级 `project.view`。
|
||||
- `thread.chat` 不从 `device.view` 自动继承,必须来自设备授权或项目授权里的显式能力。
|
||||
|
||||
## API 改造范围
|
||||
|
||||
第一阶段优先改这些接口,保证后端权限真的生效:
|
||||
|
||||
- `GET /api/v1/devices`
|
||||
- 最高管理员返回所有设备。
|
||||
- 子账号只返回授权设备。
|
||||
|
||||
- `GET /api/v1/conversations`
|
||||
- 最高管理员返回所有会话。
|
||||
- 子账号只返回授权项目/线程。
|
||||
|
||||
- `GET /api/v1/projects/[projectId]`
|
||||
- 无 `project.view` 返回 403。
|
||||
- 多设备项目按权限裁剪参与线程。
|
||||
|
||||
- `POST /api/v1/projects/[projectId]/messages`
|
||||
- 需要 `thread.chat` 或 `master_agent.ask`。
|
||||
- 如果消息触发接管或电脑控制,还需要额外权限。
|
||||
|
||||
- `GET /api/v1/devices/[deviceId]/skills`
|
||||
- 需要 `skill.view`。
|
||||
- 返回当前用户可见 Skill,而不是设备全部 Skill。
|
||||
|
||||
- `POST /api/v1/master-agent/tasks/claim`
|
||||
- local-agent 领取任务前校验任务绑定设备和用户授权。
|
||||
|
||||
- `POST /api/v1/master-agent/tasks/[taskId]/complete`
|
||||
- 写回消息时保留 `requestedByAccount`,便于审计和前台展示。
|
||||
|
||||
新增管理接口:
|
||||
|
||||
- `GET /api/v1/admin/access/accounts`
|
||||
- `POST /api/v1/admin/access/accounts`
|
||||
- `GET /api/v1/admin/access/grants`
|
||||
- `POST /api/v1/admin/access/grants`
|
||||
- `PATCH /api/v1/admin/access/grants/[grantId]`
|
||||
- `DELETE /api/v1/admin/access/grants/[grantId]`
|
||||
- `GET /api/v1/admin/skills/catalog`
|
||||
- `POST /api/v1/admin/skills/catalog/sync`
|
||||
- `POST /api/v1/admin/skills/assignments`
|
||||
- `DELETE /api/v1/admin/skills/assignments/[grantId]`
|
||||
- `GET /api/v1/admin/audit-logs`
|
||||
|
||||
这些管理接口第一阶段只允许 `highest_admin` 调用。后续再开放给具备 `account.manage` 或 `skill.manage` 的范围管理员。
|
||||
|
||||
## 主 Agent 改造
|
||||
|
||||
主 Agent 的输入上下文必须从“全局状态”改成“授权范围视图”:
|
||||
|
||||
- `highest_admin` 仍可看到所有设备、项目、线程、Skill。
|
||||
- 子账号只注入授权设备、授权项目、授权 Skill、授权进展事件。
|
||||
- 主 Agent 回复里如果用户要求查看未授权资源,应明确说“当前账号没有权限查看这台电脑或项目”。
|
||||
- 主 Agent 发起任务时必须写入:
|
||||
- `requestedByAccount`
|
||||
- `authorizedDeviceIds`
|
||||
- `authorizedProjectIds`
|
||||
- `authorizedSkillIds`
|
||||
- `requiredPermissions`
|
||||
|
||||
任务执行前的双重校验:
|
||||
|
||||
- 服务端创建任务前校验当前用户权限。
|
||||
- local-agent 领取任务时再校验该任务是否发给本设备,以及 Skill 是否被授权给请求用户。
|
||||
|
||||
## Skill 治理方案
|
||||
|
||||
不把 SkillHub 作为第一阶段权限核心。第一阶段采用:
|
||||
|
||||
- Skill 源:Gitea 私有仓库,例如 `krisolo/codex-skills`。
|
||||
- 安装位置:各电脑本机 `~/.codex/skills`。
|
||||
- 上报方式:local-agent 周期扫描并上报 `DeviceSkill[]`。
|
||||
- 授权位置:Boss 的 `AccountSkillGrant`。
|
||||
- 执行校验:Boss 和 local-agent 双重检查 `skill.use`。
|
||||
|
||||
SkillHub 接入作为第二阶段:
|
||||
|
||||
- 抽象 `SkillSource`,支持 `gitea / skillhub / local`。
|
||||
- 如果启用 SkillHub,Boss 从 SkillHub 拉取 Skill catalog。
|
||||
- Boss 仍负责账号、设备、项目和 Skill 授权。
|
||||
- SkillHub 只做 Skill 注册、版本、搜索和包分发,不直接决定 Boss 用户权限。
|
||||
|
||||
这样即使将来替换 SkillHub,也不会影响 Boss 的权限模型。
|
||||
|
||||
## Android 产品改造
|
||||
|
||||
第一阶段 Android 只做必要闭环,不做复杂企业后台:
|
||||
|
||||
- 我的页面新增或改造“账号与权限”入口。
|
||||
- 超级管理员可进入:
|
||||
- 子账号列表
|
||||
- 设备授权
|
||||
- Skill 授权
|
||||
- 审计日志
|
||||
- 子账号看到:
|
||||
- 当前账号角色
|
||||
- 已授权设备
|
||||
- 已授权 Skill
|
||||
- 无权限时的清晰提示
|
||||
- 会话页和设备页只显示服务端返回的授权内容,不在前端自行拼全量数据。
|
||||
- Skill 页面从“按设备查看本机 Skill 清单”升级为“按授权查看可用 Skill”。
|
||||
|
||||
## Web 产品改造
|
||||
|
||||
Web 第一阶段作为管理端主入口:
|
||||
|
||||
- `/me/access`:账号与权限总入口。
|
||||
- `/me/access/accounts`:子账号管理。
|
||||
- `/me/access/grants`:设备/项目/线程授权。
|
||||
- `/me/access/skills`:Skill 分配。
|
||||
- `/me/access/audit`:审计日志。
|
||||
|
||||
权限管理 UI 不追求复杂,优先做可理解、可验证:
|
||||
|
||||
- 选择账号。
|
||||
- 勾选设备。
|
||||
- 勾选权限能力。
|
||||
- 可选限制到项目或 Skill。
|
||||
- 保存后立即写审计日志。
|
||||
|
||||
## 数据迁移策略
|
||||
|
||||
为了不破坏现有单用户使用:
|
||||
|
||||
- 首次升级时自动给 `highest_admin` 全量权限,不需要写入显式 grant。
|
||||
- 当前已有设备保持原样。
|
||||
- 当前已有子账号如果没有 grant,则只能看到自己 `account` 匹配的设备。
|
||||
- 新增授权模型后,旧的 `device.account === session.account` 作为兼容 fallback。
|
||||
- 等多用户授权稳定后,再逐步把旧 fallback 收敛为显式 grant。
|
||||
|
||||
## 安全与审计
|
||||
|
||||
必须记录审计日志的动作:
|
||||
|
||||
- 创建、修改、禁用账号。
|
||||
- 授予或撤销设备权限。
|
||||
- 授予或撤销项目/线程权限。
|
||||
- 授予或撤销 Skill。
|
||||
- 触发 `computer.control`。
|
||||
- 主 Agent 因权限不足拒绝请求。
|
||||
- local-agent 因权限不匹配拒绝任务。
|
||||
|
||||
敏感数据规则:
|
||||
|
||||
- Skill 仓库 token、API Key、SSH 密钥不进入 `boss-state.json`。
|
||||
- 设备 token 继续只用于设备写入认证。
|
||||
- Skill 的安装源可以存仓库地址,但不能存私钥。
|
||||
- 审计日志不得记录完整 API Key、密码、Cookie 或系统提示词。
|
||||
|
||||
## 测试策略
|
||||
|
||||
后端单测:
|
||||
|
||||
- 最高管理员可见所有设备和项目。
|
||||
- 子账号只能看到授权设备。
|
||||
- 设备授权自动带来项目只读可见。
|
||||
- 没有 `thread.chat` 时不能发线程消息。
|
||||
- 没有 `skill.use` 时不能调用 Skill。
|
||||
- 主 Agent 上下文不会包含未授权项目。
|
||||
- 权限变更写入审计日志。
|
||||
|
||||
Android 单测:
|
||||
|
||||
- 子账号会话列表只渲染授权会话。
|
||||
- 无权限页面显示明确提示。
|
||||
- 超级管理员可以看到账号与权限入口。
|
||||
- 普通成员不能看到授权管理入口。
|
||||
- Skill 页面只显示授权 Skill。
|
||||
|
||||
集成验证:
|
||||
|
||||
- 用超级管理员创建子账号。
|
||||
- 给子账号只授权一台设备。
|
||||
- 子账号登录后只能看到这台设备和相关项目。
|
||||
- 子账号问主 Agent 全局状态时,主 Agent 只总结授权范围。
|
||||
- 子账号尝试控制未授权设备时返回 403。
|
||||
- 授权 Skill 后,子账号能看到并使用;撤销后立即不可用。
|
||||
|
||||
## 分阶段交付
|
||||
|
||||
### 第一阶段:权限底座可用
|
||||
|
||||
- 新增权限数据结构。
|
||||
- 新增 `boss-permissions` 模块。
|
||||
- 改造设备、会话、项目、消息、Skill 相关 API 的权限过滤。
|
||||
- 主 Agent 上下文按授权范围裁剪。
|
||||
- Android 和 Web 显示不越权。
|
||||
|
||||
### 第二阶段:授权管理 UI
|
||||
|
||||
- Web 端完成账号与权限管理。
|
||||
- Android 端完成基础查看和简单授权入口。
|
||||
- 审计日志可查看。
|
||||
|
||||
### 第三阶段:Skill 分发治理
|
||||
|
||||
- Gitea 私有 Skill 仓库同步。
|
||||
- Boss Skill catalog。
|
||||
- 按用户/设备/项目分配 Skill。
|
||||
- local-agent 执行前校验 Skill 授权。
|
||||
|
||||
### 第四阶段:SkillHub 可选接入
|
||||
|
||||
- 增加 SkillHub source adapter。
|
||||
- 同步 SkillHub catalog。
|
||||
- 保持 Boss 权限为最终裁决。
|
||||
- 支持 Gitea 与 SkillHub 并存。
|
||||
|
||||
## 不做范围
|
||||
|
||||
第一阶段不做:
|
||||
|
||||
- 组织架构树、部门、复杂审批流。
|
||||
- PostgreSQL 迁移。
|
||||
- 完整 SkillHub 私有部署。
|
||||
- 多租户账单。
|
||||
- 外部 SSO。
|
||||
- 细到文件级的代码仓库权限。
|
||||
|
||||
这些可以后续接入,但不能阻塞当前多用户设备控制模型落地。
|
||||
|
||||
Reference in New Issue
Block a user