Claudian 协作项目的权限转移引擎:LAN-to-Cloud 与 Cloud-to-LAN 的持久化状态机、Claim 保管与重启恢复机制
【免费下载链接】claudianAn Obsidian plugin that embeds Claude Code/Codex as an AI collaborator in your vault项目地址: https://gitcode.com/GitHub_Trending/cl/claudian
在 Claudian 的协作(Collab)功能中,项目的"权威"(即谁说了算的 Project authority)并不永远固定在一处:它既可能从局域网 Host(LAN Host)迁移到云端权威(LAN-to-Cloud),也可能从云端迁回某个局域网目标 Host(Cloud-to-LAN)。这篇指南基于仓库中该子系统的核心规约文档 authority-transfer/AGENTS.md,结合 AuthorityTransferModule、LanToCloudSourceCoordinator、AuthorityTransferPersistence 等源码,完整梳理这条权限转移链路的所有权边界、身份绑定模型、阶段状态机、离线成员的 claim 保管与核销,以及"随时 kill、随时可精确重放"的恢复机制。读完后你可以理解:一个多设备协作系统的权威迁移为何必须做到"任何时刻崩溃都不产生双写、不丢失成员资格、不重复核销凭证"。
1. 子系统的职责边界:什么归它管,什么不归它管
AGENTS.md 开篇即用 "Ownership" 一节划定了本模块的职责范围。该模块拥有的是:
- 生产环境的LAN-to-Cloud与Cloud-to-LAN两个方向的阶段策略(phase policy);
- 源端(source)/目标端(target)的编排(orchestration);
- 语义化检查点(checkpoint)的捕获与导入;
- transfer-claim 的保管(custody)与核销(redemption)收敛;
- 权威代际(authority-generation)的迁移、取消、终态结算与重启恢复。
同时,文档明确列出了几条"不要越界"的约束,这些约束直接对应仓库里可见的模块划分:
- 持久化必须走既有接缝。Project 本地的转移记录和"操作拥有的"产物,一律通过
CollabLocalProjectRepository持久化;启动枚举与恢复通过CollabProjectLifecycleSubsystem注册。文档强调"不要创建第二个 Vault 目录、第二个生命周期子系统、第二个 Project 索引或第二个物理状态属主"。在源码中,这一点体现为 AuthorityTransferRecovery 通过register(lifecycle)把自己作为CollabProjectLifecycleRecoveryStage(名为authority-transfers)和CollabProjectLifecycleDurableOwner(名为authority-transfer)注册进生命周期子系统,而不是自造一套启动扫描。 - 复用而非复制既有能力。Project 准入令牌、工作会话 drain/reset、Host 启动守卫、origin 与成员替换、索引重建、退役处理、本地 Keep/Delete 清理等,都复用其各自属主的实现,"不得修改它们的文件、不得绕开它们推断完成状态"。
- 开发夹具不是生产机制。文档特别指出
bootstrap/目录仍是"私有双客户端开发夹具",HostTransferPackage仍是物理 LAN-to-LAN 的 Host 交接——二者都不是生产传输、checkpoint、claim 或恢复机制。对照仓库结构可以看到,生产路径被单独放在 lan-to-cloud/ 和 cloud-to-lan/ 子目录中。 - 生命周期仲裁权不可旁路。在 Host 接受变更 Project 准入之前,必须取得既有
CollabProjectLifecycleSubsystem的 per-Project 生命周期仲裁器,并在每次恢复尝试中重新进入它。任何非终态的物理 Host 转移、Leave/cleanup、Retire/确认、私有 bootstrap、Manager 职责交接或另一次权限转移,都必须由其自身属主结算或阻塞;"协调器本地的检查永远不能取消或旁路它"。
这一条在 AuthorityTransferModule 中可以直接验证:sourceActiveService()返回的服务中,acceptLanToCloudTransferTarget和cancelProjectAuthorityTransfer两个会改变准入状态的操作,都被包在this.options.lifecycle.runExclusive(projectId, 'authority-transfer', 'continuation', ...)里执行,即所有关键写操作都要拿到生命周期子系统的排他锁。
2. 身份与权威模型:提议权、接受权与"在线不代表同意"
AGENTS.md 的 "Authority and identity" 一节定义了整套身份策略,这是理解整个子系统安全模型的关键:
- 任何已认证的活跃 LAN 成员都可以提出一个精确的规范化 Cloud URL 和稳定意图(proposal);但只有当前的 LAN Host能够接受该提议,并执行后续动作:结算待决准入、静默(quiesce)、捕获检查点、上传、保留 claim 保管权、提交源端让渡(source relinquishment)。
- "在线"永远不构成同意或身份证明,也不要求全员在线参与。
- 初始绑定是单向最小化的:LAN-to-Cloud 初期只通过被接受的源端证明绑定源 Host;Cloud-to-LAN 初期只通过其临时权威和本地生成的凭证绑定被选中的目标 Host。其余所有被导入的活跃成员在精确核销(exact claim redemption)之前都处于未绑定状态。
从 AuthorityTransferRecord.ts 的解码约束可以看到这套模型如何落到数据结构上。记录解码时强制要求:
localRole === 'source'必须与status.direction === 'lan-to-cloud'严格一致('source'只出现在 LAN-to-Cloud,'target'只出现在 Cloud-to-LAN);receiptVerifier(用于验证目标端签名核销收据的公共验证器)只允许出现在 LAN-to-Cloud 的 source 记录上,且其projectId/transferId必须与转移状态一致;sourceLanEndpoint只允许出现在已拥有(owned)的 source 记录中,且解码函数decodeSourceLanEndpoint要求它是https:协议、origin 形式的精确 URL——不允许用户名、密码、端口等任何多余成分。
2.1 离线成员的 claim:保管、确认与擦除
文档中最微妙的一段是 claim 的保管规则,对应"离线成员如何安全地拿到新身份":
在切换(cutover)之前,源端持久保留完整的原始 claim 批次,目标端持久确认其精确摘要(digest)。批次确认只证明保管(custody)。源端持有的 claim 只有在同一位原成员转发了一份精确的目标端签名核销收据、或经过有界过期之后,才会被擦除。
实现上这套规则由两层代码保证:
- AuthorityTransferPersistence 中的
retainClaimBatch/rotateClaimBatch先把批次以purpose: 'source-terminal'落盘并同步写入一个 commitment 记录(#persistClaimCommitment),保证"先有持久意图,再有物理产物";loadRetainedClaimBatch在重放前会用batchDigest(对规范化输入做 SHA-256)重新校验摘要,不一致即抛出durable-progress-recovery-required。 scrubClaimWithVerifiedReceipt的注释明确写道:"在方向属主把收据签名对照固定(pinned)的目标端密钥验证之后才持久化擦除。这个边界在删除原始 claim 之前重新验证每一条已持久化的转移与 claim 事实"——它逐条比对claimSha256、checkpointSha256、targetAuthorityGeneration、时间戳单调性,重复调用(同值收据)幂等返回。
另一条规则同样关键:
LAN 的 claimant 在只提交其哈希之前,自己生成并持久存储自己的凭证。claim 永远不用于认证云端入口、签发凭证、创建成员资格、选择角色、移动 refs 或绑定另一个成员。
这在 AuthorityTransferClaimantCoordinator 中得到精确落实:claim-retained阶段里,仅当targetAuthority.kind === 'lan'时调用createCredential()(默认实现是randomBytes(32).toString('base64url'),即 32 字节随机数的 base64url 编码)并推进到credential-persisted;到credential-persisted阶段向目标端提交核销请求时,LAN 方向只发送credentialHash(对 base64url 凭证做 SHA-256 hex),云方向则连哈希都不带。也就是说,明文凭证永远只存在于 claimant 自己机器上。
2.2 claimant 的运行时重建:full / target-only / local-only
文档最后一条身份规则描述了崩溃后如何重建 claimant 的运行时,这是整个恢复设计里信息量最大的一段。其核心结论是:
- Cloud-to-LAN 记录只保留重启所需的公开固定 LAN 目标端点与 CA 信任;任何 claimant 操作不得依赖之前绑定的活动运行时。
- 重建时只解析"对当前持久阶段仍然权威"的传输:核销前是完整双端(full:源+目标);源端确认不再需要之后是仅目标端(target-only);目标端成员资格已提交之后是仅本地(local-only)。
- 过期时,核销前的记录被直接擦除、不做远端重放;而已完成目标端核销的记录则通过本地收敛向前恢复,不再重试已过期的源端确认。
- 成员资格已收敛的记录是终态清理,永远不再解析任何传输。
AuthorityTransferModule 中的RecoveredAuthorityTransferClaimantBinding类型把上述规则变成了编译期穷举:五种绑定形态分别是 lan-to-cloud 的full/target-only、cloud-to-lan 的full/target-only,以及双向通用的local-only。resolveClaimantRuntime按记录方向与模式分发到bindLanToCloudClaimant、bindLanToCloudTargetOnlyClaimant、bindCloudToLanClaimant、bindCloudToLanTargetOnlyClaimant、bindLocalOnlyClaimant——后三者在源端或目标端被调用时抛出authority-transfer-claimant-source-unavailable之类的模块错误,从结构上杜绝了"已收敛记录重新发起远程调用"的可能。
3. 持久化记录层:状态机不变量与"重启栅栏"
转移的核心事实全部收敛在一个记录里:AuthorityTransferRecord。它的关键字段与设计决策如下:
schemaVersion:当前为2(AUTHORITY_TRANSFER_RECORD_SCHEMA_VERSION),v1 旧记录通过bindLegacyAuthorityTransferSourceOwner补上ownerInstallationKey(属主安装密钥)后升级。lifecycleOwnership: 'owned' | 'proposal':proposal 表示只有某成员提出了转移、尚未被 Host 接受;owned 表示 Host 已接受并绑定源端。解码约束规定 proposal 只允许出现在collecting-readiness阶段。restartFence: 'open' | 'permanent' | 'temporary':重启栅栏是本模块恢复语义的枢纽。expectedRestartFence是一个纯函数,由角色、所有权和状态推导:proposal 或"未让渡的源端在 cancelled/source-reopened 阶段"是open(允许 Host 重启源端权威);已持有relinquishmentProof(让渡证明)的源端是permanent(永久禁止重启源端);其余中间状态(含collecting-readiness、target-staged、claims-retained、cloud-relinquished、cancel-intent、target-invalidated)是temporary(暂时冻结)。解码器要求记录的restartFence字段必须与推导值一致,不一致直接判为无效记录。terminalResponder:仅出现在 LAN-to-Cloud 源端记录上,state为active/pending/expired。pending出现在claims-retained/repository-published阶段,持有让渡证明后变为active,到期后经expireAuthorityTransferTerminalResponder转为expired。它的作用是:迁移完成后,源端在一段有界时间内仍能以"终态响应者"身份回答关于此次转移的查询,到期则直接转入本地清理。stagingDirectoryName:必须严格等于`.claudian-authority-transfer-${status.transferId}`(由 LanToCloudSourceCoordinator 的stagingDirectory()生成,长度上限 160),即每个操作的暂存目录名由持久的 transferId 唯一确定,这是后续"只清理精确操作拥有的暂存目录"这一规则的地基。
3.1 状态迁移的唯一合法路径
assertAuthorityTransferTransition(AuthorityTransferRecord.ts)是两次持久化写入之间的"门禁",它的规则比一般状态机严格得多:
- 身份不可变:
projectId、transferId、operationIntentId、ownerInstallationKey、localRole、stagingDirectoryName、方向、源/目标权威的 kind 与 generation、targetUrl、createdAt、expiresAt在转移全程不可改变;sourceLanEndpoint一旦非空不可再变,从 null 变为非空仅允许发生在 proposal 阶段(即 Host 接受时);receiptVerifier一旦固定不可替换。 - 时间不可回退:
updatedAt只能前进。 - 阶段只能步进一格:新阶段必须是该方向阶段序列(
COLLAB_LAN_TO_CLOUD_TRANSFER_PHASES/COLLAB_CLOUD_TO_LAN_TRANSFER_PHASES,来自@claudian-collab/protocol)中前一阶段的后一位;例外是"取消序列"——未让渡时可以从合法的可取消阶段跳入cancel-intent,之后沿取消序列逐格推进。 - 让渡后禁止取消:已有
relinquishmentProof的记录一旦进入取消序列,抛出authority-transfer-cancellation is forbidden;checkpointSha256、claim 批次的batchRevision/batchSha256、让渡证明本身都只允许"从不设置到设置一次",之后冻结。
AuthorityTransferPersistence 的advance()在每次写入前重新执行完整解码 + 该断言,并把断言失败翻译为authority-transfer-stale或authority-transfer-cancellation-forbidden错误。此外整个持久层对每个projectId维护一条SerialTaskQueue(#projectQueues),保证同一项目的读写严格串行;close()会先 drain 所有队列再关闭。
4. LAN-to-Cloud 生产状态机:从 proposal 到终态
LanToCloudSourceCoordinator 是源端(LAN Host 侧)的生产编排器。它的三个入口是propose(任意已认证成员调用)、acceptAndTransfer(仅 Host 调用)和cancel,核心循环是resumeRecord。从源码可以还原出文档描述的完整阶段推进:
- proposal 落盘:
propose把云侧返回的初始状态(方向lan-to-cloud、阶段collecting-readiness)包成lifecycleOwnership: 'proposal'的记录写入持久层;operationIntentId直接取请求的idempotencyKey,stagingDirectoryName由 transferId 推导。 - Host 接受与端点固定(pin):
acceptAndTransfer先校验持久记录存在且 transferId 匹配,然后通过 effects 端口取得当前活动的源端 LAN 端点,把它写入记录(proposal → owned 的升级),并要求 effects 端口回算的接受请求与收到的请求逐字节一致(JSON.stringify比对),防止"接受的是另一个提议"。这里有一个非常细致的崩溃处理:如果持久化写入结果不明(ambiguous),协调器保留运行时端点 pin;只有当能从磁盘证明写入确实停留在 proposal 状态时,才调用releaseSourceEndpoint释放。这正是 AGENTS.md "端点固定"一节的实现:"LAN Host 接受时原子地固定活动的 source-active 路由,并在任何转移工作开始前持久记录该精确端点……端点在每一阶段中保持不可变,是 source-active 或终态源恢复可以绑定的唯一端点;模糊的保存结果保留运行时 pin,被证明发生在 fence 之前的取消则释放它。" - 静默与捕获(quiesce + capture):
collecting-readiness阶段中,先由 effects 端口执行acceptanceRequest/acceptProposal(要求云侧状态保持不动,否则报host-acceptance-status-mismatch),再调用capture()得到LanToCloudCapturedCheckpoint(含工件流、checkpointManifestSha256、源 Host 成员 ID、sourceProof),随后以幂等键${operationIntentId}-begin调云侧beginLanToCloudTransfer;finally中一律destroyAuthorityTransferArtifactBodies销毁本地工件体。文档对应规则:"捕获把一次静默的权威快照绑定到精确的允许仓库 ref/OID 清单和包拥有的 manifest";且逻辑检查点排除SQLite 字节、凭证、邀请密钥、CA 私钥、工作树、未发布文件、缓存与本地草稿/提交。 - 上传与验证:
source-quiesced阶段逐个uploadAuthorityTransferArtifact;checkpoint-received/checkpoint-validated主要是"读远端状态并推进"(readAndAdvance要求阶段必须前进,否则报authority-progress-pending)。 - claim 批次保管:在
checkpoint-validated阶段,若本地尚无保留批次,则用expectedBatchRevision/expectedBatchSha256乐观并发调用rotateTransferredMembershipClaims旋转批次,retainClaimBatch落盘;随后以幂等键${operationIntentId}-custody调acknowledgeTransferredMembershipClaimBatch取得目标端签名的保管收据,经acknowledgeClaimBatch存入 custody 记录(校验收据的committedAt在[createdAt, expiresAt)内、custodyAuthority与源权威 kind/generation 一致)。这就是"批次确认只证明保管"的落盘形态。 - 让渡(relinquishment):
repository-published阶段先由 effects 端口commitRelinquishmentFence生成让渡证明,再以${operationIntentId}-relinquish调commitLanToCloudRelinquishment。此后restartFence推导为permanent,文档规则"在让渡时点或之后,所有属主向前恢复,任何 Host 启动或本地恢复路径都不得重新打开源端"由assertAuthorityRestartAllowed/runWithAuthorityStartGuard执行——它们发现非 open 的栅栏就抛出durable-progress-recovery-required(permanent 时 reason 为authority-transfer-source-relinquished)。 - 终态激活:
completed时调用activateTerminal,把源端切换为"终态响应者";terminalResponder从pending转active,到期由expireTerminalResponder转expired并联动expireClaims把所有仍保留的 claim 置为expired。文档规则:"已完成 LAN-to-Cloud 的源恢复在持久的源端点上恢复终态响应者,并在其被视为已结算前收敛任何幸存的 LAN Host 成员资格;持久过期已过的响应者直接恢复为本地清理,永远不需要原 Cloud 目标可达。"
整条循环被包在最多 16 步的for循环里,每步都可从磁盘记录续跑(resume),不收敛则抛lan-to-cloud-recovery-did-not-converge。
5. 取消策略:可取消窗口、栅栏与"重开"
文档规定:"在源端让渡之前,取消必须证明目标未接受 fence。"源码中这一点体现为两处硬约束:
cancel()一开始就检查record.status.relinquishmentProof !== null,否则直接抛authority-transfer-cancellation-forbidden;- 取消请求带
expectedPhase(必须是COLLAB_AUTHORITY_TRANSFER_CANCELLABLE_PHASES之一,见cancellablePhase()),以${operationIntentId}-cancel幂等键调云侧cancelProjectAuthorityTransfer,之后沿取消序列(cancel-intent等阶段)逐格推进直至cancelled。
取消成功后源端调用reopenAfterCancellation重新开放原权威;若取消被证明发生在 fence 之前(即持久状态仍是 proposal 且sourceLanEndpoint为 null),协调器会释放运行时端点 pin(见acceptAndTransfer的 catch 分支)。而assertAuthorityTransferTransition保证一旦让渡证明存在,任何进入取消序列的迁移都会被拒绝——"让渡前可取消、让渡后单向"由此闭环。
6. 崩溃恢复:kill 在任何一个持久阶段之后
AGENTS.md 的 "Lifecycle and recovery" 一节和 "Verification" 一节合起来定义了这套系统的验收标准,值得完整展开:
- 代际规则:既有权威从 generation
1开始;目标端激活时恰好是source + 1。这在 AuthorityTransferModule 与协议层的sourceAuthority.generation/targetAuthority.generation字段中体现,且assertAuthorityTransferTransition把这两个 generation 列为不可变身份字段。 - 恢复驱动:AuthorityTransferRecovery 的
run()通过persistence.scanProjectCatalog()枚举所有可能持有转移状态的项目(枚举本身也走串行队列),先检查目录中是否存在损坏条目(invalidEntryCount > 0时记录一个durable-progress-recovery-required首错),然后逐个项目loadRecoveryOwnerRecord,用assertRecoveryOwner判定本机是否恢复属主(不是属主且原因是host-installation-recovery-owner-mismatch时跳过该项目——这支持了跨安装(Host 安装迁移)场景)。随后在lifecycle.runExclusive(projectId, 'authority-transfer', 'recovery', ...)内:先recoverInterruptedClaimCommitment(修复 custody 与 commitment 之间被中断的半写状态),再inspectLifecycleOwner分派——proposal状态只执行prepare(例如恢复源端路由),非终态才真正resume。 - TDD 验收矩阵:文档 "Verification" 一节要求"在每个协调器与属主持久化接缝上做 TDD;在每个持久阶段之后 kill恢复,并证明:精确重放、让渡后源端不重启、目标代际、无双写、离线成员保留的收敛、单 claim 收据擦除、只清理精确操作拥有的暂存目录"。仓库中对应的测试布局可以逐一映射到这些断言:
- integration/.../AuthorityTransferPersistence.test.ts:持久层行为(含 claim custody、commitment 修复、终态清理);
- integration/.../AuthorityTransferCheckpoint.test.ts:checkpoint 捕获/上传/清理;
- unit/.../recovery/AuthorityTransferRecovery.test.ts:恢复阶段的属主判定与分派;
- unit/.../AuthorityTransferClaimantCoordinator.test.ts:claimant 六阶段(
prepared → claim-retained → credential-persisted → target-claimed → source-acknowledged → membership-converged → completed)的逐步推进与过期分支; - unit/.../AuthorityTransferModule.test.ts 与 unit/.../ProductionAuthorityTransferCoordinators.test.ts:模块组装与生产 effects 绑定。
7. 物理产物处理:权限受限、原子提升、精确清理
文档对物理产物(工件体、密钥、证明、manifest、目标端私有状态)的要求非常具体,且与仓库实现一一对应:
- 权限受限 + 先意图后产物:"原始 claim 批次、checkpoint 工件、目标端暂存都是权限受限的、操作拥有的状态;创建之前有持久意图,允许取消/完成/过期时有精确清理"。持久层的实现就是
retainClaimBatch总是先写 custody 记录、再写 commitment 记录,任何后续操作都要求两者同时存在且一致(#assertClaimBatchOwner里requireCommitment分支)。 - 不持久化敏感明文:"绝不在转移记录或日志中持久化绝对路径、内容、claim、凭证、端点(敏感部分)或 Git 输出"。记录里保存的是
stagingDirectoryName(相对目录名)、哈希(checkpointSha256/batchSha256)与公开端点;凭证在 claimant 侧以本地记录保存但核销请求只传 SHA-256 哈希。 - 原子写协议:"源端密钥、证明、manifest 与目标端私有状态使用权限受限的临时文件、文件同步、原子提升、终文件验证,以及在平台允许时尽力同步父目录。重启只删除或替换精确的操作 partial,永远不把部分写入的终工件当作持久成功。"这正是 DurablePrivateFile.ts 的
writeDurablePrivateFile的完整流程:- 先
rm(partialPath, { force: true })清掉上一次崩溃留下的 partial; - 以
open(partialPath, 'wx', 0o600)创建排他的 600 权限临时文件(wx保证不会覆盖他人文件),写内容、handle.sync()落盘、关闭; rename(partialPath, filePath)原子提升;- 提升后
lstat验证终文件是普通文件且非符号链接(防链接攻击/残留符号链接),否则抛invalidFile; - 尽力打开父目录并
sync()(平台允许时,catch(() => undefined)吞掉不支持); - 任何失败都关闭句柄、删除 partial,并以调用方提供的错误工厂抛出——错误信息不泄露路径细节。
- 先
- 只清理自己的东西:终态清理入口
completeTerminalCleanup要求输入与持久记录完全一致(transferId、operationIntentId、stagingDirectoryName),且terminalResponder不得仍为 active/pending;其注释解释了顺序设计:"方向属主只在删除了精确的、由持久记录命名的操作暂存目录之后才调用本方法。先提交终态栅栏,再删除 claim 文件,这样崩溃至多留下可恢复的残余保管,而不会留下未提交的终态。"
8. 生产组装边界:模块只做"绑定",物理效果走端口
从源码结构看,AuthorityTransferModule 的定位是"Project 权威迁移的生产构造边界"(其类注释原文:Operation-specific transports and physical effects are bound before invocation or recovery; the durable records remain owned by the existing local repository)。具体分工是:
- 协调器(LanToCloudSourceCoordinator、CloudToLanTargetCoordinator)持有全部状态机逻辑,通过 options 中的 effects 端口(如
LanToCloudSourceEffects:acceptanceRequest、acceptProposal、capture、commitRelinquishmentFence、reopenAfterCancellation、activateTerminal、可选的sourceEndpoint/releaseSourceEndpoint)与物理世界交互; - 模块负责把正确的传输绑定给正确的协调器:
bindLanToCloudSource在注册运行时前检查同一项目没有冲突的方向绑定(authority-transfer-direction-runtime-conflict),并校验云会话确实支持authority-transfer与project-snapshot能力;bindCloudToLanTarget则要求expectedTargetUrl必填、target.prepareTarget必须可用——对应文档"Cloud-to-LAN 恢复直接对持久的目标 URL 准备监听器,绝不先打开未固定的回退监听器"; - 运行时注册表(
AuthorityTransferRuntimeRegistry、AuthorityTransferClaimantRuntimeRegistry)是懒解析的:按需调用resolveRuntime(record)时才assertRecoveryOwner+recoverCloudSession并重新绑定,解析失败会session.dispose()回滚,保证"记录在、进程不在"的冷启动路径与热路径走同一套绑定代码; - 源端路由服务
sourceActiveService()中,所有会改变准入状态的操作都要求actor.memberId === hostMemberId(否则authorization-denied),而只读查询getProjectAuthorityTransfer与requestLanToCloudTransfer对任何已认证成员开放——与第 2 节的身份模型完全一致。
9. 工程约束:UI 缺席与测试驱动
文档最后一节 "Verification" 还给出一条重要的阶段性约束:"在 Step 13 的能力门控入口工作之前,保持普通用户 UI 缺席。内部组合与测试驱动只能调用完整的、经过协商的操作。"这意味着当前版本中,权限转移只能由应用内部的组合层或测试代码触发(bindLanToCloudSource/bindCloudToLanTarget/bindLanToCloudClaimant/bindCloudToLanClaimant等入口),尚未暴露普通用户可见的界面;同时所有入口都只接受"完整协商后的操作"(完整的AcceptLanToCloudTransferTargetRequest、targetHost三元组 endpoint/CA 证书/CA 指纹等),不支持半构造的请求。
文档还要求恢复测试覆盖"每个持久阶段之后 kill"——即对collecting-readiness、源端静默后、checkpoint 验证后、批次保管后、让渡前后等每一个落盘点做进程杀死-重启-重放,验证精确重放幂等(所有云侧调用都携带由operationIntentId派生的幂等键,如-begin/-claims/-custody/-relinquish/-source-ack/-cancel后缀)。从 LanToCloudSourceCoordinator.resumeRecord 的结构可以确认该设计成立:每一步先读盘记录,按当前阶段决定要重放哪个幂等操作,然后推进并落盘——任何两步之间崩溃,重启后都从磁盘状态重新进入同一分支。
10. 小结:这套设计解决了什么问题
把 AGENTS.md 的规约与仓库实现对照后,可以归纳出 Claudian 权限转移引擎的三个核心工程答案:
- "谁可以做什么"用角色 + 阶段双重锁定。成员只能提议,Host 才能接受;
assertAuthorityTransferTransition把身份字段、阶段步进、取消窗口、让渡后单向性都变成了可自动验证的不变量,任何越权或回退在持久化层即被拒绝。 - "崩了怎么办"用持久记录 + 幂等键 + 串行队列闭环。每个方向一个协调器、每条写入一个幂等键、每个项目一条串行队列、每个落盘点一个 kill 恢复测试;
restartFence明确区分"可重启/暂时冻结/永久禁止"三态,让"让渡后源端永不重开"成为类型与数据双重保证。 - "离线成员怎么安全入伙"用本地生成凭证 + 哈希提交 + 目标端签名收据 + 有界过期。明文凭证只留在 claimant 本机,源端批次保管可精确重放,单条 claim 的擦除以目标端签名的核销收据为前提且可幂等重试,过期路径则无需任何远端可达性。
如果你想继续深入,建议按以下路径阅读:规约文档 AGENTS.md 与 CLAUDE.md(后者仅为前者的引用入口)、记录与状态机 AuthorityTransferRecord.ts、持久层 persistence/AuthorityTransferPersistence.ts、两个方向的协调器 lan-to-cloud/LanToCloudSourceCoordinator.ts 与 cloud-to-lan/CloudToLanTargetCoordinator.ts、claimant 协调器 claim/AuthorityTransferClaimantCoordinator.ts、恢复阶段 recovery/AuthorityTransferRecovery.ts 与 tests/integration/app/collab/authority-transfer/ 下的集成测试。
【免费下载链接】claudianAn Obsidian plugin that embeds Claude Code/Codex as an AI collaborator in your vault项目地址: https://gitcode.com/GitHub_Trending/cl/claudian
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考