深度解析:组件模型 ABI、deny-by-default 沙箱与资源计量)
IronClaw WASM 执行通道lane深度解析组件模型 ABI、deny-by-default 沙箱与资源计量【免费下载链接】ironclawIronClaw is an Agent OS focused on privacy, security and extensibility项目地址: https://gitcode.com/gh_mirrors/iro/ironclawIronClaw 的ironclaw_wasm是其 WASM 组件执行通道lane负责对已经过授权选择的 WASM 组件执行加载 → 编译 → 校验 → 计量 → 执行的完整生命周期并在 deny-by-default 的主机导入策略下为每次调用提供全新的 store 以及 fuel / epoch / 内存 / 表 / 实例等资源上限。本文以 crates/lanes/ironclaw_wasm/README.md 为主线结合仓库源码、WIT 契约与测试用例讲解该通道的公共 API、安全模型、wasm_sandbox_core领域无关沙箱原语以及 tool/channel 两套组件模型 ABI 的版本化约定。读完你既能直接驱动WitToolRuntime执行一个 WIT 工具组件也能理解 IronClaw 如何让整个工作区只有这一个通道可以持有 WASM 引擎。一、通道定位lanes / runtimes 家族的唯一 WASM 引擎持有者ironclaw_wasm属于lanes家族、runtimes层级清单位于 crates/lanes/ironclaw_wasm/Cargo.toml。它的职责边界非常明确只负责执行已经被授权选定的 WASM 组件不负责模型能看到哪些工具/通道——那是 kernel/capability 分发ironclaw_capabilities的职责。在模块组织上本 crate 同时承担两个角色WIT 工具运行时面向wit/tool.witnear:agent0.4.1的组件执行引擎领域无关沙箱原语wasm_sandbox_core模块封装 Wasmtime 引擎配置、epoch 计时器、最小 WASI p2 linker、限额与 store 辅助函数。README 明确写道No other crate in the workspace needs a WASM engine, and this lane (with its sibling limiter) is the only one permitted to hold one.——即本通道连同其兄弟 crateironclaw_wasm_limiter是工作区中唯一被允许持有 WASM 引擎的 crate。这一约束由架构测试强制保障见下文不变式小节。何时使用 / 何时不用场景结论一个已授权的调用目标指向 WASM 组件使用ironclaw_wasm主机需要领域无关的沙箱原语wasm_sandbox_core使用ironclaw_wasm决定模型看到哪些工具/通道不要用本 crate走 kernel/capability 分发需要另一个 wasmtime 主机的资源上限使用ironclaw_wasm_limiter只需要 ABI 文本读ironclaw_wasm::TOOL_WIT不要在本 crate 的wit/目录里另起include_str!二、公共 API 面Public surfaceironclaw_wasm的公共导出集中在 src/lib.rs共分四组1. 运行时核心类型WitToolRuntimeWIT 工具运行时Clone代价很低Engine内部引用计数 小型Clone配置克隆体可以移入tokio::task::spawn_blocking执行同步 guest 调用而不重建引擎见 src/runtime.rsWitToolHost主机导入集合的组装点提供deny_all()默认构造WitToolRequest/WitToolExecution/PreparedWitTool请求、执行结果与预编译工具WitToolRuntimeConfig运行时配置核心字段default_limits: SandboxLimitsWIT_TOOL_VERSION0.4.1与TOOL_WITABI 版本常量与 WIT 源文本src/config.rs错误类型WasmError/WasmHostErrorsrc/error.rs。WasmError完整覆盖运行时各阶段pub enum WasmError { EngineCreationFailed(String), // 引擎创建失败 CompilationFailed(String), // 组件编译失败 StoreConfiguration(String), // store 配置失败 LinkerConfiguration(String), // linker 配置失败 InstantiationFailed(String), // 实例化失败 UnsupportedContract(String), // 不支持的 WIT 契约版本 ExecutionFailed { message: String, usage: ResourceUsage, logs: VecWasmLogRecord }, InvalidSchema(String), // schema 导出不是合法 JSON 对象 }WasmHostError则描述注入主机服务返回的错误包含AuthRequired、Denied、Network { message, code, request_sent }、Timeout、Unavailable、Failed、FailedAfterRequestSent七个变体其中request_was_sent()与stable_code()两个内部方法分别用于网络计量与错误码透传。2. Host-import trait全部 deny-by-default每个 trait 都提供Deny*拒绝、Recording*测试录制、System*系统实现等实现但不是完整矩阵——README 建议用rg -n pub struct (Deny|Recording|System) src/重新推导。从 src/host.rs 可以看到典型形态pub trait WasmHostHttp: Send Sync { fn request(self, request: WasmHttpRequest) - ResultWasmHttpResponse, WasmHostError; } /// Fail-closed HTTP host service. #[derive(Debug, Default)] pub struct DenyWasmHostHttp; impl WasmHostHttp for DenyWasmHostHttp { fn request(self, _request: WasmHttpRequest) - ResultWasmHttpResponse, WasmHostError { Err(WasmHostError::Unavailable(WASM HTTP egress is not configured.to_string())) } }WasmHostHttp的文档注释揭示了一个关键设计生产组装应把该 trait 接到共享的 Reborn 运行时 egress 服务上在该服务出现之前默认实现拒绝一切请求使 WASM 无法直接进行网络 I/O。另一个值得注意的细节是WasmHostHttp是同步 traitfn request(...) - Result...因此运行时在调用前会把WasmHttpRequest::timeout_ms封顶到WIT HTTP 默认值与剩余执行截止时间两者中较小者——因为同步主机调用一旦进入便无法安全抢占。deny-by-default 清单还包括WasmHostWorkspace、WasmHostSecrets、WasmHostTools、WasmHostClock含SystemWasmHostClock、DenyWasmHostWorkspace、DenyWasmHostSecrets、DenyWasmHostTools、RecordingWasmHostHttp等实现均可从 src/lib.rs 的 re-export 中核实。3. 分阶段凭证交接staged credential handoff除基础 trait 外host 面还提供一套分阶段凭证交接原语WasmStagedRuntimeCredential(s)、WasmRuntimeCredentialProvider、WasmRuntimeCredentialRequest、WasmRuntimeHttpAdapter、WasmRuntimePolicyDiscarder、EmptyWasmRuntimeCredentials。这印证了 WIT 注释中的安全模型——凭证在主机边界注入WASM 永远看不到真实密钥见下文 ABI 小节。4. 领域无关的wasm_sandbox_coresrc/wasm_sandbox_core.rs 是本 crate 对外的另一面引擎搭建、epoch 计时器、最小 WASI p2 linker、限额、store 核心助手刻意保持领域无关——不引用任何 product、capability、registry、filesystem、network、secrets、host-runtime 或 composition 概念。三、核心安全与计量模型三个不变式支柱README 的 Invariants 部分是理解本通道安全性的钥匙每条都有源码与测试背书1. Deny-by-default 主机导入组件获得的宿主能力恰好等于组合层显式接线的能力不会因遗漏而获得任何默认权限。WitToolHost::deny_all()是元数据提取阶段的实际执行配置WitToolRuntime::prepare在提取description/schema时就使用 deny-all 主机src/runtime.rs也就是说元数据提取阶段组件没有任何能力。2. 每次调用全新 store 聚合内存记账WitToolRuntime::execute每次都通过instantiate创建一个全新Storesrc/runtime.rs杜绝跨调用共享可变状态。内存计量采用跨多 memory 组件的聚合记账WasmResourceLimiter以memory_limit为聚合上限多内存组件无法通过拆分来倍增预算见 crates/lanes/ironclaw_wasm_limiter/README.md。限额由共享 limiter 提供WasmResourceLimiter::new(memory_limit)在构造时固定——聚合线性内存上限memory_limit、最多 10 张表、10 个实例、10 个 memory组件模型内部机制会合法创建多个、表增长上限 10,000 条目。它还实现memory_grow_failed回滚当 OS 级 grow 在批准后失败暂记的记账会回滚使得重试到完整上限仍能成功。limiter 有配套单元测试limiter_tracks_aggregate_growth_across_memories等见 src/wasm_sandbox_core.rs。3. Guest 诊断安全沙箱出口清洗每个 guest 撰写的失败码与消息在沙箱出口处被清洗并限制为 4 KiB不切分 UTF-8 码点每条缓冲的 guest 日志记录同样受限。实现位于scrub_guest_errorsrc/runtime.rsconst MAX_GUEST_ERROR_BYTES: usize 4096; fn scrub_guest_error(error: String) - String { let (mut scrubbed, _redacted) GUEST_ERROR_LEAK_DETECTOR.redact_all_secrets(error); if scrubbed.len() MAX_GUEST_ERROR_BYTES { let mut end MAX_GUEST_ERROR_BYTES; while !scrubbed.is_char_boundary(end) { end - 1; } scrubbed.truncate(end); } scrubbed }其中GUEST_ERROR_LEAK_DETECTOR是共享的LeakDetector注册表识别常见的厂商 API token 形状、PEM/SSH 密钥、bearer/JWT 等。这是每个 WIT 工具 guest 错误离开沙箱的必经咽喉点——例如 provider 把调用方发送的凭证原样回显在拒绝响应体中guest 再原样转发就会携带活凭证在此清洗能保护所有 WASM 工具而非单个工具。scrub_guest_error有完整的测试矩阵识别泄漏形状、保留良性文本、UTF-8 边界截断src/runtime.rs。该清洗之后ironclaw_loop_host的模型可见诊断缝还会再次应用规范的MODEL_DIAGNOSTIC_MAX_BYTES上界类型化 provider 消息进入 dispatch 元数据前会被进一步收窄结构化 JSON 信封保持可解析的 kind/code/message 形态。4. 两层不变式由架构测试强制wasm_sandbox_core保持领域无关由wasm_sandbox_core_module_stays_domain_free_v1_parity_kernel扫描crates/app/ironclaw_architecture_tests/tests/reborn_dependency_boundaries.rs同时固定本 crateAGENTS.md中的两处措辞——修改该文件需保持原句无直接网络由reborn_runtime_http_egress_has_single_network_boundary扫描ABI 文本单一include_str!所有者TOOL_WIT是唯一导出scripts/check-version-bumps.sh以wit/的两个路径为键做 ABI 版本门禁且WIT_TOOL_VERSION必须等于wit/tool.wit的包版本。四、组件模型 ABItool 与 channel 两套契约本 crate 在wit/目录拥有规范的工具/通道组件模型 ABI。wit/tool.witnear:agent0.4.1唯一受支持的 tool 契约完整契约见 crates/lanes/ironclaw_wasm/wit/tool.wit。world sandboxed-tool的结构是import host; export toolhost 接口工具与外部世界交互的唯一途径刻意最小化攻击面log(level, message)结构化日志执行完成后统一收集限速 1000 条/次、4 KB/条now-millis() - u64Unix 纪元毫秒时间戳workspace-read(path) - optionstring读工作区文件需授权路径必须相对、禁止..http-request(method, url, headers-json, body, timeout-ms) - resulthttp-response, http-failureHTTP 请求需授权。http-error-kind是粗粒度、主机所有的枚举auth-required/input/output-too-large/executor/network-denied/client/operation-failedprovider 细节放在code里扩展时无需扩宽枚举http-failure带request-sent: bool是资源记账事实而非重试策略timeout-ms默认 3000030s且被运行时封顶在回调超时以内防止挂起tool-invoke(alias, params-json)按别名调用其他工具间接层WASM 看不到真实工具名限速且输出经泄密扫描secret-exists(name) - bool只能检查密钥是否存在永远不能读取值真实凭证由主机在 HTTP 请求时注入。tool 接口沙箱工具必须实现requestparamsJSON 编码参数 可选context状态化任务的 job 上下文error-kind枚举与guest-failure记录kind是稳定、封闭的 guest 失败原因词汇表与主机侧RuntimeDispatchErrorKind映射表 1:1外加auth-required映射到DispatchError::AuthRequiredcode/message是自由文本载体出口时被清洗message下游还会被再次限制与校验后才到达模型response变体success(string)JSON 编码输出或failure(guest-failure)execute(req) - response主入口解析 params → 执行 → 返回结果或错误schema() - string返回 JSON Schemadescription() - string人类可读描述供 LLM 判断何时调用。版本化失败关闭目标是其他 tool 契约版本的组件在实例化时失败关闭。classify_instantiation_error会从 wasmtime 报错文本中解析near:agentx.y.z引用若与WIT_TOOL_VERSION不符则返回WasmError::UnsupportedContract并附带the host only supports near:agent0.4.1提示同版本缺失导入、无关导入错误则保持普通实例化失败测试见 src/runtime.rs。wit/channel.witnear:agent0.3.1通道契约通道契约保持在near:agent0.3.1crates/lanes/ironclaw_wasm/wit/channel.wit。同一 WIT 包名存在两个版本意味着 bindgen 永远只喂单个文件、绝不喂目录——这是wit/目录布局的刻意设计。world sandboxed-channel为 import channel-host; export channelchannel-host 接口在 tool 基础能力之上扩展emit-message消息入队、回调成功后投递限速 100 条/次、全局按通道可配置、单条内容上限 64 KB、workspace-write路径自动加channels/name/前缀防逃逸、store-attachment-data附件二进制单附件 20 MB、单回调总计 50 MB、回调结束后清除、websocket-send-text以及 DM pairing 三件套pairing-upsert-request/pairing-resolve-identity/pairing-read-allow-fromchannel 接口定义配置类型http-endpoint-config、poll-config、channel-config轮询间隔最小 30000ms、请求/响应类型incoming-http-request带secret-validated标志、outgoing-http-response、attachment、agent-response、status-type枚举thinking/done/interrupted/tool-started/tool-completed/tool-result/approval-needed/status/job-started/auth-required/auth-completed以及 7 个生命周期回调on-start、on-http-request、on-poll、on-respond、on-status、on-broadcast、on-shutdown。WIT 头部注释中的Host-Managed Event Loop架构图清晰展示了 HTTP Router / Polling Scheduler / Timer Scheduler 三者汇入on-http-req/on-poll/on-respond/on-status的流程。五、执行流程源码级拆解1. 引擎与运行时初始化WitToolRuntime::newsrc/runtime.rs的 wasmtime 配置是安全基线wasmtime_config.wasm_component_model(true); wasmtime_config.wasm_threads(false); // 关闭线程减少攻击面 wasmtime_config.consume_fuel(true); // 燃料计量 wasmtime_config.epoch_interruption(true); // epoch 中断 wasmtime_config.debug_info(false); // 不保留调试信息随后spawn_epoch_ticker启动一个名为reborn-wasm-epoch-ticker的线程每EPOCH_TICK_INTERVAL500ms见 src/config.rs调用engine.increment_epoch()。wasm_sandbox_core中的通用版本更精细spawn_epoch_ticker持有WeakEngine而非强引用克隆一旦所有Engine克隆被宿主丢弃ticker 观察到upgrade() None即退出并 join 线程——避免每个component_engine调用泄漏线程与Engine克隆对测试和长驻宿主的热更新场景至关重要回归测试见epoch_ticker_exits_when_engine_is_dropped。2. 预编译与元数据提取prepare(name, wasm_bytes)先编译组件然后调用extract_metadata用deny-all 主机实例化组件调用description()与schema()导出并要求 schema 是合法 JSON 对象否则InvalidSchema。产物是PreparedWitToolsrc/types.rs持有名称、描述、schema、编译后组件与限额快照。3. 每次调用的执行与计量execute的流程src/runtime.rsinstantiate创建全新StoreStoreDataStoreData::new(host, memory_bytes, timeout)同时构造WasmResourceLimiter、最小 WASI p2 上下文、ResourceTable与截止时间src/store.rsconfigure_store设置 fuel、epoch_deadline_trap、按timeout / 500ms换算的 epoch deadline 刻度并挂上 limitercreate_linker注册wasmtime_wasi::p2::add_to_linker_sync最小 WASI与SandboxedTool::add_to_linker构造bindings::exports::near::agent::tool::Request并调用tool.call_execute捕获执行错误若store.data().deadline_exceeded()则报WASM execution deadline exceeded否则对错误文本做scrub_guest_error组装ResourceUsagewall_clock_ms、output_bytes与日志将Response::Success(output)/Response::Failure(failure)映射为WitToolOutcome失败分支再次对code/message做清洗。截止时间在每次主机导入前后都被检查见 src/store.rsworkspace_read_core、http_request_core、tool_invoke_core、secret_exists_core都在调用宿主 trait 前后调用deadline_exceeded()——即便宿主实现本身超时返回路径也会被截止时间兜底拦截。remaining_timeout_ms把 guest 请求的 HTTP 超时封顶到剩余执行时间record_network_egress按请求体字节累计network_egress_bytes含request_was_sent()的错误路径。4. Host 错误到 WIT 错误的映射wit_http_failuresrc/store.rs把WasmHostError映射为 WIThttp-failureAuthRequired → auth-required、Denied → network-denied、Network/Timeout → client、Unavailable → executor、Failed/FailedAfterRequestSent → operation-failed并保留code与request_sent。配套测试wit_http_failure_mapping_preserves_network_code_and_request_sent验证映射不丢失网络错误码与请求已发送标志。5. 默认限额SandboxLimits的默认值src/wasm_sandbox_core.rs参数默认值说明memory_bytes10 MiB聚合线性内存上限fuel500,000,000燃料预算timeout60 s执行超时epoch 中断实现WitToolRuntimeConfig::for_testing()提供更严格的测试配置1 MiB 内存、100,000 fuel、5s 超时src/config.rs。六、依赖与消费关系依赖工作区正常依赖ironclaw_host_api提供ResourceUsage、HTTP egress 类型、MODEL_DIAGNOSTIC_MAX_BYTES、LeakDetector等、ironclaw_wasm_limiter——后者是家族中唯一的 runtimes→runtimes 同层边被reborn_same_layer_edge_inventory.rs固定ironclaw_extension_contracts目前仅是 dev 依赖2026-08-05 实测families/lanes.md将其列为正常依赖实际依赖树比设计记录更窄外部wasmtime、wasmtime-wasi见 Cargo.toml。被消费2026-08-05 实测ironclaw_host_runtime正常依赖ironclaw_integration_testsdev-onlytest-tools/下的九个wasm-src/guest 组件通过相对路径引用wit/——移动本 crate 会改写全部九处wit-bindgen的path:参数并强制 guest 重建由 scripts/ci/check-wasm-artifact-freshness.py 校验。七、运行测试cargo test -p ironclaw_wasm # sandbox-core 扫描 egress 扫描 边清单 cargo test -p ironclaw_architecture_testscrate 内测试覆盖 tests/wit_tool_runtime_contract.rs用wit-component将 WAT 模块编码为组件、按near:agent0.4.1契约验证执行与失败分支、tests/wasm_http_adapter_contract.rs 与 tests/wasm_dispatch_integration.rs驱动真实RuntimeDispatcherlimiter 的 3 个单元测试则验证上限、聚合增长与 grow 失败回滚cargo test -p ironclaw_wasm_limiter。八、延伸阅读工作规则与安全规则规范文本、门禁锁定措辞crates/lanes/ironclaw_wasm/AGENTS.md家族边界含wit/落位于此的原因crates/lanes/AGENTS.md契约文档docs/internal/reborn/contracts/wasm.md、docs/internal/reborn/contracts/runtime-workflows.md、docs/internal/reborn/contracts/network.md均位于 docs/internal/reborn/contracts 目录下设计记录PROPOSAL §6.6.1tool lane、§6.6.2limiter家族设计见docs/internal/reborn/target-architecture/families/lanes.md共享限额实现crates/lanes/ironclaw_wasm_limiter总而言之ironclaw_wasm用单一引擎持有者 deny-by-default 导入 每次调用全新 store 聚合资源计量 出口诊断清洗这一组合把不可信的 WASM 组件约束在可审计、可计量的执行边界内同时以wit/tool.witnear:agent0.4.1与wit/channel.witnear:agent0.3.1两套版本化 ABI 定义了工具与通道组件的唯一合法契约——任何偏离该契约的组件都会在实例化阶段失败关闭。【免费下载链接】ironclawIronClaw is an Agent OS focused on privacy, security and extensibility项目地址: https://gitcode.com/gh_mirrors/iro/ironclaw创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考