保险库授权
本文介绍本地守护进程授权服务和 SaaS Center 授权 HTTP 接口。以下本地 RPC 操作可通过共享核心、CLI 和 MCP 授权工具使用。
现有的 memories mcp grant、memories mcp revoke、Center 票据导入和保险库审计命令均使用更新后的核心检查。
角色与权限范围上限
| 保险库角色 | 最大授权范围 | 能否签发或委托? |
|---|---|---|
| 读取者 | read |
否 |
| 编辑者 | read_write |
否 |
| 管理员 | read_write_delete |
是 |
| 所有者 | allow_all |
是 |
省略权限范围意味着 read,而非 allow_all。这适用于本地授权、Center 票据签发、静态托管授权和成员创建。显式的旧版 write: true 仍选择 read_write;write: false 选择 read。write 仍是仅写权限范围。allow_all 必须显式选择并由所有者签发,即使它目前涵盖的普通资产内容操作与 read_write_delete 相同。
签发时会检查签发者当前的保险库角色;对于持有者调用方,还会检查用于签发的授权的权限范围。授权不能允许访问另一个保险库,不能超过任一上限,也不能晚于父授权到期。有效期为 1–60 分钟。请求更广泛或更长时间的权限会失败,而不会静默收窄所请求的权限范围。接收者必须已经是所选保险库的成员。内容授权可以向成员提供额外的内容访问权限,但不会改变其角色:持有广泛授权的编辑者或读取者仍然不能委托或审批请求。
授权记录公开 id、vault、scope、issued_by(成员 ID)、grantee、parent(签发授权 ID,或 null)、depth 和以 Unix 秒表示的 expiry。授权 ID 是独立于持有者令牌的随机不透明标识符。签发仅返回一次令牌;访问和授权列表从不包含令牌。授权和请求 ID 以及主体 ID 均为不透明的规范标识符;面向客户端的保险库 ID 保留常规的短 ID 映射。
本地管理员/所有者签发授权需要解锁保险库,不需要终端提权。撤销同样不需要提权,但只允许授权的签发者或其保险库的当前管理员/所有者执行。撤销会传递到后代。移除签发者或接收者,或将签发者的角色降低至其已签发权限范围以下,会在提交策略时使受影响的授权及其后代失效,包括通过同步导入的策略变更和重启后进行的变更。 授权持久化在加密的设备本地 Store 中,仅存储 SHA-256 持有者哈希和授权元数据;认证以常量时间比较哈希。撤销和后代移除随加密状态一起提交。锁定会丢弃内存中的授权和 Store 密钥,使所有授权在解锁前不可用;未到期授权在解锁或守护进程重启后恢复。绝对到期时间会持久化,到期授权及其后代在加载和守护进程定时处理时清除。授权不会通过保险库同步或恢复备份导出。
成员变更和设备审批除新增的管理员/所有者要求外,仍保留原有的提权检查。创建邀请现在要求在至少一个本地保险库中拥有管理员/所有者成员身份。没有保险库的调用方以及仅有编辑者/读取者成员身份的调用方均会被拒绝。管理员可以管理管理员成员身份,但不能添加、移除或更改所有者。所有权变更使用签名的所有权转移操作,并确保始终恰好有一个所有者。
权限请求
指定接收者与委托深度
守护进程和托管 Center 均拒绝向角色高于签发者有效角色的接收者签发授权,即使请求的内容权限范围仅为 read。Center 还会拒绝保留的 center-admin 接收者,包括此前已持久化且以其为主体的授权。
授权调用方使用的授权权限中,主体与真实签发者不同(subject != issued_by)。这些调用方可以创建请求,但不能审批、拒绝、取消或领取任何权限请求,包括自己的请求。守护进程返回 Unauthorized;Center 返回 HTTP 403。这些调用方不具备审批资格。自签发授权保留原有的请求规则;Center 领取仍要求使用请求者的持久成员凭据。持久 Center 成员持有者令牌代表成员权限,而非委托授权权限。
守护进程保留对请求者凭据的精确绑定:使用指定接收者的持有者令牌创建的请求,之后不能用另一个成员凭据领取。请使用将用于领取的成员凭据或自签发凭据重新创建此类请求。Center 则将领取绑定到请求者的持久成员持有者令牌。
两种实现均定义 pub const MAX_DELEGATION_DEPTH: u32 = 2。成员直接签发的授权深度为 1。使用授权权限签发时,包括领取时的延迟审批父授权,深度为 parent.depth + 1;深度 3 会被拒绝。depth 会持久化,并包含在授权摘要、签发响应、访问视图和已签发授权列表中。没有 depth 的旧记录根据父链重建(没有父授权的授权深度为 1)。父链缺失、循环或超过限制会导致拒绝,并在加载时清除。Center 内部的持久成员凭据深度为 0,不属于委托授权。
委托签发会沿父链保留真实签发者,而不会将指定接收者转换为自签发主体。确定调用方真实签发者时,也会检查现有的旧版链。
使用指定接收者授权记录的旧版延迟审批也会在领取时被拒绝;请从符合资格的成员权限获得新的审批。
破坏性变更:角色更高的接收者和 center-admin 接收者不再能获得授权;指定接收者的持有者令牌不再能参与权限决定、取消或领取;深度超过两个授权的链不再可用。请使用成员凭据进行权限决定和领取;当进一步委托会超过限制时,签发新的根授权。
任何当前成员都可以在保险库中提交自己的访问请求:
{
"op": "request_create",
"vault": "VAULT",
"target": {"type": "scope", "scope": "read_write"},
"reason": "Update the shared project notes",
"duration_minutes": 15,
"approvers": []
}
target 为 {"type":"scope","scope":"..."}(权限范围默认是 read)或 {"type":"role","role":"editor"}。理由、时长和审批者均为可选项。省略时长时,权限范围审批默认有效期为 30 分钟。角色变更是永久的:角色请求若包含时长会被拒绝,限时授权调用方不能审批永久角色变更。审批角色请求要求与直接成员角色变更相同的、绑定调用方的提权。所有者审批所有者角色请求时,会执行签名的所有权转移。
空审批者列表会向该保险库所有当前管理员/所有者成员广播。否则只有列出的成员 ID 可以作出决定;创建时列出的每个 ID 都必须是管理员/所有者成员,并在作出决定时重新检查资格。读取者和编辑者不能列为审批者。请求者不能审批或拒绝自己的请求,即使其为管理员/所有者或指定审批者;其仍可取消请求。权限范围/角色审批必须符合审批者的角色和持有者权限范围,权限范围到期时间必须符合父授权。超过上限的审批会使请求保持待处理状态。
请求从 pending 到 approved、rejected 或 cancelled 恰好发生一次状态转换。只有请求者可以取消待处理请求。权限范围审批记录授权参数,但不生成持有者令牌。请求者使用 request_claim 一次来生成并接收令牌;request_list 仅返回元数据。创建时间、代次和授权时长从领取开始,并受已记录的父授权到期时间限制。审批和领取状态在锁定/重启后保留,但被撤销或已到期的审批权限不能授权领取。请求状态或持久化 Store 状态中不保留明文令牌。已领取授权以哈希和元数据形式在锁定/重启后保留。请求和令牌不通过保险库同步导出。
request_list 返回调用方自己的请求(包括已决定的请求),以及其有资格决定的待处理请求。其他成员不能查看定向请求。一个保险库最多支持 1,000 个存储的请求;理由限制为 4,096 字节。本地守护进程和托管 Center 均最多允许 10,000 个授权同时处于有效状态。
待处理限制:一个请求者在每个保险库中最多可持有 20 个待处理请求,一个保险库最多有 200 个待处理请求;超过限制的创建会返回限制错误(托管 Center 为 HTTP 429)。待处理和已审批但未领取的请求在 7 天后到期(审计为 request.expire,操作者为 system);已决定的请求在最后一次转换 30 天后清理。
RPC 操作与接口
守护进程 JSON op 名称为:
| 操作 | 字段 | 权限 |
|---|---|---|
access_list |
vault |
管理员/所有者 |
grant_list |
vault |
管理员/所有者查看全部;其他当前成员仅查看自己签发的授权 |
grant_issue |
vault、minutes,可选 scope、grantee |
管理员/所有者 |
grant_revoke |
vault、id |
签发者或管理员/所有者 |
request_create |
vault、target,可选 reason、duration_minutes、approvers |
任何成员 |
request_list |
vault |
自己的请求或有资格决定的待处理请求 |
request_approve |
vault、id |
符合资格的管理员/所有者 |
request_claim |
vault、id |
已审批权限范围请求的原始请求者凭据;仅一次 |
request_reject |
vault、id,可选 reason |
符合资格的管理员/所有者 |
request_cancel |
vault、id |
待处理请求的请求者 |
audit |
vault、verify_only |
管理员/所有者 |
旧版 mcp_grant 和 mcp_revoke 仍受支持。持有者调用方只能针对其授权的保险库使用这些授权 RPC;这不允许 MCP 继承终端提权或访问存储的凭据。移动端 FFI 继续拒绝授权管理和新的权限请求操作;无需更改原生 ABI。
access_list 返回 principals,按 owner、admin、editor 和 reader 分组,以及有效的 grants、devices(保险库策略中已授权且未撤销的设备:device、所属 member、name;策略不记录创建时间)、pending_requests 和 approved_unclaimed_requests(每个包含 id、requester、权限范围/角色 target、approvers 列表或 "broadcast",以及以 Unix 秒表示的 created 和 expires)。可见性仍限于管理员/所有者。grant_list 返回 {"vault":"VAULT","grants":[...]},不包含主体分组。每个已签发授权包含 id、parent(根授权为 null)、issued_by、grantee、scope、vault、以 Unix 秒表示的 expiry、generation、depth,以及可用时以 Unix 秒表示的 created。本地代次为签发时已提交状态的代次,而非列出时的当前代次。已到期/撤销的授权不包含在内。管理员/所有者可列出保险库中的全部授权;其他当前成员仅可列出自己签发的授权,而不能列出仅由自己接收的授权。持有者调用方仍绑定到其授权的保险库。两种响应中均不出现令牌、持有者哈希或资产正文。守护进程 RPC、MCP grant_list 和 memories grant list
VAULT 共享此服务响应。
CLI 包装器实现以下精确命令(ID 是不透明的):
memories access list VAULT
memories grant issue VAULT --minutes N [--scope S] [--grantee ID]
memories grant list VAULT
memories grant revoke VAULT ID
memories request create VAULT [--scope S | --role R] [--reason T] [--duration M] [--approver ID]...
memories request list VAULT
memories request approve VAULT ID
memories request claim VAULT ID
memories request reject VAULT ID [--reason T]
memories request cancel VAULT ID
memories audit list VAULT [--verify-only]
旧版 memories audit VAULT [--verify] 语法仍受支持。权限范围默认是 read;没有时长的本地权限范围请求保留守护进程默认的 30 分钟审批时长。角色请求不能包含时长。输出为常规 CLI 响应封装中的 JSON。
托管 Center 的对应命令使用 HTTP 授权路由:
memories center access VAULT
memories center grant issue VAULT --minutes N [--scope S] [--grantee ID]
memories center grant list [VAULT]
memories center grant revoke ID
memories center request create VAULT [--scope S] [--reason T] [--duration M] [--approver ID]...
memories center request list [VAULT]
memories center request approve ID
memories center request claim ID
memories center request reject ID [--reason T]
memories center request cancel ID
memories center audit [--vault V] [--format json|jsonl]
这些命令接受 --center-url,否则使用 MEMORIES_CENTER_URL 或配置的 SaaS Center。若已设置,则使用 MEMORIES_CENTER_TOKEN,否则使用 MEMORIES_CENTER_ADMIN_TOKEN;权限失败不会使用另一持有者令牌重试。托管请求支持权限范围,不支持本地角色变更。权限范围默认是 read,请求时长默认是 30 分钟。JSONL 审计导出每行输出一个 JSON 条目,不含 CLI 响应封装;其他输出使用 JSON 响应封装。
守护进程 MCP 工具为 access_list、grant_issue、grant_list、grant_revoke、request_create、request_list、request_approve、request_claim、request_reject、request_cancel 和 audit_list。参数遵循上述 RPC 字段(授权签发使用 minutes,请求创建使用 target 和 duration_minutes,审计使用 verify_only)。省略授权权限范围或请求目标时默认是 read。每次调用均使用现有的 MCP 能力,绝不使用终端提权或托管 Center 持有者令牌。
SaaS Center 实现 GET /v1/access/{vault}、POST/GET /v1/grants、DELETE /v1/grants/{id}、POST/GET /v1/requests、POST /v1/requests/{id}/approve|claim|reject|cancel,以及仅限管理员/所有者的 GET /v1/audit。这些路由要求托管模式(--data)。精确的 HTTP 请求体、查询参数、响应和持久化文件参见 Center 文档。
SaaS Center 授权
托管成员具有可选的显式 role(reader、editor、admin、owner),独立于其内容 scope。省略 role 时,现有权限范围级别映射为读取者(read)、编辑者(write/read_write)、管理员(read_write_delete)或所有者(allow_all)。运营者持有者令牌在所有托管保险库中作为 center-admin 所有者。静态持有者主体使用 static:OPAQUE_ID ID 和相同的权限范围到角色映射。成员 ID 不能使用保留名称 center-admin。内容权限范围不会将读取者/编辑者接收者提升为管理角色。
POST /v1/grants 默认 scope 为 read,duration_minutes 为 30;minutes 是时长别名。签发要求管理员/所有者角色,角色、权限范围、保险库和到期时间边界与本地签发相同。响应使用 subject 表示接收者,并包含 parent、issued_by、expiry、generation 和 depth。授权令牌只出现在签发/领取响应中,绝不出现在审批、列表或审计中。
对于成员调用方,接收者默认是签发者;显式 grantee 必须标识所选保险库中的现有主体。运营者签发时若未提供 grantee,会创建由授权 ID 标识的匿名授权主体,绝不会创建 center-admin 主体。这也适用于托管票据签发。
每个托管成员都有持久化的代次计数器。生成授权时会固定签发者和接收者在签发时的代次;认证从不刷新它们。更改成员的权限范围、角色或保险库,或移除成员,仅递增该成员的代次。被移除成员的计数器会保留,因此重新创建同一 ID 不会重置其代次。
移除或更新会撤销由该成员签发或向其签发的授权及所有后代,并记录审计,而无关授权继续有效。公开的 generation 字段是接收者在签发时的代次。静态和运营者主体具有固定代次。持久成员凭据仅在显式策略转换时重新建立,而非在认证时建立。
托管生成授权在重启后保存在仅所有者可访问的 DIR/grants.json 中,仅包含 SHA-256 持有者哈希、固定代次、到期时间/父授权元数据和撤销记录。哈希比较使用常量时间。到期授权及其后代在加载时、定期以及认证/签发期间清除。锁定丢弃 Store 密钥并暂停授权,而不撤销它们;未到期授权和静态/持久凭据在解锁后恢复。持久化失败会锁定服务并要求运营者修复/重启,以免发生权限复活。
POST /v1/requests 接受 vault,以及可选的 scope、reason、duration_minutes(别名 duration)和 approvers。空或省略的 approvers 通过符合资格的管理员/所有者待处理请求列表广播;提供 ID 时则定向请求。GET /v1/requests 返回调用方拥有或处于其审批权限内的待处理请求,以及请求者已审批但未领取的请求,并可按 vault 过滤。审批和拒绝均要求所选审批者具备相应的角色、权限范围、保险库和到期时间权限。请求者不能审批或拒绝自己的请求,即使其为管理员/所有者或指定审批者;只有请求者可以取消。一次决定会串行化并持久化;重复操作返回 409。审批仅返回 request 元数据,包含 status: approved、claimed: false 以及经过验证的权限范围、时长和签发者边界;它不会生成授权或返回令牌。请求者随后使用其持久成员持有者令牌调用 POST /v1/requests/{id}/claim,收到 request 和一次性的 grant 响应。审批者、其他成员和同一主体的其他授权不能使用这些凭据领取。第二次领取返回 409。代次、创建时间和时长从领取开始,到期时间受审批权限已记录的到期时间限制。已撤销/到期的审批凭据或已更改的成员代次不能用于领取。
守护进程对权限范围请求使用相同的延迟生成流程:request_approve 存储审批边界,request_claim 一次性返回授权元数据及 token。经过本地 OS 认证的调用方仍必须是请求成员;MCP 必须提供创建请求的确切能力,而非同一主体的其他授权。列表从不返回令牌。角色审批仍是即时策略变更,没有可领取的授权。加密 Store 状态和 DIR/requests.json 仅包含请求元数据;授权仅持久化持有者哈希。启动时根据已验证的审计核对 Center 决定和领取,使中断的元数据保存不能重新开放已经决定或领取的请求。如果成功的领取响应丢失,令牌无法再次获取;请请求新的审批。
审计格式与验证
本地审计条目保留 vault、previous、sequence、timestamp、action、object、result、签名证书和签名。新增的可选顶层字段为 actor、subject(接收者)、scope、expiry、parent、reason、issued_by、via_grant 和 request_id。操作者标识执行操作的成员;签名者标识记录事件的守护进程设备。因此委托操作的操作者和签名用户可以不同。审计条目不包含密码、存储的凭据或持有者令牌。
使用授权权限生成的条目包含 issued_by(真实签发者)和 via_grant(调用方的授权 ID);actor 仍是授权主体。签发事件例外(mcp.grant / Center grant.issue)将真实签发者记录为 actor,将接收者记录为 subject,包括委托签发。两个可选字段均使用 serde 默认值,并在缺失时省略,以保留旧版签名审计和 Center JSONL 日志的序列化及验证。托管内容审计在共享 Store 中使用相同的归属信息。
审计是按设备签名的哈希链。previous 指向前一条序列化签名条目的 SHA-256 哈希,审计映射以这些哈希为键。Vault::verify 验证签名、哈希、序列连续性和现有的保险库历史不变量。旧记录省略所有新字段,并保留其精确的旧版签名字节和哈希。新字段使用 serde 默认值,并在缺失时省略;已填充的详情受签名保护。
事件包括 device.authorize、mcp.grant、mcp.grant.center、mcp.revoke、session.lock、session.unlock 和 request.create|request.approve|request.claim|request.reject|request.cancel,以及现有的成员和设备事件。显式锁定会传播审计持久化错误,但仍清除已解锁状态。生命周期/空闲锁定会尝试审计,并始终清除状态,即使持久化失败;不能为了保留审计记录而使秘密保持解锁。锁定状态下的失败无法签署审计。
SaaS Center 使用单独的、仅追加、权限模式为 0600 的 DIR/audit.jsonl SHA-256 链,而非本地签名审计格式。GET /v1/audit 要求管理员/所有者,接受 vault 和 format=json|jsonl,并在过滤/导出前验证完整链。绑定保险库的管理员不能读取另一个保险库的事件;运营者还可以查看全局锁定、解锁和注册事件。条目包含 sequence、timestamp、prev_hash、hash、action、actor、subject、scope、vault、expiry、parent、reason、request_id 和 object;可选详情为 null。哈希涵盖序列化元组 (sequence, timestamp, prev_hash, event_fields);首个条目的 prev_hash 为 null,之后为前一条目的哈希。
verify_audit_chain(path) 位于 crates/center/src/saas/audit.rs,检查哈希、序列连续性、前置哈希和完整 JSONL 行。启动时验证现有文件;追加/读取也将其链头与运行中进程已知的链头比较。事件包括 device.authorize、grant.issue、grant.revoke、member.add|member.set|member.remove、session.lock|session.unlock 和 request.create|request.approve|request.claim|request.reject|request.cancel。领取事件将请求者记录为操作者,将生成的授权 ID 记录为对象,绝不记录令牌。审计追加或请求/成员持久化失败会锁定托管服务,设置 restart_required,并阻止远程解锁,直到运营者修复并重启。设置 restart_required 时,授权变更和权限决定也会停止;运营者的只读检查仍可使用。这些无密钥链不能证明运营者没有重写整个日志或回滚所有文件;更强保证需要外部链头锚点。
威胁模型与行为变更
- 经过本地 OS 认证的调用方以已解锁设备的成员身份行动。同一用户的恶意进程仍处于现有端点信任边界内。
- 能力持有者令牌属于秘密;任何持有者都可以使用其受限权限,直到撤销、锁定或到期。持有者令牌不能扩大自身权限范围、超出其保险库、延长到期时间或绕过其主体的委托角色。
- 撤销不会清除此前已复制的明文或密钥,也无法立即触达离线对等端。策略和授权失效在已提交更新后策略的守护进程上生效。
- 哈希链能检测相对于已知状态的篡改,但不能检测数据库完整回滚或被攻陷设备遗漏的事件。要获得更强保证,仍需外部锚点和独立安全审计。
- 破坏性变更:未指定权限范围的签发为只读;编辑者/读取者不能生成授权或邀请;审计访问要求管理员/所有者,而非提权;成员变更使委托授权失效。新的管理员/所有者授权签发和签发者/管理员撤销不再要求提权。没有保险库的调用方创建邀请会被拒绝,不会静默将其视为管理员。
- 破坏性变更:审批不再生成或交付持有者令牌,请求列表也不再交付已审批的持有者令牌。请求者必须显式领取一次。此前已审批但未记录延迟审批边界的请求无法领取;请创建新请求。现有单独签发的授权继续遵循正常的到期/撤销规则。
限制参见 安全文档,仅票据 Center 和由运营者持有密钥的托管 Center 的独立信任边界参见 Center 文档。
权限请求限制、到期与保留
同时适用于守护进程(请求位于加密 Store 中)和 Center(requests.json):
MAX_PENDING_PER_REQUESTER_PER_VAULT = 20和MAX_PENDING_PER_VAULT = 200仅统计待处理请求。已决定、已取消、已到期和已审批但未领取的请求不计入这些限制。(这取代了此前统计所有曾创建请求的全局 1000 个请求上限。)超过限制时,守护进程返回限制错误,Center 返回 HTTP 429。PENDING_REQUEST_TTL = 7 days:超过此时长的待处理请求转换为expired状态。它会以request.expire、操作者system记录审计,且不能再被审批。已审批的权限范围请求若在审批后 7 天内未领取,也以相同方式到期,之后不能领取。REQUEST_RETENTION = 30 days:被拒绝、取消、到期或已领取的请求,在最后一次转换后经过此时长即被删除(使用decided时间戳;对于没有该字段的旧记录,使用created)。- 到期和清理惰性执行:守护进程在任何授权请求开始时执行,Center 在请求创建、列出、决定和领取时执行。启动时不执行。
- Center 启动核对会忽略已清理请求的审计条目,并接受
request.expire条目。expired请求必须有经过审计的到期记录。请求 ID 是从不复用的随机 128 位值,因此已清理的请求(包括已领取的请求)不再存在,也不能领取。因此,Center 不保留单独的墓碑记录集合。