可信计算——信任分级与授权审批系统

📑 目录

IronClaw 深度剖析(十一):可信计算——信任分级与授权审批系统

信任不是声明,而是主机策略推导出的约束上限。

在 IronClaw 的世界里,没有任何扩展可以自称为"可信"——可信是主机赋予的属性,是可以被撤销的承诺,是受架构强制约束的上限。


一、为什么需要信任分级

1.1 不是所有代码都同等可信

传统操作系统将安全视为一个二元问题:要么是 root(无所不能),要么是普通用户(受限)。这种"非黑即白"的模型在现代 AI Agent 生态中已不再适用。当一个平台允许加载来自不同来源的扩展时——官方内置的、第三方开发的、用户自行安装的——它们显然不应该拥有相同的权限。

想象一下这个场景:你的 Agent 同时加载了三个扩展:

扩展来源你对它的信任程度期望权限范围
官方内置的文件读写工具完全信任读写工作目录
社区开发的翻译插件谨慎信任仅 HTTP 网络访问
从互联网下载的未知扩展不信任仅能沙箱内计算

如果这三个扩展拥有相同的系统权限,任何一个被攻破都将导致全局沦陷。信任分级的本质就是将安全决策从"能否运行"细化为"能以什么权限运行"。

1.2 从 seL4/Capsicum 到 IronClaw

IronClaw 的信任模型深受两个先驱项目的影响:

  • seL4:世界上第一个经过形式化验证的操作系统内核,其核心思想是所有权限都通过 capability 显式授予,没有任何隐式信任。
  • Capsicum/CloudABI:FreeBSD 的 capability 模式,将进程的能力限制在显式授予的句柄集合内,file descriptor 就是 capability。

IronClaw 站在这些巨人的肩膀上,但面向的是 AI Agent 特有的挑战:动态加载的扩展、人类审批流程、资源配额管理、义务追踪。它不是操作系统的安全模型,而是Agent 运行时的可信计算基(TCB)

1.3 安全是架构基础,不是附加功能

IronClaw 的安全架构最值得关注的设计决策是:安全不是一个可以关闭的插件,而是架构的基础假设。

graph TD
    subgraph "传统安全模型(附加式)"
        A1[业务代码] --> A2[安全检查层(可绕过)]
        A2 --> A3[系统调用]
    end
    
    subgraph "IronClaw 安全模型(内建式)"
        B1[CapabilityHost] --> B2{授权检查}
        B2 -->|Deny| B3[拒绝(默认)]
        B2 -->|Allow| B4[义务准备]
        B2 -->|RequireApproval| B5[审批等待]
        B4 --> B6[调度执行]
        B6 --> B7[义务完成]
    end

从上图可以看出,IronClaw 的授权检查不是一个可以跳过的中间层,而是调用路径上不可绕过的必经节点CapabilityHost 作为协调中心,所有能力调用都必须经过它的授权流程。


二、Trust Class:四级信任模型

2.1 理论模型与实现

ironclaw_trust crate 定义了 IronClaw 的信任分级体系。理论上,模型分为四个层级:

信任级别说明权限天花板
System系统级组件,主机内置最高权限
FirstParty第一方代码,官方维护高权限,有限制
UserTrusted用户显式信任中等权限,需审批
Sandbox沙箱级,默认级别最低权限

而在实际实现中,IronClaw 扩展为 六级模型,在两个极端之间插入了更细粒度的分级:

/// ironclaw_trust/src/decision.rs
/// 
/// 有效信任级别——不是扩展可以声明的,而是主机策略推导的
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
#[serde(transparent)]
pub struct EffectiveTrustClass {
    inner: TrustClass,
}

impl EffectiveTrustClass {
    // 外部可构造——低信任级别
    pub fn sandbox() -> Self      { Self { inner: TrustClass::Sandbox } }
    pub fn user_trusted() -> Self { Self { inner: TrustClass::UserTrusted } }
    
