Repository navigation
RFC:Account 范围的 Global 身份与用户授权协议 #6183
MaojiaSheng
started this conversation in
RFC
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
RFC:Account 范围的 Global 身份与用户授权协议
1. 摘要
本文提议在 OpenViking 的
ROOT、ADMIN、USER之外增加一种 account 范围的GLOBAL身份。GLOBAL 通常由 agent 使用,可以读写当前 account 的共享空间,并在用户明确授权后,只读访问该用户空间中的部分内容。用户通过放置在自己
viking://user/{user_id}/GLOBAL_POLICY.md的 OKF 协议文件声明授权范围,并可在 OKF Markdown 正文中向 GLOBAL agent 留言。协议的 YAML frontmatter 是服务端强制执行的权限上限;建议 GLOBAL agent 阅读并遵守 Markdown 正文中的用户说明,但显式读取完整协议不是访问前提,正文也不能扩大 frontmatter 授予的权限。本提案以隐私保护为首要原则:
privacy/**。sessions/**。viking://user容器上进行跨用户检索,只允许枚举用户列表。2. 状态与决策分类
本文使用以下标记区分已确认结论与仍需讨论的设计:
3. 背景与现状
OpenViking 当前通过
account_id和user_id建立两级租户边界:account是最外层租户边界,不同 account 的数据相互隔离。viking://resources和viking://agent是 account 内共享空间。viking://user/{user_id}是用户私有空间。当前实现已经具备部分基础能力,但不能直接满足本提案:
Role支持注册自定义角色和 privilege rank,但 rank 只用于 OAuth 等路径的降权判断,不会自动继承 ADMIN 权限。user或admin。viking://resources,不能描述用户根下的授权。相关现有文档:
4. 目标
4.1 功能目标
4.2 安全目标
5. 保持不变的问题
以下问题保持不变:
6. 术语
6.1 GLOBAL identity
由 ROOT 在某个 account 内创建的专用身份,通常由 agent 使用。一个 account 可以存在多个 GLOBAL identity。
6.2 Owner user
跨用户访问中,
viking://user/{user_id}/...所指向的目标用户。6.3 GLOBAL access policy
Owner user 放在自己用户根目录下的
GLOBAL_POLICY.md。该文件的 OKFtype为global-access-policy;frontmatter 声明可访问范围,Markdown 正文包含给 GLOBAL agent 的留言。6.4 共享空间
当前 account 内的
viking://resources与viking://agent。6.5 永久禁区
不允许任何用户协议解除的跨用户访问边界。v1 至少包括其他用户的
privacy/**和sessions/**。7. 设计原则
7.1 GLOBAL 是正交角色
已确认。 GLOBAL 不是
USER < ADMIN < ROOT线性层级中的一个新等级:因此实现不应仅依赖数值 rank 判断权限,而应使用显式 capability 或集中式授权决策。
7.2 Account 是不可跨越的边界
已确认。 GLOBAL 只能访问其身份所属 account。即使目标 URI 的文本相同,底层 account 不同也必须拒绝。
7.3 机器权限与用户留言分离
已确认。 OKF frontmatter 决定服务端可强制执行的最大权限;Markdown 正文表达用户对 agent 的补充要求。
正文可以要求 agent 比机器权限更加克制,但不能扩大机器权限,也不能覆盖系统、部署方或服务端安全规则。
7.4 Fail closed
已确认。 以下情况均按未授权处理:
7.5 控制面与数据面分离
已确认。 OpenViking 将角色分成控制面与数据面两类:
account_id数据空间内ROOT 不属于某个 account 的数据面角色,不能通过指定 account/user header、URI 或查询参数获得数据访问权。控制面能够创建或回收数据面身份,但这种管理能力不能被实现为 ROOT 对租户数据的隐式 bypass。
本 RFC 当前把持有 ROOT credential 的控制面视为可信边界。ROOT 创建或重置数据面 credential 可能使控制面获得可用于模拟数据面身份的 secret;如果未来要求 ROOT 主体也无法间接获得数据,则需要改用用户自行生成 secret、一次性 enrollment token 或公钥注册等更强的凭据签发模型。
本地
dev模式可以继续使用默认 ROOT 身份便利开发,但它不是生产安全模型,也不能作为 ROOT 具有数据权限的依据。7.6 用户是公开范围的唯一决定者
已确认。 GLOBAL 跨用户读取采用以下组合保证:
GLOBAL_POLICY.md时默认拒绝。GLOBAL_POLICY.md。因此,除协议定义的系统永久禁区外,只有用户本人能够决定自己的哪些普通数据可被 GLOBAL 读取。
7.7 GLOBAL 是 account 内可信的数据面身份
已确认。 GLOBAL 被视为 account 内的可信数据面身份,可以完整管理共享空间。这里的“可信”表示 GLOBAL 被允许维护 account 共享内容和访问控制,不表示它可以越过 account 边界、用户 policy 或永久禁区。
shared.manage,包含读取、创建、更新、移动、删除和 ACL manage,但每个资源上的实际操作仍受 effective ACL 约束。privacy/**、sessions/**或 policy 未允许的数据。8. 角色权限模型
8.1 数据权限矩阵
viking://resourcesshared.manage:读、创建、更新、移动、删除和 ACL manage;与其他数据面身份一样受 effective ACL 约束viking://agentshared.manage:读、创建、更新、移动和删除;skill 必须走正规 skill 管理入口privacy/**sessions/**ls viking://userviking://user上的检索、递归枚举或统计8.2 GLOBAL 自己的 sessions
本 RFC 中
sessions/**永久禁区是指 GLOBAL 对其他用户 session 数据的跨用户读取。GLOBAL 自己的 session 仍按自身用户身份正常使用。8.3 协议对所有 GLOBAL 统一生效
已确认。 一个用户的协议对同一 account 内所有 active GLOBAL identity 生效:
principals或 GLOBAL user ID 白名单。user_id和 credential fingerprint。8.4 复用 account user 记录
已确认。 GLOBAL identity 复用现有 account user 记录、credential registry 和 API Key 解析链路,通过
role=global与 USER/ADMIN 区分,不引入独立的 service identity 存储类型。account_id和自身user_id。role=global执行。8.5 Actor 与 effective user 双身份上下文
GLOBAL 访问某个明确用户的数据时,可以派生该用户的 effective identity discriminator,以复用现有 user-scoped URI、存储路径、向量 owner filter 和内容组织代码,但必须同时保留真实 actor:
actor_usereffective_user/target_userroledelegation_modeglobal_read,表示跨用户只读派生上下文policy_decision建议的处理顺序:
global_read派生上下文。effective user 只是数据 owner 判别,不授予目标 USER 的身份能力:
实现可以提供面向既有存储代码的 effective-user compatibility view,但原始 GLOBAL actor 必须始终可用且不可覆盖。任何只保留 target user、丢失 actor 的上下文转换都属于安全错误。
9. GLOBAL 身份生命周期
9.1 ROOT-only 管理
已确认。 GLOBAL identity 只能由 ROOT 管理。
9.2 ADMIN 可见性
已确认。 ADMIN 可以在当前 account 的用户/身份列表中看到 GLOBAL 的存在。
建议向 ADMIN 暴露:
account_iduser_idrole: global不向 ADMIN 暴露:
9.3 所有修改入口都必须保护
不能只在“创建 GLOBAL”接口上检查 ROOT。任何以 GLOBAL 为目标的管理操作都必须经过统一 object-level guard,包括:
10. 跨用户访问范围
10.1 数据访问必须绑定唯一 owner
已确认。 除非是非递归枚举
viking://user,每次跨用户数据访问都必须解析出唯一 owner user:viking://user/alice/resources/project-als viking://userviking://user用作 read、tree、find、search、grep、glob、递归 ls 或统计范围viking://user/alice/...和viking://user/bob/...v1 建议直接拒绝混合 owner 的批量请求,而不是在服务端静默拆分。
10.2 禁止跨用户大范围检索
已确认。 GLOBAL 不得以包含多个用户的目录为目标执行:
findsearchtreegrepgloblsGLOBAL 只允许通过非递归
ls viking://user枚举当前 account 的用户目录。返回项只包含用户目录名或user_id,不包含 role、abstract、overview、count、size、mtime、credential 状态以及任何用户空间内容。viking://user不得成为 GLOBAL 的检索、递归遍历或统计目标。GLOBAL 的默认检索范围也不得自动包含全部用户,只能包含共享空间和 GLOBAL 自己的用户空间。10.3 通用前置检查
已确认。 无论后续采用前置校验、后置过滤还是混合模式,所有跨用户操作都必须在访问数据前完成以下通用检查:
account_id一致。privacy/**、sessions/**或其他系统永久禁区。上述检查失败时直接拒绝,不能进入查询、遍历、读取或后台任务阶段。
10.4 按操作返回形态选择权限时机
已确认的基础分类:
read/get使用前置校验;find/search/tree/glob使用服务端后置过滤。在此基础上,v1 建议采用以下完整操作矩阵:
read、get、原始字节读取stat、attrs、raw vector IDabstract、overviewdownload、WebDAVGET/HEADlslsfind、search、deprecatedrecalltree、globgrepPROPFIND Depth: 0statPROPFIND Depth: 1/infinitylog/show/diff表中的
get指获取一个明确文件或对象内容的语义操作,不表示所有 HTTPGET路由。授权分类必须基于操作语义,而不是 HTTP method 或 CLI 命令拼写。整体流程如下:
flowchart TD A[规范化请求与识别唯一 owner] --> B{操作类型} B -- 单目标读取 --> C[加载并校验当前协议] C --> D{目标允许?} D -- 否 --> X[拒绝且不读取目标内容] D -- 是 --> E[执行读取并返回] B -- 集合查询或遍历 --> F[执行 account 范围内的查询或遍历] F --> G[按当前协议后置过滤候选] G --> H[只返回允许结果] B -- 派生 / 历史 / 复合 / 持续操作 --> I{操作矩阵允许且检查完整?} I -- 否 --> X I -- 是 --> J[按操作矩阵检查全部来源与目标] J --> H后置过滤必须发生在结果离开可信存储/授权边界之前。特别是
find和search,未授权候选不得进入外部 reranker、LLM、preview/context assembly 或面向调用者的结果统计;否则即使最终响应删除了对应条目,内容也已经发生泄露。集合操作的
offset、limit、count、has_more、排序和排名必须基于过滤后的可见结果。后置过滤不能导致 deny 结果占用用户可见分页额度,也不能通过聚合字段泄露被过滤节点的数量。find、search、tree、glob的范围级结果语义固定如下:GLOBAL_ACCESS_DENIED,不能伪装成空结果。目录
stat、ls、tree返回的count、abstract、overview 等字段可能聚合 deny 子树。无法按 policy 安全重算时,应省略相应字段或拒绝该派生字段,而不能返回原始聚合值。复合操作按普通读写语义逐动作判定:
cp从 policy 允许的用户路径复制到 GLOBAL 可写的共享路径时,可以执行。cp的 destination 位于其他用户目录时,因为 GLOBAL 无写权限而拒绝。mv还需要删除 source;因此 GLOBAL 不能把其他用户目录内容 move 出去。shared.manage执行。非递归
ls viking://user仍是单独定义的用户目录枚举能力,不读取各用户数据,也不受单个用户协议控制。10.5 未登记操作默认拒绝
实现应维护显式的 GLOBAL 跨用户操作注册表。只有在注册表中声明了授权模式、目标解析方式和过滤阶段的操作才能跨用户运行。
新增 REST API、MCP tool、SDK 聚合操作或后台任务如果没有登记,必须默认拒绝跨用户访问。默认拒绝是完整操作推断后的最后兜底,不代替对已知表面的逐项设计。
11. OKF 授权协议
11.1 文件位置与 OKF 类型
已确认。 协议文件使用保留名称
GLOBAL_POLICY.md,OKFtype使用global-access-policy:协议文件需要使用保留名称,避免被普通资源导入、语义处理或用户目录整理误操作。
11.2 文档结构
协议采用 OKF Markdown:严格 YAML frontmatter 加 Markdown 正文。
协议的最小结构如下;规则字段的扩展仍可继续讨论:
11.3 Frontmatter 语义
建议的最小模型:
typeglobal-access-policyschema_version1defaultallow/denyrulesrules[].effectallow/denyrules[].pathrules[].recursive第一版建议只支持精确路径和递归前缀,不支持 glob、正则表达式、否定模式或规则顺序覆盖。
11.4 规则优先级
已确认的原则: 系统永久禁区不能被协议覆盖。
建议使用固定优先级,而不是“最后一条规则获胜”:
defaultdefault: allow的含义是允许所有可共享内容,不包括privacy/**、sessions/**、系统内部路径以及其他不可共享数据。11.5 严格解析
安全策略不能复用现有 OKF sidecar 的“忽略未知字段”行为。建议:
.、..、反斜线、盘符、重复编码和 URI 逃逸。v1 不为
GLOBAL_POLICY.md额外定义专属的文件大小、frontmatter 大小、规则数量或 Markdown 正文长度限制。它仍受底层文件系统、请求体、内存保护和服务通用安全上限约束;触发通用上限、解析失败、type/schema 不受支持或内容无法识别时,整份 policy 按无效处理,并回退到默认拒绝,而不是尝试截断或宽松解释。11.6 Markdown 正文
已确认。 Markdown 正文用于用户给 GLOBAL agent 留言。建议 GLOBAL 阅读并遵守,但显式读取正文不是服务端授权的前置条件。
正文具有以下边界:
协议正文建议经过 owner 的 privacy sanitizer。GLOBAL 读取时只能看到持久化占位符,绝不能恢复 owner 的 privacy 值。
Privacy sanitizer 的职责边界建议如下:
读取和渲染行为建议保持简单:
11.7 存储与索引行为
协议 OKF 应当:
12. 协议阅读与强制执行
12.1 Agent 阅读建议
已确认。 不引入协议阅读握手、签名 Policy Ack、ETag precondition 或其他客户端授权 token。GLOBAL 不必显式读取完整
GLOBAL_POLICY.md才能发起访问。仍建议 GLOBAL agent 在需要理解用户留言或主动规划访问范围时读取该用户的
GLOBAL_POLICY.md:MCP tool description、CLI/SDK 文档和面向 agent 的运行时说明可以建议 agent 阅读协议,但不能把“已经读取”作为调用前提,也不能只把 agent 指向一个运行时不可访问的仓库文档。
12.2 服务端安全边界
GLOBAL 是否阅读协议不属于服务端安全前提。即使 agent 未阅读、误解或忽略协议,服务端仍必须拒绝越权请求:
read、get在请求入口执行前置校验。find、search由数据库/检索层对候选结果执行后置过滤。tree、glob由存储访问层对遍历结果执行后置过滤。stat、attrs、download 等单目标读取采用前置校验;ls、WebDAVPROPFIND等集合操作采用前置根目录加后置成员过滤。客户端不提交 policy hash、acknowledgment 或其他授权证明;服务端自行加载当前协议或使用可验证、可失效的服务端缓存。
12.3 策略缓存与失效上界
已确认。 GLOBAL policy 可以使用服务端缓存,但旧授权的最大继续生效时间必须是明确、有界的分钟级窗口:
GLOBAL_POLICY.md的权威写入、替换或删除成功时开始计算。永久禁区不依赖用户 policy 缓存;
privacy/**、sessions/**等硬边界始终立即执行。13. 集中式授权架构
13.1 不在各路由散落角色判断
GLOBAL 是正交角色,不能通过在各处追加
Role.GLOBAL白名单完成。建议引入集中式授权决策:决策结果至少包含:
13.2 Capability 模型
候选 capability:
shared.readshared.manageuser.self.read_writeuser.other.read_if_allowedaccount.manage_usersprivacy.readshared.manage包含共享空间的 create、update、move、delete 和 ACL manage,不再拆分单独的 shared delete 或 ACL manage capability;它表示角色能力上限,不绕过资源 effective ACL。GLOBAL 永远不拥有account.manage_users,也不通过跨用户路径获得privacy.read或 session 读取能力。13.3 文件系统与向量检索一致
以下层必须复用同一授权语义:
向量层继续首先执行 account filter,再按当前
GLOBAL_POLICY.md对find/search候选做后置过滤。文件系统和其他表面按照第 10.4 节的操作矩阵执行前置校验、后置过滤、派生来源检查或复合检查。所有入口复用同一个 operation registry 和 policy evaluator;未登记操作默认拒绝跨用户访问。14. Privacy 与 Session 永久隔离
14.1 Privacy
GLOBAL 对其他用户的以下表面永久无权访问:
privacy/**原始文件跨用户读取
SKILL.md时,只能返回持久化的 privacy 占位符:14.2 Sessions
GLOBAL 对其他用户的以下表面永久无权访问:
sessions/**文件与目录14.3 Derived content
仅按目标文件路径判断不足以防止派生泄露:
建议 v1 对包含被拒绝后代的跨用户递归目标采用保守策略:拒绝该范围的摘要或大范围递归操作,并要求 GLOBAL 缩小到一个完整允许且不包含永久禁区的前缀。
15. 共享空间与 ACL
已确认。 GLOBAL 是 account 内可信的数据面身份,对当前 account 的
viking://resources和viking://agent拥有shared.manage:viking://resources的 ACL,不需要单独 capability,但目标资源的 effective ACL 必须已经授予 GLOBAL 对应 principalmanage。viking://resources或viking://agentnamespace 根本身,这属于系统结构保护,不是权限不足。viking://agent/skills的变更必须走 add/update/delete skill 等正规入口,不能用普通 write/rm 绕过索引、privacy 和元数据维护。GLOBAL 在 ACL 语义上没有特殊性:
user:{global_user_id}、user:*和服务端解析的 group principals 参与 ACL 计算。role=global获得隐式 manage、ACL bypass 或 ADMIN 权限。16. 各认证模式
16.1 API Key
16.2 Trusted
X-OpenViking-Role: global临时声明 GLOBAL。16.3 OIDC / LDAP
16.4 OAuth 与签名 token
为了开发本需求,我们已经做了一些调整:
17. 审计与用户透明度
每次跨用户访问应记录:
不得在审计中记录文件正文、privacy 值或包含秘密的检索 query。
建议 owner user 可以查看针对自己空间的 GLOBAL 访问记录。ADMIN 可以看到 GLOBAL 身份存在;是否可查看完整跨用户访问审计仍需单独确定。
18. 错误与信息泄露
需要为以下情况提供稳定错误码:
GLOBAL_POLICY_MISSINGGLOBAL_POLICY_INVALIDGLOBAL_POLICY_SCHEMA_UNSUPPORTEDGLOBAL_CROSS_USER_SCOPE_REQUIREDGLOBAL_MIXED_OWNER_REQUESTGLOBAL_PROTECTED_PATHGLOBAL_ACCESS_DENIED为了减少路径与内容存在性泄露,需要进一步决定:
GLOBAL_POLICY.md本身时必须被允许;仍需确定是否隐藏“用户不存在”与“用户存在但未放置协议”的差异。19. 兼容性与迁移
19.1 现有角色
19.2 账号存储
账号存储需要接受
globalrole,但必须保持 ROOT-only 生命周期约束。File/MySQL 等 provider 必须行为一致。19.3 客户端与 Studio
19.4 协议缺省
上线 GLOBAL 功能后,所有现有用户默认没有协议,因此默认拒绝 GLOBAL 跨用户读取。不会因为升级自动共享任何用户数据。
20. 实现范围参考
以下是可能受影响的现有模块,最终以实现计划为准:
openviking/server/identity.pyopenviking/server/api_keys/openviking/server/account_stores/openviking/server/auth/openviking/server/routers/admin.pyopenviking/core/namespace.pyopenviking/core/retrieval_targets.pyopenviking/storage/viking_fs/openviking/storage/viking_vector_index_backend.pyopenviking/service/fs_service.pyopenviking/server/mcp_endpoint.pyopenviking/observability/usage_audit/openviking_cli/web-studio/本列表不是授权在上述模块中直接加入分散的 role 特判;实现应优先建立集中式 policy service 和授权决策。
21. 测试计划
21.1 身份与生命周期
role=globalidentity。21.2 双身份上下文
global_read。21.3 Account 隔离
account_id。21.4 协议解析
default: allow不能覆盖永久禁区。GLOBAL_POLICY.md。GLOBAL_POLICY.md。21.5 单 owner 限制
ls viking://user成功,且只返回最小用户目录列表。viking://user上的 find/search/tree/grep/glob/recursive ls 和统计均拒绝。21.6 前置校验与后置过滤
read/get在未授权时不读取目标内容并返回拒绝。stat/attrs在未授权时不返回存在性或 metadata,目录 count 不包含 deny 子树。abstract/overview无法证明派生来源全部可见时拒绝返回。download在签发和取用阶段都按当前 policy 校验。ls先校验目录,再过滤子项;offset、limit、count 和 has_more 基于过滤后的可见结果。find/search的未授权候选不出现在结果中。tree/glob的未授权节点不出现在结果中。find/search/tree/glob返回GLOBAL_ACCESS_DENIED,合法范围内无匹配才返回成功空结果。grep只返回允许文件中的命中。shared.manage成功。find/search的未授权候选不进入外部 reranker、LLM、preview/context assembly 或响应统计。21.7 Privacy 与 Sessions
21.8 共享空间
viking://resources或viking://agentnamespace 根。21.9 审计
21.10 安全测试
shared.manage与 effective ACL 必须同时允许 read/write/delete/ACL-manage 操作。22. 被否决或暂不采用的方案
22.1 仅靠 robots.txt 式自觉遵守
否决。Advisory 文本不能作为隐私安全边界,恶意或错误客户端可以忽略。
22.2 JSON-only 协议
否决。JSON 不适合同时承载结构化权限与用户给 agent 的可读留言。本 RFC 采用 OKF frontmatter + Markdown body。
22.3 仅扩展现有 resources ACL
暂不采用。现有 ACL 只覆盖共享 resources,不能表达用户根协议、agent 留言、永久禁区和单 owner 检索约束。
22.4 把 GLOBAL 当作 ADMIN 的更高等级
否决。GLOBAL 与 ADMIN 权限正交,线性 rank 会导致权限继承错误。
22.5 仅在客户端或响应序列化末端过滤
否决。v1 允许对
find、search、tree、glob做服务端后置过滤,但过滤必须位于可信存储/授权边界内,并早于外部 reranker、LLM、preview/context assembly、响应统计和响应序列化。不能把未授权数据交给客户端后再过滤。22.6 协议阅读握手与签名 Policy Ack
否决。它会引入额外 token 生命周期、跨 REST/MCP/CLI 的状态同步和缓存复杂度,但不能证明 agent 真正理解了正文。GLOBAL 可以按需读取
GLOBAL_POLICY.md;服务端以当前协议直接约束文件系统和数据库访问。24. 已确认决策摘要
截至本 Draft:
GLOBAL_POLICY.md,OKFtype为global-access-policy,schema_version为1。ls viking://user枚举当前 account 的最小用户目录列表。viking://user上进行跨用户检索、递归遍历或统计。privacy/**。sessions/**。read/get使用前置校验;find/search/tree/glob使用服务端后置过滤。account_id内的数据面角色。GLOBAL_POLICY.md内容;默认拒绝与 GLOBAL 禁止写 user 目录共同防止他人替用户开放数据。stat等单目标 metadata 读取采用前置校验;ls等集合操作采用前置根目录加后置成员过滤;派生、导出、历史和复合操作按显式矩阵执行。shared.manage包含共享内容的创建、更新、移动、删除和 ACL manage,不需要额外 delete/ACL capability;namespace 根仍受结构保护。global_read。find/search/tree/glob返回GLOBAL_ACCESS_DENIED;只有合法范围内无匹配时才返回成功空结果。shared.manage与 effective ACL 必须同时允许操作。All reactions