- 人工智能
- AI 应用
- 交互助手
- AI Agent
【免费下载链接】ironclaw
IronClaw is an Agent OS focused on privacy, security and extensibility
IronClaw 作为以隐私、安全与可扩展性为核心的 Agent OS,其"Reborn"持久化体系(persistence rules)定义了整个工作区所有数据落盘的唯一范式:一个存储平面(RootFilesystem挂载目录)、一套原子性规则(共享的cas_updateCAS 路径)、以及严格的多后端行为一致性要求。本文以 .claude/rules/database.md 为骨架,结合 ironclaw_filesystem 的源码与测试,逐条讲解每条规则背后的设计意图、落地代码与验证手段,帮助你在为 IronClaw 新增任何持久化能力(新领域存储、挂载点、版本化写操作)时,一次做对、不踩并发与数据安全陷阱。
一、单一存储平面:一切持久化都活在挂载目录里
规则第一条即声明:新增持久化必须使用RootFilesystem挂载目录(mount catalog)。消费者拿到的是ScopedFilesystem与类型化的领域包装(typed domain wrappers),它们从不自己挑选后端,也不维护并行的后端分发 trait。
这意味着整个工作区——密钥(secrets)、租约(leases)、进程(processes)、记忆文档(memory documents)、项目文件、事件日志(event logs)、引擎状态、设置等——全部收敛到同一组操作之上:put/get/delete/list_dir/list_dir_page/query/ensure_index/stat/begin/append/tail。这一边界在 ironclaw_filesystem/CONTRACT.md 中被描述为"universal storage dispatch fabric"(通用存储分发织网)。
1.1 核心类型:一个 trait、一个 Entry、一个分发器
从 src/root.rs 可以看到,整个体系只有一个 traitRootFilesystem:
put/get:统一 Entry 平面的读写,put携带CasExpectation(比较交换前置条件)并返回新的RecordVersion;list_dir/list_dir_page:目录列举,后者是受max_entries约束、以子节点名做续传键(keyset)的分页;query/ensure_index:声明式索引与查询原语,SQL 字符串不越过这一边界;begin:供原生支持多键事务的后端使用;append/tail:日志形态挂载(event plane)的追加与回放。
同时还有CompositeRootFilesystem(src/catalog.rs)——它本身也是一个RootFilesystem,通过最长前缀(longest-prefix)匹配的挂载表把虚拟路径路由到具体后端。这里刻意没有"Backend / Dispatcher"两级拆分:分发器就是后端,唯一的 trait 贯穿始终。
ScopedFilesystem(src/scoped.rs)是调用作用域化的视图:高层存储在其构造函数中接收Arc<ScopedFilesystem<F>>,每次操作都会携带调用方的ResourceScope,由MountViewResolver解析出MountView并在任何后端分发之前先做权限校验。生产环境组合(composition)提供的解析器会把/secrets、/authorization等消费方别名解析到/tenants/<tenant>/users/<user>/<alias>这样的真实虚拟路径,租户隔离来自解析器而非每租户的存储缓存。
1.2 用命令验证核心面
规则文档给出的验证命令可以直接复现:
rg -n "trait RootFilesystem|struct ScopedFilesystem|fn cas_update" crates/substrates/ironclaw_filesystem三条命中分别对应 src/root.rs、src/scoped.rs 与 src/cas.rs,是"一个平面"最直接的代码证据。改动任何存储行为前,都应先读 ironclaw_filesystem/CONTRACT.md 与所属领域契约。
二、所有权划分:谁拥有什么,边界在哪
持久化体系的第二根支柱是清晰的职责归属,规则文档给出四层划分:
| 层 | 拥有什么 |
|---|---|
ironclaw_filesystem | 路径(paths)、挂载(mounts)、包含关系(containment)、版本(versions)、CAS |
| 领域 crate(domain crates) | 记录 schema、序列化、领域不变量 |
| 组合层(composition) | 选择具体后端并把它们挂载起来 |
| 产品工作流(product workflow) | 消费类型化存储,绝不穿透到后端 |
配套的硬性红线是:不要仅仅因为某个领域 DTO 或策略分支"需要被持久化",就把它们塞进 filesystem crate。领域 crate 拥有记录语法与服务契约,这是 crates/AGENTS.md 中"domains 是系统所知道的(记录)"这一分层模型在存储侧的延伸。
ironclaw_filesystem自身还坚持一套严格的依赖边界:从 CONTRACT.md 看,它只允许依赖ironclaw_host_api、ironclaw_safety(仅一个敏感路径脱敏谓词)、ironclaw_libsql_runtime(连接准入,它从不自建连接池)与ironclaw_observability,超出即属边界违规,由架构测试(ironclaw_architecture_tests)把关。
三、新增持久化操作的标准流程
规则文档给出了六步流程,配合"审查标志"使用:
- 定位领域所有者:在 crates/AGENTS.md 中找到领域 owner,阅读其本地契约;
- 在该领域 crate 内定义类型化操作与记录形态:
Entry的新 record kind、索引投影(indexed projections)属于消费方 crate,不属于 filesystem crate; - 复用现有作用域挂载:若领域确实是全新的,才通过 filesystem catalog 与 composition 装配新增挂载;
- 后端选择留在组合层:领域存储不得依据 PostgreSQL、libSQL 或本地文件系统配置做分支;
- 版本化文件系统变更用
cas_update;后端原生多语句不变量用后端事务; - 在公开领域操作或类型化包装接缝处补契约测试;涉及挂载选择、重启或跨域行为时,再补一个生产组合(production-composition)测试。
3.1 五个"红灯"审查标志
评审新代码时,以下任一情况都该停下来重新设计:
- 新增的
Store/Repositorytrait 唯一目的只是挑选后端(应直接使用RootFilesystem挂载); - 消费者直接打开具体后端连接(应只接触
ScopedFilesystem与类型化包装); - 类型化存储接收
RootFilesystem,而实际上ScopedFilesystem就足够(作用域与权限校验会被绕过); - 一次写入先读版本、随后不经过 CAS 直接覆盖(读改写必须走 CAS 路径);
- 在文件系统/后端 I/O 上横跨持有一个按记录粒度的 async mutex(进程内锁无法协调多进程,且会在爆发流量下造成运行时队头阻塞)。
其中最后一条正是后面要展开的 CAS 规则的核心动机。
四、原子性与并发:cas_update是唯一合法的读改写通道
规则文档的并发章节可以浓缩为一句话:每一个 read-modify-write 都必须走共享的有界 CAS 更新路径。不要持有进程内 mutex 跨越后端 I/O——它既无法协调多进程,又会在同作用域写者爆发时形成"车队"(convoy),把运行时拖垮。
4.1 为什么不能用 per-record mutex:一次真实事故
从 src/cas.rs 的模块文档可以读到完整来龙去脉:历史上每个存储用tokio::sync::Mutex包裹整个 CAS 循环并横跨.await持有,爆发流量下这些锁形成车队,最终造成 2026-06-24 的运行时 wedge 事故。PR #5142 从ironclaw_turns中移除了 mutex,验证了无锁模式:乐观 CAS 重试循环,配上有界重试、抖动指数退避与总超时。cas_update把这个经过验证的模式抽取成唯一被审计的实现,让每个存储都能把 mutex 换成它。
4.2cas_update的契约与常量
cas_update 是一个泛型助手,调用方提供三个闭包:
decode: Fn(&[u8]) -> Result<S, E>——把存储体反序列化为快照类型S;encode: Fn(&S) -> Result<Entry, E>——把下一版快照序列化为版本化Entry(在此设置kind/content_type);apply: FnMut(Option<S>) -> Future<...>——接收当前快照(记录缺失时是None),计算下一版快照与结果。apply必须幂等、可重入:每次 CAS 重试都会基于最新读到的快照重新调用它,因此绝不能修改外部状态。
循环语义如下:
- 读取
path当前的版本化快照并解码; - 运行
apply得到下一版快照与调用方定义的结果T(或错误E); - 快照未变化(或调用方显式返回 no-op)时直接返回结果,不写盘;
- 否则以读到的版本作为 CAS 前置条件写回(首次写入用
CasExpectation::Absent),或按CasApply::delete做条件删除; - 命中
FilesystemError::VersionMismatch则重读、以抖动指数退避重试;成功的条件删除也必须重读重放apply后再返回,防止 delete + recreate 的 ABA 周期提前满足调用方后置条件。
整个循环被FILESYSTEM_APPLY_TIMEOUT超时包裹——一个卡死的后端操作只消耗本次调用方的尝试配额,不会让无关调用方无限等待。相关常量(与ironclaw_turns参考实现逐字对齐,见 cas.rs):
| 常量 | 值 | 含义 |
|---|---|---|
FILESYSTEM_CAS_RETRIES | 32 | 竞争下的变更尝试上限 |
FILESYSTEM_APPLY_TIMEOUT | 15s | 整个循环(含全部重试)的截止时间 |
FILESYSTEM_CAS_BACKOFF_BASE | 2ms | 首次重试前的退避 |
FILESYSTEM_CAS_BACKOFF_MAX | 50ms | 指数退避的天花板 |
退避是"2ms 基数、每次翻倍、封顶 50ms,再加最多一个基数时长的抖动"(cas_retry_backoff),抖动由RandomState播种的尝试索引哈希产生,在粗时钟平台(VM、容器、Windows)上也不会塌缩为零。
4.3 四种变更结果与失败关闭
apply通过CasApply<S, T>选择四种类型化变更结果(cas.rs):
CasApply::new(snapshot, outcome)——正常写回;当快照与apply收到的一致时,走PartialEq快速路径跳过写盘;CasApply::no_op(snapshot, outcome)——无条件跳过写入,用于当前记录为None且不应创建空/默认记录的场景;CasApply::delete(snapshot, outcome)——按刚读到的版本条件删除,删除成功后还会再读一次并重跑apply,以验证调用方后置条件在 delete + recreate 的 ABA 周期下依然成立;CasApply::force_write(snapshot, outcome)——只绕过解码快照的相等性快速路径,仍然走相同的有界、能力门控的 CAS 重试;用于修复Entry侧车(sidecar,如索引投影元数据)的场景。
失败模式由CasUpdateError<E>承载(cas.rs):调用方apply的错误以Apply(E)原样透传不二次包裹;Timeout、RetriesExhausted、CasUnsupported与Backend(FilesystemError)由调用方用一个map_err闭包统一映射进自己的错误枚举。
失败关闭(fail-closed)能力门控是cas_update的底线:它绝不在非 CAS 后端上退化为盲写(CasExpectation::Any)。门控分两层:
- 预检(pre-flight):若后端声明了已知能力形态且不含
TxnCapability::Cas(或更丰富),直接返回CasUnsupported——这能在字节型挂载(如仅支持字节读写的DiskFilesystem)误配时于写操作前就拦住; - 操作时(op-time):能力形态为空/未知(即组合路由器)时延后到写操作,把
FilesystemError::Unsupported映射为同一个CasUnsupported。
无论哪层,结果都是拒绝而非无条件变更。所有生产存储挂载都解析到支持 CAS 的数据库/内存后端,因此失败关闭是正确且安全的默认。
4.4 并发验证:CAS 风暴测试
规则"冲突、重试耗尽、重启、部分失败行为必须在公开领域操作接缝测试"在 tests/concurrent_cas_storm.rs 中有直接落地:多线程 tokio 运行时上,每轮tokio::spawn16 个写者、重复 100 轮,全部并发对同一个快照路径做cas_update自增——每个cas_update必须成功(捕获后端错误缺陷),且最终计数必须等于WRITERS * ITERATIONS(=1600,捕获丢失更新缺陷)。该测试同时覆盖 in-memory、libSQL 与 PostgreSQL 三种后端:libSQL 变体是 #5466 的回归钉(此前"每次操作新建连接"策略在 C 库内间歇性失败、而被否决的"单共享连接"设计又会把 CAS 影响行数读回损坏成丢失更新);Postgres 变体用每次运行唯一的 UUID 前缀隔离共享数据库,且在后端不可达时优雅跳过。
配套的run_delete_storm进一步压测delete_if_version本身的并发:每轮重建共享路径到已知版本,16 个任务以精确版本竞争条件删除,断言每轮恰好一个赢家、其余全部观察到格式良好的NotFound,绝无丢失或重复删除。
五、多后端一致性:行为一致,而非仅 schema 一致
当某个领域显式支持多个持久化后端时,规则要求对排序、唯一性、时间戳、索引、事务、错误分类保持行为一致,并且把对抗性(adversarial)一致性用例放进共享的一致性套件(shared conformance suite),而不是为每个实现复制一份测试。规则文档明确强调:
Parity is behavioral, not merely schema-shaped.
即一致性是行为层面的,不是表结构层面的。需要对比的点包括:唯一性与索引、时间戳精度与排序、JSON/枚举序列化、事务回滚、并发写者结果、种子/默认记录、迁移回放、错误分类。修复某个实现时,要同时搜索它的同类实现与共享一致性套件中相同的模式。
从源码结构看,ironclaw_filesystem正是通过让全部后端实现同一个RootFilesystemtrait 来支撑这一要求的:DiskFilesystem、PostgresRootFilesystem、LibSqlRootFilesystem、InMemoryBackend、HsmBackend都是该 trait 的实现(可用rg -n "impl RootFilesystem for" crates/substrates/ironclaw_filesystem/src/复现),契约测试与 CAS 风暴测试直接以同一份测试体驱动多个后端,天然构成共享一致性套件。能力由BackendCapabilities/Capability/TxnCapability在挂载前声明(src/types.rs),挂载时校验拒绝无法满足消费方声明的后端(见 catalog.rs 的mount_dyn),运行期Unsupported只是兜底信号而非主要信号。
后端选择确实发生在组合层:例如 production_backend_assembly.rs 中,libSQL 路径用LibSqlRootFilesystem::from_runtime装配、Postgres 路径用PostgresRootFilesystem::new装配,领域存储只依赖类型化包装,不感知具体后端。数据库 schema 演进则集中由 migrations/ 目录管理(V1 到 V34,覆盖 wasm 版本化、用户身份、根文件系统条目与索引、工具作用域等主题),这与规则中"迁移回放属于一致性对比点"相互印证。
六、数据安全:模型输出与用户数据绝不静默丢弃
最后一条规则划定不可逾越的数据安全底线:
- 绝不静默丢弃模型输出、审计事件、对话记录(transcripts)或用户数据;
- 破坏性操作需要显式的产品契约(product contract)、授权(authorization)与作用域隔离测试;
- 存储错误在返回给边界时必须脱敏(sanitized boundary error),同时保留服务端原因以便排障——这与 src/types.rs 中"Display 输出刻意使用作用域/虚拟路径而非原始宿主路径,保护主机路径机密性"的约定一致;
- 缓存淘汰不等于持久删除:缓存未命中必须能从属主存储重新加载;任何保留/删除功能都需要显式的租户/用户作用域、可审计证据、重启安全行为,以及证明无关记录存活的测试。
七、落地清单与验证手段
把规则文档落到实际工作,可总结为一张检查清单:
- 定位 owner:先读 crates/AGENTS.md 与领域契约,再动手;
- 不建新 trait:一个
RootFilesystem足够,特殊情况优先考虑拓宽Entry或扩展 trait,而不是另立门户; - 读改写一律
cas_update:有界重试(32 次)、总超时(15s)、抖动退避(2ms 起、50ms 封顶)、失败关闭能力门控;绝不复制本地重试循环、绝不加 per-record mutex(历史遗留的本地循环见 CONTRACT.md 的迁移追踪,不是可模仿的先例); - 多语句不变量用后端事务:顺序 await 不构成原子性;同时有 Postgres 与 libSQL 实现时,用共享一致性套件验证相同的提交/回滚与并发行为;
- 索引投影是唯一可查询面:后端绝不解析
Entry::body来求值过滤器,一切可查询内容都在Entry::indexed;请求路径使用query_ordered+ 声明的精确/前缀索引 + keyset 游标,且容量/能力挂载时校验; - 补测试:在公开领域操作接缝补契约测试(可借助 fault.rs 的
FaultInjecting装饰器让真实存储走故障路径),涉及挂载选择/重启/跨域时补生产组合测试; - 验证命令:
cargo test -p ironclaw_filesystem跑完整 crate 测试,rg -n "impl RootFilesystem for" crates/substrates/ironclaw_filesystem/src/复核后端清单。
遵循这套规则,新增的持久化能力就会自然获得多进程安全的原子性、跨后端的行为一致性与可审计的数据安全,而这正是 IronClaw 以隐私和安全为核心的设计哲学在存储层最直接的体现。
- 人工智能
- AI 应用
- 交互助手
- AI Agent
【免费下载链接】ironclaw
IronClaw is an Agent OS focused on privacy, security and extensibility
相关推荐
AgentView 存储规则全解:SQLite 归档、DuckDB 镜像与多后端一致性设计
AgentView 存储规则全解:SQLite 归档、DuckDB 镜像与多后端一致性设计 AgentView 是一个本地优先(local first)的编码
AI 应用数据分析数据可视化可观测性Nacos Agent 存储规范深度解析:持久化模型、运行时发布与一致性契约
Nacos Agent 存储规范深度解析:持久化模型、运行时发布与一致性契约 导读 本文是 Agent Storage Spec https://link.gi
后端微服务配置中心服务注册发现云原生Slang 内存一致性模型实战指南:数据竞争、原子操作、内存屏障与多后端验证
Slang 内存一致性模型实战指南:数据竞争、原子操作、内存屏障与多后端验证 导读 本文以 Slang 语言参考文档 https://link.gitcode.
编译器图形学编程语言
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考