    // 仅在 ironclaw_trust 内部可构造——高信任级别
    pub(crate) fn signed() -> Self      { Self { inner: TrustClass::Signed } }
    pub(crate) fn first_party() -> Self { Self { inner: TrustClass::FirstParty } }
    pub(crate) fn system() -> Self      { Self { inner: TrustClass::System } }
}

注意这里精妙的可见性控制SystemFirstPartySigned 的构造器标记为 pub(crate),意味着外部代码完全无法伪造高信任级别。这是编译器级别的安全保证——不是约定,不是检查,而是根本无法构造。

2.2 信任金字塔

graph BT
    S[System
系统级] FP[FirstParty
第一方] SN[Signed
签名验证] UT[UserTrusted
用户信任] TP[ThirdParty
第三方] SB[Sandbox
沙箱级] S --> FP FP --> SN SN --> UT UT --> TP TP --> SB style S fill:#ff4444,color:#fff style FP fill:#ff8844,color:#fff style SN fill:#ffaa44,color:#000 style UT fill:#ffdd44,color:#000 style TP fill:#ffee88,color:#000 style SB fill:#e8e8e8,color:#666

这个金字塔的核心含义是:向下的箭头代表权限天花板的继承关系。 System 可以做的事情,Sandbox 不一定能做;但 Sandbox 能做的,System 一定能做。每一层都继承了下一层的所有能力,同时拥有额外的权限。

2.3 默认决策:Fail-Closed

IronClaw 的默认决策策略是安全设计的典范:

fn default_decision(_input: &TrustPolicyInput, evaluated_at: Timestamp) -> TrustDecision {
    TrustDecision {
        effective_trust: EffectiveTrustClass::sandbox(),  // 沙箱级!
        authority_ceiling: AuthorityCeiling::empty(),      // 零权限
        provenance: TrustProvenance::Default,
        evaluated_at,
    }
}

所有未被策略匹配的扩展,默认落入 Sandbox 级别,权限天花板为空。 这不是一个配置选项,而是硬编码的架构常量——在 IronClaw 中,未知即不可信。


三、三大安全不变量

IronClaw 的信任系统建立在三个不可违反的安全不变量之上。这些不变量不是文档中的建议,而是由类型系统和模块可见性在编译期保证的约束

不变量 #1:有效信任仅限主机策略

FirstParty 和 System 信任类只能在 ironclaw_trust crate 内部构造

这意味着:任何外部代码——无论它多么"可信"——都无法声称自己是 System 级别。信任级别的判定权完全掌握在主机策略引擎手中,这是主权的不可让渡性

不变量 #2:信任是天花板而非授权

pub struct TrustDecision {
    pub effective_trust: EffectiveTrustClass,    // 推导出的信任级别
    pub authority_ceiling: AuthorityCeiling,      // ← 这是"天花板"
    pub provenance: TrustProvenance,
    pub evaluated_at: Timestamp,
}

pub struct AuthorityCeiling {
    pub allowed_effects: Vec<EffectKind>,         // 允许的效果种类
    pub max_resource_ceiling: Option<ResourceCeiling>, // 资源上限
}

AuthorityCeiling 这个名字本身就传达了设计意图:它定义的是允许范围的上限,而不是实际授予的权限。一个扩展可能拥有很高的信任天花板,但如果没有任何显式的 CapabilityGrant,它仍然什么都不能做。信任给你的是可能性,不是权力。

不变量 #3:信任变更使活动授权失效

当信任策略发生变化——管理员撤销了某个扩展的信任、签名证书过期、安全漏洞被发现——所有基于旧信任的活跃授权必须立即失效。

pub struct TrustChange {
    pub identity: PackageIdentity,
    pub previous: AuthorityCeiling,
    pub current: AuthorityCeiling,
}

// 检查现有 Grant 是否在新的天花板下仍然有效
pub fn grant_retention_eligible(
    grant: &CapabilityGrant, 
    new_ceiling: &AuthorityCeiling
) -> bool {
    // 如果 Grant 所需的 effects 超出了新的天花板,则失效
    grant.required_effects().iter().all(|e| 
        new_ceiling.allowed_effects.contains(e)
    )
}

这个机制确保了信任撤销具有即时性——一旦信任被降级,相关的能力授权在下次使用时就会失效,无需等待会话结束或手动清理。


四、原子变更模式:mutate_with

4.1 问题:信任变更中的竞态条件

假设我们有一个多线程运行时,正在执行以下操作序列:

  1. 线程 A:更新信任策略(将扩展 X 从 UserTrusted 降级到 Sandbox
  2. 线程 B:正在使用扩展 X 的活跃授权执行能力调用

如果没有同步机制,线程 B 可能在线程 A 的策略更新和授权失效通知之间"溜过去",继续使用已降级的授权执行操作。这就是经典的 TOCTOU(Time-of-Check Time-of-Use)竞态条件

4.2 mutate_with 的设计

IronClaw 的解决方案是 mutate_with——一个将策略变更与失效通知原子化的 API:

/// ironclaw_trust/src/policy.rs
///
/// 原子化信任策略变更——策略修改与失效通知不可分割
pub fn mutate_with<F, R>(
    &self,
    bus: &InvalidationBus,                    // 失效事件总线
    affected_identity: PackageIdentity,       // 受影响的包身份
    requested_authority: BTreeSet<CapabilityId>, // 请求的权限
    requested_trust: RequestedTrustClass,     // 请求的信任级别
    f: F,                                     // 用户闭包:实际变更逻辑
) -> Result<R, TrustError>
where
    F: FnOnce(&SourceMutators<'_>) -> Result<R, TrustError>,
{
    // 1. 获取写锁——独占访问
    let _gate = self.mutation_gate.write()?;
    
    // 2. 预评估:记录变更前的信任决策
    let prev = self.evaluate_unlocked(&probe)?;
    
    // 3. 执行用户闭包——变更暂存,尚未提交
    let result = f(&mutators)?;
    
    // 4. 如果闭包出错,丢弃所有暂存变更
    //    (这是自动的,mutators 在出错时不会被提交)
    
    // 5. 提交变更——此时策略才真正更新
    mutators.commit()?;
    
    // 6. 后评估:获取变更后的信任决策
    let curr = self.evaluate_unlocked(&probe)?;
    
    // 7. 如果有效信任发生变化或权限天花板收缩
    if let Some(change) = TrustChange::new(identity, &prev, &curr) {
        // 8. 在释放锁之前同步发布失效事件!
        bus.publish(change);  // ← 关键:在锁内完成
    }
    
    // 9. 释放写锁
    Ok(result)
}

4.3 原子变更序列图

sequenceDiagram
    participant Caller as 调用者
    participant Policy as HostTrustPolicy
    participant Gate as mutation_gate
    participant Bus as InvalidationBus
    
    Caller->>Policy: mutate_with(identity, f)
    Policy->>Gate: write() acquire
    Gate-->>Policy: lock acquired
    
    Policy->>Policy: evaluate_unlocked() prev
    Note right of Policy: 记录变更前的信任决策
    
    Policy->>Caller: f(&mutators)
    Caller-->>Policy: result
    
    alt 闭包成功
        Policy->>Policy: mutators.commit()
        Note right of Policy: 策略原子提交
        
        Policy->>Policy: evaluate_unlocked() curr
        Note right of Policy: 获取新信任决策
        
        opt trust_changed || ceiling_shrank
            Policy->>Bus: publish(TrustChange)
            Note right of Bus: 同步失效通知!
在释放锁之前 end Policy->>Gate: release Policy-->>Caller: Ok(result) else 闭包失败 Policy->>Policy: discard staged Policy->>Gate: release Policy-->>Caller: Err(...) end

这个设计的精妙之处在于:失效事件在写锁释放之前同步发布。这意味着:

  1. 任何在 publish 之后开始的授权检查,都会看到新的信任策略
  2. 任何在 publish 之前开始的授权检查,会在完成前被阻塞(因为写锁未释放)
  3. 不存在任何时间窗口,让"旧授权"在"新策略"下执行

AC #6(Architecture Constraint #6)是一个编译期保证——通过将 mutate_with 设计为唯一合法的变更入口,确保没有任何代码路径可以绕过失效通知。


五、审批系统:ironclaw_approvals

5.1 审批与执行的分离

IronClaw 的审批系统遵循关注点分离原则。ironclaw_approvals 只负责审批决议和授权租赁的签发,不负责提示用户、执行能力或调度运行时工作。这种分离带来了清晰的安全边界:

/// ironclaw_approvals/src/resolver.rs
///
/// 审批解析器——审批决策与能力执行的桥梁
pub struct ApprovalResolver<'a, A, L>
where
    A: ApprovalRequestStore + ?Sized,    // 审批请求存储
    L: CapabilityLeaseStore + ?Sized,    // 能力租赁存储
{
    approvals: &'a A,    // 审批请求持久化存储
    leases: &'a L,       // 已签发租赁的存储
    audit_sink: Option<&'a dyn AuditSink>,  // 审计日志出口
}

CapabilityHost 的授权检查返回 Decision::RequireApproval 时,审批流程启动:

1. CapabilityHost 确定需要审批 → 创建 ApprovalRequest
2. ApprovalRequest 持久化到 ApprovalRequestStore
3. 运行状态转为 BlockedApproval
4. 人类审批者审查并批准
5. ApprovalResolver::approve_dispatch() 被调用
6. 验证审批状态为 Approved 且与调用匹配
7. 创建 CapabilityLease(带范围授权)
8. 租赁存入 CapabilityLeaseStore
9. CapabilityHost::resume_json 申领并消费租赁

5.2 CapabilityLease:绑定一切的安全令牌

CapabilityLease 是 IronClaw 审批系统的核心数据结构。它将授权与调用上下文紧密绑定,防止多种攻击:

/// ironclaw_approvals/src/lease.rs
///
/// 能力租赁——单次使用、限时、绑定调用的授权令牌
pub struct CapabilityLease {
    pub id: CapabilityLeaseId,                    // 唯一标识
    pub grant: CapabilityGrant,                   // 实际的能力授权
    pub fingerprint: InvocationFingerprint,       // ← 绑定到特定调用
    pub approved_at: Timestamp,                   // 批准时间
    pub expires_at: Timestamp,                    // ← 显式过期时间
    pub consumed: AtomicBool,                     // ← 单次使用标记
}

impl CapabilityLease {
    /// 申领租赁(防止并发恢复攻击)
    pub fn claim(&self) -> Result<ClaimedLease, LeaseError> {
        // CAS 操作确保只有一个调用者能成功申领
        if self.consumed.compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst)
            .is_err() 
        {
            return Err(LeaseError::AlreadyClaimed);
        }
        Ok(ClaimedLease { lease: self })
    }
    
    /// 检查是否过期
    pub fn is_expired(&self, now: Timestamp) -> bool {
        now > self.expires_at
    }
    
    /// 验证调用指纹匹配
    pub fn verify_fingerprint(&self, actual: &InvocationFingerprint) -> Result<(), LeaseError> {
        if &self.fingerprint != actual {
            return Err(LeaseError::FingerprintMismatch);
        }
        Ok(())
    }
}

5.3 CapabilityLease 生命周期

stateDiagram-v2
    [*] --> Pending: CapabilityHost返回
RequireApproval Pending --> Approved: 人类审批者批准 Pending --> Denied: 人类审批者拒绝 Pending --> Expired: 超时未处理 Approved --> Created: ApprovalResolver创建
CapabilityLease Created --> Claimed: resume_json申领 Created --> Expired: 超过expires_at Claimed --> Consumed: 能力执行成功 Claimed --> Released: 执行失败/异常 Consumed --> [*]: 单次使用完成 Released --> [*]: 资源释放 Denied --> [*]: 记录审计日志 Expired --> [*]: 自动清理

CapabilityLease 设计了四重安全防护:

防护机制作用防御的攻击
调用指纹绑定租赁只能用于特定调用重放攻击——将审批过的租赁用于其他调用
显式过期时间租赁在指定时间后失效时间窗口攻击——长期持有已审批租赁
单次使用消费后不可再用重复使用攻击——多次使用同一审批
申领机制CAS 原子操作确保单一消费并发恢复攻击——多个线程同时恢复同一审批

5.4 先申领后消费模式

// ironclaw_capabilities/src/resume.rs

// 1. 查找与审批关联的租赁
let lease = capability_leases
    .lookup(&scope, approval.lease_id)
    .await?;

// 2. 验证调用指纹(防止重放)
lease.verify_fingerprint(&invocation_fingerprint)?;

// 3. 申领租赁(CAS 原子操作,防止并发恢复)
let claimed_lease = capability_leases
    .claim(&scope, lease.id, &invocation_fingerprint)
    .await?;

// 4. 使用租赁进行授权(此时 guaranteed 唯一)
let decision = authorizer
    .authorize_trusted_dispatch(&context, &trust, descriptor, estimate)
    .await?;

// 5. 执行能力调用
let result = dispatcher.dispatch_json(...).await;

// 6. 无论成功与否,消费租赁(使其永久失效)
match result {
    Ok(_) => capability_leases.consume(&scope, claimed_lease.id).await?,
    Err(_) => capability_leases.consume(&scope, claimed_lease.id).await?, 
}

注意第 6 步:无论执行成功与否,租赁都被消费。这确保了租赁的语义一致性——申领即承诺消费,不存在"申领后放弃"的代码路径。


六、义务系统:ironclaw_events

6.1 授权不是终点

在传统安全模型中,授权是一个二元决策:允许或拒绝。IronClaw 在此基础上引入了**义务(Obligation)**的概念——授权决策可以附带一组必须在调用前后履行的义务。

/// ironclaw_capabilities/src/obligations.rs
///
/// 义务处理接口——授权决策的副作用管理
#[async_trait]
pub trait CapabilityObligationHandler: Send + Sync {
    /// 调度前:满足前置条件
    async fn prepare(&self, request: CapabilityObligationRequest<'_>)
        -> Result<CapabilityObligationOutcome, CapabilityObligationError>;
    
    /// 下游失败时:清理已准备的资源
    async fn abort(&self, request: CapabilityObligationAbortRequest<'_>)
        -> Result<(), CapabilityObligationError>;
    
    /// 调度成功后:完成后置义务
    async fn complete_dispatch(&self, request: CapabilityObligationCompletionRequest<'_>)
        -> Result<CapabilityDispatchResult, CapabilityObligationError>;
}

6.2 四阶段义务追踪

graph TD
    A[授权决策: Allow with obligations] --> B[PreDispatch]
    B -->|Fulfilled| C[能力调度执行]
    B -->|Failed| D[Abort清理]
    C -->|Success| E[PostDispatch]
    C -->|Failure| F[OnFailure处理]
    E --> G[OnCompletion清理]
    F --> G
    D --> H[返回错误]
    G --> I[返回结果]
    
    style A fill:#4488ff,color:#fff
    style B fill:#44bb44,color:#fff
    style C fill:#ffaa44,color:#000
    style E fill:#44bb44,color:#fff
    style F fill:#ff4444,color:#fff

义务系统的四个阶段覆盖了能力调用的完整生命周期:

阶段触发时机典型义务失败处理
PreDispatch调度执行前资源预留、挂载策略应用、网络策略注入调度取消
PostDispatch调度成功后审计日志、输出脱敏、资源上限强制执行结果标记
OnFailure调度失败时回滚副作用、释放预留资源、记录失败原因尽力而为
OnCompletion全部完成后清理临时资源、更新配额使用、归档审计记录异步处理

6.3 义务类型

/// ironclaw_host_api/src/decision.rs
///
/// 授权决策附带的义务集合
pub enum Obligation {
    /// 要求调度后进行审计日志
    AuditAfter { category: AuditCategory, sensitivity: SensitivityLevel },
    
    /// 对输出进行脱敏处理
    RedactOutput { patterns: Vec<RedactionPattern> },
    
    /// 应用文件系统挂载策略
    ApplyMountPolicy { view: MountView },
    
    /// 应用网络访问策略
    ApplyNetworkPolicy { policy: NetworkPolicy },
    
    /// 强制执行资源天花板
    EnforceResourceCeiling { ceiling: ResourceCeiling },
    
    /// 限制输出大小
    EnforceOutputLimit { max_bytes: u64 },
    
    /// 注入凭证(不暴露给调用者)
    InjectCredential { handle: SecretHandle, target: InjectionTarget },
}

6.4 义务追踪的代码示例

// 义务处理实现示例
impl CapabilityObligationHandler for DefaultObligationHandler {
    async fn prepare(&self, req: CapabilityObligationRequest<'_>) 
        -> Result<CapabilityObligationOutcome, CapabilityObligationError> 
    {
        for obligation in &req.obligations {
            match obligation {
                Obligation::ApplyMountPolicy { view } => {
                    // 在调度前设置文件系统可见性
                    self.mount_manager.apply(view).await?;
                }
                Obligation::ApplyNetworkPolicy { policy } => {
                    // 配置网络访问白名单
                    self.network_policy.enforce(policy).await?;
                }
                Obligation::EnforceResourceCeiling { ceiling } => {
                    // 预留资源配额
                    self.resource_manager.reserve(ceiling, req.scope).await?;
                }
                Obligation::InjectCredential { handle, target } => {
                    // 安全注入凭证——调用者看不到凭证内容
                    let secret = self.secret_store.lease(handle, req.scope).await?;
                    self.credential_injector.inject(secret, target).await?;
                }
                // PreDispatch 不处理 PostDispatch 义务
                _ => {}
            }
        }
        Ok(CapabilityObligationOutcome::Fulfilled)
    }
    
    async fn complete_dispatch(&self, req: CapabilityObligationCompletionRequest<'_>) 
        -> Result<CapabilityDispatchResult, CapabilityObligationError> 
    {
        let mut result = req.dispatch_result;
        
        for obligation in &req.obligations {
            match obligation {
                Obligation::AuditAfter { category, sensitivity } => {
                    // 记录审计日志
                    self.audit_sink.log(AuditEntry {
                        category: *category,
                        sensitivity: *sensitivity,
                        invocation: req.invocation_id,
                        timestamp: now(),
                    }).await?;
                }
                Obligation::RedactOutput { patterns } => {
                    // 对输出进行脱敏
                    result.output = self.redactor.redact(&result.output, patterns)?;
                }
                Obligation::EnforceOutputLimit { max_bytes } => {
                    // 截断超大输出
                    if result.output.len() > *max_bytes {
                        result.output = truncate(&result.output, *max_bytes);
                        result.warnings.push(OutputWarning::Truncated);
                    }
                }
                _ => {}
            }
        }
        Ok(result)
    }
}

关键设计: Spawn 操作(异步子进程)不能拥有 PostDispatch 义务,因为子进程的生命周期独立于父调用。这体现了 IronClaw 安全模型的保守性原则——如果不能保证执行后处理,就不允许赋予该义务。


七、Capability 授权检查:双重授权模型

7.1 显式授权 + 隐式信任天花板

IronClaw 的授权检查采用双重验证机制:

/// ironclaw_authorization/src/lib.rs
///
/// 信任感知的能力调度授权器
#[async_trait]
pub trait TrustAwareCapabilityDispatchAuthorizer: Send + Sync {
    /// 使用显式授权/租赁和策略派生的权限上限进行授权
    async fn authorize_trusted_dispatch(
        &self,
        context: &ExecutionContext,              // 调用上下文
        trust: &TrustDecision,                   // ← 信任决策(隐式约束)
        descriptor: &CapabilityDescriptor,       // 能力描述
        estimate: &ResourceEstimate,             // 资源估算
    ) -> Decision;
}

双重授权的含义是:

  1. 第一重:显式授权——ExecutionContext.grants 中必须包含匹配的 CapabilityGrantCapabilityLease
  2. 第二重:隐式约束——TrustDecision.authority_ceiling 必须覆盖请求的效果和资源

只有两个条件同时满足,CapabilityDispatchAuthorizer 才会返回 Allow

7.2 Fail-Closed 默认

// CapabilityDispatchAuthorizer 的默认实现
impl Default for DefaultDispatchAuthorizer {
    fn default() -> Self {
        Self {
            // 默认拒绝所有请求
            default_decision: Decision::Deny { 
                reason: DenyReason::NoAuthorizerConfigured 
            },
        }
    }
}

如果没有配置授权器,或者授权器遇到任何异常,默认行为是拒绝(Deny)。在 IronClaw 的安全哲学中,"错误"的方向永远是更安全的一方。

7.3 授权决策流程

graph TD
    A[authorize_trusted_dispatch] --> B{上下文有效?}
    B -->|No| Z[Deny: InvalidContext]
    B -->|Yes| C{存在匹配的Grant?}
    C -->|No| D{存在有效的Lease?}
    D -->|No| Z
    C -->|Yes| E{Grant在TrustCeiling内?}
    D -->|Yes| E
    E -->|No| Z[Deny: TrustClassTooLow]
    E -->|Yes| F{资源估算在限制内?}
    F -->|No| Z[Deny: ResourceLimitExceeded]
    F -->|Yes| G{需要审批?}
    G -->|Yes| H[RequireApproval]
    G -->|No| I[Allow with obligations]
    
    style Z fill:#ff4444,color:#fff
    style H fill:#ffaa44,color:#000
    style I fill:#44bb44,color:#fff

八、资源配额系统:ironclaw_resources

8.1 六级账户层级

IronClaw 的资源配额系统采用六级层级结构,每一级都可以有独立的配额限制:

graph TD
    T[Tenant
租户级] --> U[User
用户级] U --> P[Project
项目级] P --> A[Agent
Agent级] A --> M[Mission
任务级] M --> Th[Thread
线程级] T -.配额继承.-> U U -.配额继承.-> P P -.配额继承.-> A A -.配额继承.-> M M -.配额继承.-> Th style T fill:#ff4444,color:#fff style U fill:#ff8844,color:#fff style P fill:#ffaa44,color:#000 style A fill:#ffdd44,color:#000 style M fill:#ccff44,color:#000 style Th fill:#88ff44,color:#000

每一级都可以定义自己的配额限制,子级的可用配额是其自身限制与所有父级限制的交集(取最小值)。这种设计确保了资源约束的逐级收紧——越细粒度的作用域,可用资源越少。

8.2 五维资源限制

/// ironclaw_resources/src/quota.rs
///
/// 五维资源配额定义
pub struct ResourceQuota {
    /// 货币成本上限(如 API 调用费用)
    pub cost_ceiling: Option<Decimal>,
    
    /// Token 消耗上限(LLM 调用)
    pub token_ceiling: Option<u64>,
    
    /// 执行时间上限(毫秒)
    pub time_ceiling: Option<u64>,
    
    /// 可同时运行的沙箱数量
    pub sandbox_ceiling: Option<u32>,
    
    /// WASM fuel 上限(指令计数)
    pub fuel_ceiling: Option<u64>,
}

8.3 预留-执行-对账协议

IronClaw 的资源管理采用**预留-执行-对账(Reserve-Execute-Reconcile)**三阶段协议:

/// 资源管理接口
#[async_trait]
pub trait ResourceManager: Send + Sync {
    /// Phase 1: 预留——在执行前锁定所需资源
    async fn reserve(
        &self, 
        scope: &ResourceScope, 
        estimate: &ResourceEstimate
    ) -> Result<ResourceReservation, ResourceError>;
    
    /// Phase 2: 执行——实际使用资源(在预留范围内)
    /// (执行阶段不直接与 ResourceManager 交互,通过预留令牌跟踪)
    
    /// Phase 3a: 对账——执行成功后,将实际使用与预留对比
    async fn reconcile(
        &self,
        scope: &ResourceScope,
        reservation: ResourceReservation,
        actual: &ResourceUsage,
    ) -> Result<ReconciliationResult, ResourceError>;
    
    /// Phase 3b: 释放——执行失败或取消时,释放预留资源
    async fn release(
        &self,
        scope: &ResourceScope,
        reservation: ResourceReservation,
    ) -> Result<(), ResourceError>;
}
阶段操作目的失败处理
Reserve锁定预估资源防止超支、确保资源可用返回 ResourceBlocked,调用者可降级重试
Execute实际消耗资源在预留范围内执行由 sandbox 级别的 fuel/time 限制兜底
Reconcile对比实际与预估多退少补、更新配额使用记录差异,供后续配额调整参考
Release释放未用预留避免资源永久占用定时器兜底自动释放

这个协议解决了资源管理中的经典问题:预估不准怎么办? 预留阶段基于保守估算锁定资源,执行阶段在 sandbox 层面有硬限制兜底,对账阶段则将预估误差纳入后续决策参考。


九、总结:可信计算的架构之美

IronClaw 的信任与授权系统展现了一个统一的安全哲学:安全不是检查清单,而是不可绕过的架构约束。

┌─────────────────────────────────────────────────────────────┐
│                    IronClaw 可信计算全景                        │
├─────────────────────────────────────────────────────────────┤
│  信任层 (ironclaw_trust)                                     │
│  ├── 六级信任分级 —— 编译期可见性控制                          │
│  ├── 三大安全不变量 —— 类型系统保证                            │
│  └── mutate_with —— 原子变更 + 同步失效                        │
├─────────────────────────────────────────────────────────────┤
│  审批层 (ironclaw_approvals)                                 │
│  ├── ApprovalResolver —— 审批与执行分离                        │
│  └── CapabilityLease —— 指纹绑定 + 限时 + 单次使用              │
├─────────────────────────────────────────────────────────────┤
│  能力层 (ironclaw_capabilities)                              │
│  ├── CapabilityHost —— 协调中心                              │
│  ├── 双重授权 —— 显式Grant + 隐式TrustCeiling                  │
│  └── 义务系统 —— Pre/Post/OnFailure/OnCompletion              │
├─────────────────────────────────────────────────────────────┤
│  资源层 (ironclaw_resources)                                 │
│  ├── 六级账户层级 —— 逐级收紧的配额继承                        │
│  └── 预留-执行-对账 —— 三阶段资源协议                          │
├─────────────────────────────────────────────────────────────┤
│  沙箱层 (ironclaw_wasm)                                      │
│  ├── Fuel 计量 —— 逐指令资源追踪                              │
│  └── 主机 API 隔离 —— deny-by-default                         │
└─────────────────────────────────────────────────────────────┘

从上往下看,每一层都为上一层提供更细粒度的安全保证。从下往上看,每一层都建立在下层的约束之上。信任分级决定了权限天花板,审批系统控制敏感操作的二次确认,义务系统确保安全后处理的不可绕过,资源配额防止资源耗尽攻击,沙箱隔离提供了最终的执行边界。

这不是完美的安全——没有系统是绝对安全的——但这是一个在架构层面将安全内建的系统。它的安全保证不依赖于配置的正确性,不依赖于管理员的谨慎,而是依赖于代码无法编译通过如果它试图绕过安全控制

正如 IronClaw 的设计者所言:"我们不相信代码,我们相信架构。"


本文是 IronClaw 深度剖析系列的第十一篇。系列索引:blog-index