首页
/ RuView ADR-141 精读:BFLD 隐私控制平面——命名模式、离散动作与哈希链防篡改运行时证明

RuView ADR-141 精读:BFLD 隐私控制平面——命名模式、离散动作与哈希链防篡改运行时证明

2026-09-07 11:33:56作者:宣聪麟

本文面向部署 RuView WiFi 传感栈的开发者与隐私合规工程人员,系统讲解架构决策记录 ADR-141《BFLD Privacy Control Plane》所确立的三层抽象:面向运维的命名隐私模式(PrivacyMode)、可机器校验的离散隐私动作(PrivacyAction),以及基于 BLAKE3 哈希链的运行时证明(PrivacyAttestationProof)。读完后你将理解:在 wifi-densepose-bfld 这个 crate 中,一个"部署点当前到底允许对外发出什么"的问题,如何从散落四处、隐式的硬编码,收敛为一个可审计、可被下游(ADR-139 WorldGraph、ADR-140 Semantic State Record)查询的"单一事实源",且不改变任何线上字节契约。

一、背景:数值分级存在,但"策略"缺失

1.1 此前的真空:隐私姿态被编码在四个互不相干的地方

在 ADR-141 之前,BFLD crate 已经具备一套结构上强制的隐私分级,但它完全建立在一个 4 值的数值枚举 PrivacyClass 之上,没有"模式"(mode)与"动作"(action)这样的一等公民概念。对照真实源码:

  • lib.rs 定义 #[repr(u8)] 的四级隐私类:Raw = 0Derived = 1Anonymous = 2Restricted = 3(见 lib.rs 中 PrivacyClass 枚举及 allows_network()allows_matter()as_u8() 等成员方法)。这是系统唯一描述"该部署允许发出什么"的词汇表,但它不解释为什么一个节点处于 class 2 而非 class 3,也不记录实际施加了哪些隐私变换。
  • privacy_gate.rsPrivacyGate::demote() 是单调、清零化的变换器:在每次类切换时剥离 compressed_angle_matrixcsi_deltaamplitude_proxyphase_proxy 等 payload 区段。剥离是真实且不可逆的(代码通过 black_box 防死存储消除,见 zeroize_then_clear),但它是沉默的——没有任何记录说明"哪一帧、被置零了哪些区段",下游验证者无法证明"确实被剥过了"。
  • sink.rs 通过 Sink::MIN_CLASS 常量与运行时 check_class::<S>() 在编译/运行两层强制结构不变量 I1(Raw BFI 永不离开节点)。MQTT 主题路由与 HA discovery 进一步把"仅 class >= Anonymous 才可发布、identity_risk 仅在恰为 Anonymous 时发布"这类规则硬编码散落在多处文件——这是隐式 ACL,没有在单一位置声明,也没有绑定到具名模式。
  • identity_risk.rs 定义 GateAction { Accept, PredictOnly, Reject, Recalibrate },但这些是逐事件的风险门控动作,而非隐私动作;系统中没有一个枚举能命名一个模式所执行的隐私变换(如 "suppress identity"、"drop raw"、"aggregate only")。
  • emitter.rsprivacy_class: PrivacyClass::Anonymous 作为构造默认值;Soul Signature 门控是否生效,仅取决于是否注入了一个 SoulMatchOracle

由此产生的真空正是 ADR-141 的出发点:一个部署的隐私姿态被拆进四处(构造出的 PrivacyClassSoulMatchOracle 有无、class 门控的 MQTT/HA 扇出、signature_hasher 是否安装),没有任何一个声明式对象能够回答"这个节点运行在 CareWithConsent 模式,即 class Derived、Soul Signature 开启、identity_risk 可发布、Raw 绝不联网"。监管者、Home Assistant 仪表盘或 WorldGraph(ADR-139)都读不到"有效策略"这一运行时工件,更不存在逐帧证明策略确实被执行的密码学证据。

与之直接挂钩的需求来自兄弟 ADR:ADR-140 要求每条语义状态都能追溯到一个 privacy_action;ADR-139 需要 privacy_limited_by 注解来判断哪些边/区域因隐私而降级。在 ADR-141 之前,两者都没有可绑定的对象——BFLD 只暴露数值 class,既无 action 也无 attestation。

1.2 三个术语的精确定义

  • PrivacyMode(隐私模式):面向运维的、有名字的部署姿态(如 CareWithConsent),是监管者/安装者用来推理的人话单元。它不是新的强制原语,而是一种声明式选择,映射到既有的 PrivacyClass、Soul Signature 门控决策和 MQTT/Matter ACL。
  • PrivacyAction(隐私动作):模式强制的、离散的、可机器检查的隐私变换(如 SuppressIdentityDropRaw)。动作是"人话模式"与 privacy_gate.rs 中已有的字节级剥离之间的桥,也是 ADR-140 privacy_action 字段所携带的内容。
  • PrivacyAttestationProof(隐私证明):按 ADR-010(Witness Chains)风格哈希链式记录的工件,说明"哪个模式处于活动、强制执行了哪些动作、每个事件剥离了哪些字段"。它是"声明模式确实被恪守"的密码学连续性证明,以只读方式呈现在 HA/Matter 诊断面上。

1.3 明确边界:本 ADR 不做什么

本设计不改变四个 PrivacyClass 字节值,不削弱 lib.rs 中声明的结构不变量 I1/I2/I3(Raw 不联网、身份嵌入仅驻内存、跨站点身份关联密码学上不可行),也不替换 PrivacyGate::demote()——它的职责是"记录 demote() 做了什么",而非重写它。

1.4 在管线中的位置

ADR 用一张调用图清晰标出新增件(标 ← NEW)插入的位置:

SensingInputs
  → BfldEmitter::emit()                       (identity_risk + CoherenceGate)
       ↑ consults
  PrivacyModeRegistry::active_mode()          ← NEW
       ↓ resolves to (PrivacyClass, Soul gate, ACL)
  → PrivacyGate::demote(frame, target_class)  (existing; now records stripped fields)
       ↓ emits per-frame
  PrivacyActionRecord { actions, fields_stripped }  ← NEW
       ↓ folded into
  PrivacyAttestationProof { mode, actions, fields_stripped_per_event, prev_hash }  ← NEW (hash-chained, ADR-010)
       ↓ surfaced
  mqtt_topics.rs / ha_discovery.rs            (active mode + proof hash diagnostic entity)
       ↓ consumed by
  ADR-139 privacy_limited_by  /  ADR-140 privacy_action

两个关键的节流设计:注册表只在类切换时被查询一次(而非逐字节);证明链按发布事件窗口追加一次(而非逐帧),以约束链增长(见下文 2.5 的量化分析)。

二、决策:四个新抽象,覆盖在既有字节契约之上

2.1 PrivacyMode:五个具名变体,叠加在 PrivacyClass 之上

设计在 mode.rs(落地为 privacy_mode.rs)引入 PrivacyMode,它是既有 4 级 PrivacyClass 之上的语义抽象,在线上不新增任何字节。ADR 给出的设计草图定义了五个变体及其关键方法(代码中实际方法名略有演化,见第三节的对照):

// v2/crates/wifi-densepose-bfld/src/privacy_mode.rs(落地版本语义)
pub enum PrivacyMode {
    RawResearch,          // 本地研究:Raw BFI 保留、绝不联网 → Raw
    PrivateHome,          // 单家庭生产:匿名感知、Soul OFF → Anonymous
    EnterpriseAnonymous,  // 多租户/企业:匿名 + 每 seed 盐隔离 → Anonymous
    CareWithConsent,      // 有明确同意的照护部署:身份派生字段在同意后启用 → Derived
    StrictNoIdentity,     // 受监管/无身份:最严格姿态 → Restricted
}

每个模式通过 target_class()(ADR 正文中名为 privacy_class())解析到唯一的 PrivacyClass;通过 soul_signature_enabled() 表达是否启用 Soul Signature(安装 SignatureHasher + 非空 oracle);CareWithConsent 还通过 soul-signature Cargo feature 与真实 SoulMatchOracle 触发 coherence_gate.rs 中为已登记受试者保留的 Recalibrate 豁免。模式另有稳定的字节/字符串 token(as_u8() / 小写蛇形 token),用于 TOML 配置、MQTT 诊断与证明哈希,保证序列化确定性。

为何不把 5 个模式直接压进 4 字节的 class 枚举? 因为 PrivacyClass线上与 sink 强制原语:其字节值被序列化进 BfldFrameHeader、在 sink.rs::check_class、MQTT 路由和 NVS/Matter 表示中被 switch。五个模式中有两个(PrivateHomeEnterpriseAnonymous)解析到同一个 class Anonymous、只在盐域上不同——这在 class 层不可区分,若硬要作为 class 变体,就得为 frame.rs/sink.rs 发明它们本不需要学习的字节语义。模式是严格更高层的概念,绝不能扰动既有字节契约。

2.2 PrivacyAction:被强制变换的词汇表

PrivacyAction 是连接"人话模式"与 privacy_gate.rs 字节级剥离的机器可检查桥梁:

// 语义(落地代码见 privacy_mode.rs)
pub enum PrivacyAction {
    Allow = 0,            // 无额外变换,按所在 class 原样发布
    SuppressIdentity = 1, // 剥离身份派生字段(identity_risk_score、rf_signature_hash)
    ReduceResolution = 2, // 降采样角度/CSI 面(压缩角度矩阵、csi_delta 置零)
    DropRaw = 3,          // 拒绝把 Raw 帧送上网络(结构不变量 I1)
    AggregateOnly = 4,    // 只发聚合感知(存在/运动/计数/置信度)
}

关键设计点:PrivacyAction 实现 Ord,因此一个事件的动作集合可以被归约成最强动作(取最大值),正好满足 ADR-140 那个单值 privacy_action 字段的需求。动作集合与 GateActionidentity_risk.rs 中的 Accept/PredictOnly/Reject/Recalibrate刻意正交GateAction 回答"这个事件风险高到不该发吗?",PrivacyAction 回答"活动中的模式要求对每个事件做什么变换?"。二者可组合——一个模式可以强制 SuppressIdentity,而逐事件门控独立地 Reject

2.3 PrivacyModeRegistry:单一事实源 + 仅追加审计日志

注册表正是第 1.1 节"缺口"所缺失的那个声明对象:它拥有活动模式、模式→动作映射、ACL,以及一份可被 witness 验证器重放的仅追加审计日志。ADR 草图中的核心 API(落地实现见下文)包括:

  • new(mode)——构造时钉住某个模式。生产安全默认是 PrivateHome(解析为 Anonymous,与 emitter 构造默认值一致,保证向后兼容)。
  • active_mode() / active_class() / enforced_actions() / is_action_enforced(action)——只读查询路径,其中 is_action_enforced 正是 ADR-139/ADR-140 决定 privacy_limited_byprivacy_action 时调用的谓词。
  • allows_sink<S: Sink>()——复用既有 sink.rs::check_class,把编译期 ACL 变成"声明在一处的 MQTT/Matter ACL"。
  • record_transition(ts) / audit_log()——记录每次类切换/解析并返回条目;日志以 Vec 为后端,通过 API 表面强制仅追加(无 pop、无索引写)。

审计日志由 Vec<PrivacyAttestationProof> 承载,ADR-010 的哈希链链接在此层完成。只有 record_transition/set_mode 需要 &mut self;发布路径上安全调用的 active_modeis_action_enforced 等均为 &self——这是 AC6 验收标准要求的"ADR-139/140 以只读方式消费"。

2.4 模式 → (Class, Soul 门控, 盐域, MQTT ACL) 全映射表

这是 ADR-141 最核心的"单一地点声明"。每一行由 PrivacyMode::privacy_class()soul_signature_enabled() 及既有 class 门控路由共同强制:

模式 PrivacyClass Soul Signature 盐域 MQTT/HA 暴露(既有路由) 强制执行的动作
RawResearch Raw (0) 每节点 无——class 0 永不联网(I1) Allow
PrivateHome Anonymous (2) 每节点 存在/运动/计数/置信/identity_risk SuppressIdentityDropRaw
EnterpriseAnonymous Anonymous (2) 多 seed 隔离signature_hasher.rs 每 seed site_salt PrivateHome SuppressIdentityDropRawAggregateOnly
CareWithConsent Derived (1) SoulMatchOracle + SignatureHasher 每节点 仅 LAN/研究面——class 1 不在公共主题树 DropRawReduceResolution
StrictNoIdentity Restricted (3) 每节点 仅存在/运动/计数/置信;identity_risk 发布 SuppressIdentityReduceResolutionDropRawAggregateOnly

ADR 对两行做了重点解释:

  • PrivateHomeEnterpriseAnonymous 同为 Anonymous,差别在盐隔离而非 class。 Enterprise 开启多 seed 盐(multiseed_salt()),使得共享租户边界两侧、观测同一人的两个 seed 产生互不相关rf_signature_hash,从而在跨租户共享场景保住 I3(见 signature_hasher.rs 中 per-seed site_salt 派生与 I3 隔离注释)。单家庭不需要。两者都按既有规则在 class 2 发布 identity_risk,但 Enterprise 额外在语义上强制 AggregateOnly,压制五个聚合实体之外的任何区域/聚类级表面。
  • CareWithConsentDerived + Soul 开。 这是唯一解析到 class Derived 的模式,与 lib.rs 中"Required for Soul Signature deployments"的注释一致。它启用 soul-signature Cargo feature(见 Cargo.toml)并安装真实 SoulMatchOracle,使门控对已登记受试者触发 Recalibrate 豁免。class Derived 不在公共 MQTT 主题树上(公共树要求 >= Anonymous),因此经同意的身份数据只停留在 LAN/研究面,DropRawReduceResolution 依然生效——这一点与 sink.rsNetworkSink::MIN_CLASS = Derived 形成有意的纵深防御(路由层比 sink 层更严,见 ADR 后附的 Security & Privacy Review)。

2.5 PrivacyAttestationProof:按 ADR-010 哈希链防篡改

证明赋予"声明的模式确实被恪守"以密码学连续性。它直接复用 ADR-010 witness 链原语:每一条证明都包含前一条的哈希(previous_hash/entry_hash 链接),因此任何插入、删除或重排都会破坏验证。ADR 草图中的链节结构与封链逻辑如下:

// v2/crates/wifi-densepose-bfld/src/attestation.rs(设计草图语义)
pub struct PrivacyAttestationProof {
    pub mode: PrivacyMode,
    pub actions_enforced: Vec<PrivacyAction>,
    pub fields_stripped_per_event: Vec<PrivacyActionRecord>,
    pub prev_hash: [u8; 32],   // 前一条 entry_hash;创世为全零
    pub entry_hash: [u8; 32],  // BLAKE3(mode ‖ actions ‖ records ‖ prev_hash)
}

seal() 计算 entry_hash 时,以定宽/定界方式混入 mode token、各动作字节、每条记录的 timestamp_ns 与最强动作字节,以及被剥离字段名(每个字段名后补 0x00 作无长度前缀的分隔符),最后混入 prev_hashverify_link() 同时校验"prev_hash 与预期一致"和"entry_hash 可由已封字段重算"两条,实现篡改证据。诊断用 short_hash() 输出 "blake3:<16 hex>"(取 entry_hash 前 8 字节),供 HA 诊断实体展示。

链粒度:按窗口而非按帧。 证明链接的是一个事件窗口(例如 BfldEmitter 一个发射周期),而非一帧 CSI。ADR 给出了量化的取舍依据:若以 20 Hz 逐帧链接,一天将增长 1,728,000 条链节/节点——完全不可控;按窗口则把链约束在已发布事件率上,同时仍能证明每一次剥离(窗口内 fields_stripped_per_event 逐条枚举了其中的剥离)。算法选择 BLAKE3 而非 ADR-010 MAT 路径的 SHAKE-256:ADR-010 的"Hash chain"只规定哈希链式结构而非固定算法;BFLD 已用 BLAKE3 为 rf_signature_hash 做 keyed 哈希(见 signature_hasher.rs 与 Cargo.toml 中 blake3 依赖),复用可避免在该可 no_std 的 crate 中引入第二个加密依赖。

2.6 集成进 MQTT Discovery:只读 HA 诊断实体

活动模式与证明哈希以只读诊断实体的形式对外可见,使运维者、监管者或 cognitum-v0 仪表盘无需触碰感知实体就能看到实时隐私姿态。设计要点:

  • render_discovery_payloads() 渲染一个 entity_category = "diagnostic"sensor,unique-id 为 <node>_bfld_privacy_mode,状态主题 ruview/<node>/bfld/privacy_mode/state,负载为紧凑 JSON:{"mode":"care_with_consent","class":"derived","proof":"blake3:<16hex>","actions":["drop_raw","reduce_resolution"]}
  • 发布门控:在每个 class >= Anonymous 时照常发布,额外在 class Raw/Derived 的 LAN 研究面上也发布——因为研究/照护部署恰恰最需要在本地展示自己的证明。公共主题树的 class 门控保持不变;诊断模式实体加到本地诊断面,无论 class 如何,保证证明总能在节点上被检查到。
  • 严格只读:无 command_topic。模式切换属于运维/配置动作(TOML + 重启,见 2.7),绝非 MQTT 写入——与 privacy_gate.rs 的"无 promote"姿态保持一致。实体上展示的是最近封链证明的 short_hash();持有完整链的验证者(未来通过 attestation export CLI 导出)可以从创世到当前哈希确认连续性。

2.7 将注册表接进 Emitter

BfldEmitter 获得一个拥有的 PrivacyModeRegistry,并在每个发射窗口封一条证明链节。设计是追加式的:既有的 emit()/emit_with_oracle() 签名不变,注册表通过新的 builder(with_mode(mode))配置。其副作用链:

  1. with_modemode.target_class() 设定 privacy_class,按 soul_signature_enabled() 安装/清除签名哈希器与 Soul oracle,并钉住注册表;
  2. 每次 emit() 在门控决策之后调用 registry.record_transition(ts)
  3. 构建一个 PrivacyActionRecord枚举隐私门控实际剥掉的字段(例如在 Restricted 下,event.rsapply_privacy_gating() 会把 identity_risk_scorerf_signature_hashNone,得到 fields_stripped = ["identity_risk_score","rf_signature_hash"]);
  4. 调用 PrivacyAttestationProof::seal(...) 并更新链尾 last_proof_hash

ADR 特别强调:默认基线模式 PrivateHome 保留当前的 Anonymous 默认(见 emitter.rs 构造默认值),因此未迁移的调用方行为完全一致,只是多了一条被填充的证明链(这正是验收标准 AC5/T7 的观测等价性来源)。

2.8 下游消费者:ADR-139 / ADR-140 的绑定表

消费者 读取内容 绑定关系
ADR-140 语义状态记录 PrivacyActionRecord::strongest_action 填充记录必填的 privacy_action 字段;证明 entry_hash 填充隐私溯源引用
ADR-139 WorldGraph is_action_enforced(AggregateOnly) / ReduceResolution 被降分辨率或仅聚合降级的区域/边被标记 privacy_limited_by = <mode token>,数字孪生将其判为"隐私降级区"而非"传感器盲区"
ADR-136 流引擎 attestation() 短哈希 阶段边界帧契约可携带活动模式 token,下游阶段无需重推导
ha_discovery.rs / mqtt_topics.rs 活动模式 + short_hash() 只读诊断实体(2.6)

这落实了项目规则"每条语义状态都能追溯到 信号证据 + 模型版本 + 校准版本 + 隐私决策":ADR-141 提供的是"隐私决策"这一半——PrivacyActionRecord(执行了什么)+ 链 entry_hash(证明确实执行了),与来自 ADR-134/ADR-135 的信号/模型/校准溯源并列记录。

三、仓库源码对照:落地状态与设计草图的差异

ADR 文档本身带有"Implementation Status & Integration"与两份后续审查小节(2026-06-14),如实披露了落地进度。以当前仓库为据,可进一步确认:

3.1 已建成并通过测试的积木块

ADR 记载 commit 7d88eb84c(issue #845)已交付:PrivacyMode / PrivacyAction / PrivacyModeRegistry 以及 BLAKE3 哈希链的 PrivacyAttestationProofverify_chain() 可检出篡改),共 6 个测试。源码事实与文档一致:

  • 实现集中在单一文件 privacy_mode.rs(而非 ADR 草图设想的 mode.rs + attestation.rs 两个文件),模块 doc 明确标注"ADR-141 — BFLD privacy control plane"。
  • 活动模式的强制动作用位集表达(action_bits() -> u8,bit i 对应 PrivacyAction::ALL[i]),并提供 enforces(action) 判断——比 ADR 草图的 Vec<PrivacyAction> 更紧凑,也更适合 no_std。
  • 注册表以 #[cfg(feature = "std")] 门控(审计日志是堆分配的 Vec),与 crate 惯例一致——ESP32-S3 的 no_std 单体路径采用固定模式姿态、不带可增长日志(见 frame.rs)。
  • PrivacyAttestationProof 直接暴露 modeaction_bitsclassprev_hashhash 五元组,compute 哈希公式为 BLAKE3(prev_hash ‖ mode_byte ‖ action_bits ‖ class_byte)PrivacyModeRegistry 提供 new(initial)自动落创世证明)、set_mode()(切换并追加链节)、latest_proof()audit_log()verify_chain()
  • 文档记载的单元测试可逐一对上:mode_to_class_mappingsoul_signature_only_in_raw_and_careaction_enforcementregistry_tracks_active_and_actionsattestation_chain_is_continuous_and_verifiable(验证 prev_hash 逐链衔接、创世 prev 为全零)、tampered_chain_fails_verification(篡改中间节点的 modeverify_chain() 失败)——均位于 privacy_mode.rs 内嵌的 mod tests
  • crate 版本号已推进到 0.3.1,并在 Cargo.toml 版本注释中标注"ADR-141: privacy control plane (modes/actions/attestation)";lib.rs 重新导出 PrivacyModePrivacyActionPrivacyAttestationProof 及 std 门控的 PrivacyModeRegistry

3.2 需如实标注的"文档 vs 代码"差异

对照 ADR 正文的代码草图与当前源码,存在若干实现层面的演变,引用代码时应以源码为准:

  • 方法命名演化:ADR 草图的 PrivacyMode::privacy_class() 在落地实现中为 target_class();草图里的 PrivacyAttestationProof::seal(...)/verify_link()/short_hash() 演化为基于位集的 compute()(std 门控)与注册表层的 verify_chain()
  • Soul Signature 的开启集合略有出入:ADR §2.4 表格标注 RawResearch 为 Soul off、仅 CareWithConsent 开启;而 privacy_mode.rssoul_signature_enabled() 返回 true 的模式是 RawResearch | CareWithConsent(对应测试 soul_signature_only_in_raw_and_care 同时断言两者为真、PrivateHome/StrictNoIdentity 为假)。
  • CareWithConsent 的动作集:ADR 表格列为 DropRaw + ReduceResolution;代码中其 action_bits() 返回 1(即仅 Allow,注释为"Allow (consent granted)"——在已获同意前提下放行身份派生),而 target_class() 仍是 Derived。即代码层面的动作集比 ADR 正文草图表更宽松。读者需要知道:ADR 是设计决策记录,仓库代码是此后落地的产物,二者在个别语义上尚未完全对齐,这正是文档末尾"Integration glue — not yet on the live path"所指出的推进空间。

3.3 集成粘合尚未登上实时 20 Hz 路径

ADR 文档两次如实声明(Implementation Status 与 2026-06-14 Security & Privacy Review 的 Observation):

  • 注册表尚未接入发射路径:当前 BfldEmitter/pipeline 仍直接强制原始 PrivacyClassemitter.rs 构造默认 AnonymousBfldEvent::apply_privacy_gating 在 Restricted 置空身份字段等),class 字节级强制(emitter + event + renderers + 修正后的 process_to_frame)才是当下的线上保证PrivacyModeRegistry 已被导出并单元测试,但目前处于 advisory 地位。
  • MQTT discovery 诊断实体与 PrivacyGate 类切换中的记录钩子属于"待接线的粘合层"。

与此同时,该 crate 在 2026-06-14 的超越 SOTA 安全评审中完成了几项真实加固(下文四、五节详述),说明隐私强制路径本身在被持续打磨。因此,阅读/引用本 ADR 时建议PrivacyMode 系列类型视为已交付、可测试、但尚未成为运行时唯一强制入口的策略骨架

四、后果分析

4.1 正面后果

  • 单一声明策略对象。部署的隐私姿态从此是一个具名 PrivacyMode + 一个 PrivacyModeRegistry,而非四处分散的接线决策。安装者选 CareWithConsent,注册表便确定性地推导出 class、Soul 门控、盐域与 ACL。
  • 密码学连续性PrivacyAttestationProof 让"我们以 StrictNoIdentity 运行且对每个事件剥离了身份字段"从代码评审断言变成可验证的声明;链复用 ADR-010 原语,既有 witness 验证器可自然扩展。
  • 监管/运维可见性。只读 HA 诊断实体在不扩大感知面的前提下暴露实时模式与证明哈希——对养老院合规审计尤为有用。
  • 干净的 ADR-139/ADR-140 绑定privacy_actionprivacy_limited_by 有了具体、可查询的来源(is_action_enforcedstrongest_action),语义状态的追溯要求就此闭合。
  • 零线上改动PrivacyClass 字节值、BfldFrameHeadersink.rs ACL 与 MQTT 主题树均未被触碰,模式纯属追加式抽象。

4.2 负面后果

  • 两个同 class 模式PrivateHomeEnterpriseAnonymous 都解析为 Anonymous,其差异(盐域、AggregateOnly)活在 class 层之上,只有当下游消费方尊重动作集时才有意义;只看 PrivacyClass 的消费方无法区分二者。
  • 链增长。即便按窗口封链,繁忙节点仍会积累证明链节,需要导出/修剪策略(验证导出后的创世重锚定),ADR 明确将其推迟到后续迭代。
  • emitter 状态增加。emitter 现在持有注册表与链尾,内存足迹变大,emit() 也不再是输入→事件的纯变换;每次封链的 BLAKE3 开销亚微秒级但非零。
  • 改模式必须重启。设计上不存在运行时改模式的 MQTT 命令主题(镜像 privacy_gate.rs 的无 promote 姿态),运维通过 TOML 配置 + 重启变更模式,比仪表盘开关更重。

4.3 风险与缓解

风险 概率 影响 缓解
模式→动作映射与 privacy_gate.rs 实际剥离漂移,证明"作证"了并未真正删除的字段 证明撒谎——比没有证明更糟 fields_stripped实际门控输出(event.rs/privacy_gate.rs 返回值)填充而非查模式表;用单测断言记录的剥离 == 门控实际置零的字节
EnterpriseAnonymous 多 seed 盐未真正隔离(两 seed 共享盐)→ 同 class 下破坏 I3 跨单元身份关联 multiseed_salt() 门控 per-seed site_salt 派生;验收测试断言跨 seed 汉明距离 ≈128 位(复用 ADR-120 §2.7 AC2)
链创世混淆:部署中途重启的节点新建创世,破坏与前链的连续性 验证者看到无法与篡改区分的断点 创世链节记录 prev_hash = [0;32] 与启动纪元;验证者把带已记录重启事件的创世链节视为合法重锚定而非断裂
运维选 RawResearch 却假设 raw 永不联网,但误配的自定义 Sink 接受 class 0 I1 被破坏 RawResearchDropRaw 与编译期 sink.rs ACL(MIN_CLASS)冗余;注册表 allows_sink::<NetworkKind>()Raw 返回 false,构成运行时第二道防线

五、备选方案为何被否决

ADR 逐一记录并否决了五个备选路径,每一条都值得部署决策者了解:

  1. PrivacyClass 扩到五个以上变体——被否:class 是线与 sink 强制原语,字节值序列化进 BfldFrameHeader,被 sink.rs::check_class、MQTT 路由和 NVS/Matter 表示 switch;两个同 class 模式在字节层不可分,硬分就会给 frame.rs/sink.rs 发明不存在的字节语义。
  2. 逐帧证明链——被否:20 Hz × 86,400 s = 每天每节点 170 万条链节,无界增长;按窗口粒度(2.5)在事件发布率量级上证明每一次剥离,不丢证据。
  3. 复用 GateAction 代替新 PrivacyAction 枚举——被否:GateAction 回答的是逐事件风险门控问题,复用之会把"此事件有风险"与"此模式每事件剥离身份"混为一谈,丢失正交性,也破坏 ADR-140 所需的、独立于逐事件风险的稳定 privacy_action 值。
  4. 通过 MQTT command_topic 运行时改模式——被否:理由与 privacy_gate.rs 无 promote 相同——一个默认未认证的远程 MQTT 写操作若能削弱隐私(如 StrictNoIdentityRawResearch),就是特权提升面。模式是配置期 + 重启决策,诊断实体只读。
  5. 为精确对齐 ADR-010 用 SHAKE-256 而非复用 BLAKE3——被否:ADR-010 MAT 路径用 SHAKE-256,但该 crate 是 #![cfg_attr(not(feature = "std"), no_std)],引入第二套加密依赖得不偿失;ADR-010 只要求哈希链结构而非固定算法,BFLD 本就用 BLAKE3 做 rf_signature_hash,复用可在最小 no_std 足迹下满足链接/防篡改契约。

六、测试计划与验收标准(文档原文核心资产)

6.1 九项测试计划

  • T1 模式→class/Soul/盐映射(单元):对五个变体逐一断言 privacy_class()soul_signature_enabled()multiseed_salt() 精确匹配 §2.4 表;断言 token() 可经 from_token() 解析往返。
  • T2 规范动作集(单元):对每模式断言 actions_for(mode) 等于 §2.4"强制执行的动作"列、按 Ord 升序且去重;对全部 25 个(模式 × 动作)组合断言 is_action_enforced 与集合成员关系一致。
  • T3 ACL 与 sink.rs 一致(单元):断言 allows_sink::<LocalKind>()::<NetworkKind>()::<MatterKind>()check_class::<S>(privacy_class()).is_ok() 恒等;尤其 RawResearch.allows_sink::<NetworkKind>() == false(I1)。
  • T4 证明链链接(单元):封创世(prev_hash = [0;32])再加三节、逐节穿链;断言四节 verify_link() 对正确前驱全过;篡改某节 mode 后失败;插入/删除/重排后验证断裂。
  • T5 记录剥离 == 实际门控输出(单元)with_mode(StrictNoIdentity) 发射一个本会携带 identity_risk_score + rf_signature_hash 的事件,同时断言 (a) 发射事件的这两个字段为 None,且 (b) 封链的 PrivacyActionRecord.fields_stripped == ["identity_risk_score","rf_signature_hash"](排序)——证明作证的是真实剥离而非表格声称。
  • T6 多 seed 盐隔离(单元,复用 ADR-120 AC2):两个 EnterpriseAnonymous emitter 以不同 per-seed 盐观测相同身份特征,rf_signature_hash 汉明距离落在 [112, 144] 位(期望 ≈128);同测试在 PrivateHome 共享节点盐下不要求隔离(记录二者差异)。
  • T7 默认模式向后兼容(单元):未调用 with_mode()BfldEmitter::new(node_id) 行为与今日完全一致(class Anonymous)且注册表报告 active_mode() == PrivateHome
  • T8 HA 诊断实体渲染(单元)render_discovery_payloads() 产出 entity_category = "diagnostic"、无 command_topic、状态 JSON 含模式 token/class/short_hash()/动作 token 的 privacy_mode 诊断传感器;断言公共感知树输出与改动前逐字节一致(对 mqtt_topics.rs 无回归)。
  • T9 确定性证明(CI,扩展 ADR-028):从硬编码模式序列封一条固定 4 节链,断言最终 entry_hash 匹配 archive/v1/data/proof/expected_features.sha256bfld_attestation_chain_v1 键记录的 SHA-256-of-bytes 常量,令证明哈希端到端确定。

6.2 验收标准

  • AC1:五个模式全部解析出 §2.4 精确的 (class, Soul, salt, ACL, actions) 元组——T1/T2/T3 绿。
  • AC2:证明链防篡改——任一单字段变更、插入、删除或重排都令 verify_link() 失败——T4 绿。
  • AC3:每个事件的 fields_stripped 等于门控实际置零的字段集合(无证明撒谎)——T5 绿。
  • AC4EnterpriseAnonymous 跨 seed 保住 I3(跨 seed 汉明 ≈128 位)——T6 绿。
  • AC5:未迁移的 BfldEmitter::new() 除多一条填充的证明链外观测上与今日完全一致——T7 绿;公共 MQTT 树逐字节一致——T8 绿。
  • AC6is_action_enforcedstrongest_action 可在不取 &mut(读路径为 &self)的情况下被 ADR-139/140 调用。

6.3 Witness / 证明(ADR-028/ADR-010)

文档给 witness 日志新增三行:W-39(模式→动作映射全且符合 §2.4,证据为 cargo test -p wifi-densepose-bfld mode::tests::mapping_table)、W-40(链防篡改,...attestation::tests::tamper_breaks_chain)、W-41(记录剥离 == 实际门控输出,...attestation::tests::strips_match_gate);witness bundle 的 source-hashes.txt 增加 SHA-256(mode.rs)SHA-256(attestation.rs)。(注意:上述测试名与模块路径对应 ADR 文档记载的原始布局;当前代码将这些测试整合于 privacy_mode.rsmod tests,如 attestation_chain_is_continuous_and_verifiabletampered_chain_fails_verification。)

七、ADR 后续的两份审查记录(2026-06-14)

文档末节如实追加了两份超越 SOTA 级评审结论,它们是理解本控制平面"可信度"的关键上下文,也直接修正了强制路径上的两个真实缺陷:

隐私单调性评审(结论:干净)。针对 wifi-densepose-engine::StreamingEngine::process_cycle_calibrated 治理信任环路的检查确认:单调性不变量成立——降级只会让发射 class 更严格,绝不更宽松。每周期从活动模式的 target_class()(下限)现算 effective_class,最多执行单步降级(demote_one,钳制于 Restricted),无跨周期状态允许宽松 class 覆盖严格 class。由 forced_contradiction_never_relaxes_class 这一对全部五个 PrivacyMode 的属性测试钉住:强制矛盾(校准失配/阵列几何不足/网格分区风险,ADR-032)抬升 class 字节,干净周期精确发射基础 class。fail-closed 边界亦被钉住:空周期报错(empty_cycle_fails_closed),单节点边界被刻画为合法非降级模式(single_node_cycle_is_well_formed)。同轮 witness 域分离修复记录在 ADR-137(witness 折叠 effective_class,使降级可审计)。

隐私与安全评审(两个真实 bug 已修,均以 fails-on-old 测试钉住)

# 严重度 位置 问题 修复 钉住测试
1 隐私绕过(HIGH) pipeline.rs::process_to_frame 文档化的线上字节生产路径给帧头盖上活动 PrivacyClass,却经 BfldFrame::from_payload 原样序列化调用方 payload——从未经过 PrivacyGate::demote。标成 Anonymous(2)/Restricted(3) 的帧仍携带完整 compressed_angle_matrix(身份面)+ 幅相 + csi_delta;而 NetworkSink 接受 class >= Derived(1),身份面可带着限制性 class 字节越出节点边界——字节在内容上撒谎 构造后应用 PrivacyGate::demote(frame, active_class):同 class 切换即剥离该 class 禁止的区段;Raw/Derived 保留完整 payload pipeline_to_frame.rsprocess_to_frame_at_anonymous_strips_identity_leaky_sections..._in_privacy_mode_strips_amplitude_and_phase(修复前失败);..._at_derived_preserves_full_payload(防过度剥离护栏)
2 PII/注入(MEDIUM) mqtt_topics.rs::render_events zone_activity 负载以 format!("\"{zone}\"") 拼接、无 JSON 转义(而 ha_discovery.rs 已转义)。含 "/\ 的区域名在 HA 状态主题上产生畸形/可注入 JSON 引入镜像 ha_discovery::push_str_fieldjson_string_literal() 转义器;正常区域名值等价 mqtt_topic_routing.rs::zone_payload_escapes_json_metacharacters(修复前失败)

此外还确认若干维度干净且有证据:事件字段隐私门控(BfldEvent::apply_privacy_gating 在 Restricted 置空 identity_risk_score + rf_signature_hash,且 serde(skip_serializing_if = "Option::is_none") 使它们被整体省略,隐私门控事件与"从未设置该字段"的事件观测上不可区分);witness/哈希成帧(SignatureHasher::compute 用固定 4 字节 day_epoch + 定宽 canonical-f32 特征块,PrivacyAttestationProof 哈希定长 32 字节 prev_hash + 三个定长 1 字节值,无变长、可受操作者影响的字符串被拼进任何摘要——长度前缀成帧碰撞不可能);fail-closed(payload.rs::from_bytes 拒绝截断/溢出/尾随字节,frame.rs::from_bytes 校验 magic/version/length/CRC,PrivacyClass::try_from 拒绝未知字节,identity_risk::score 将 NaN/退化因子映射为 0.0 等)。评审还记录了一条"观察(非 bug)":ADR-141 控制平面尚未接入发射路径,class 字节级强制才是当下的线上保证——与本文 3.3 的结论相互印证。

八、相关 ADR 的关系网

ADR 关系
ADR-010(Witness Chains) 复用PrivacyAttestationProof 采用其哈希链原语(prev/entry hash 链接);BFLD 用 BLAKE3 而非 SHAKE-256
ADR-118(BFLD) 扩展:在既有管线上叠加 modes/actions/attestation 层;I1/I2/I3 不变
ADR-120(Privacy Class + Hash Rotation) 扩展PrivacyMode 映射到 PrivacyClassEnterpriseAnonymous 把多 seed site_salt 隔离形式化
ADR-121(Identity-Risk Scoring) 组合PrivacyActionGateAction 正交;CareWithConsent 启用 Soul 门控豁免
ADR-122(RuView HA/Matter Exposure) 扩展:新增只读 privacy_mode 诊断实体;公共树不变
ADR-136(Streaming Engine) 消费方:活动模式 token 可随阶段边界帧契约传播
ADR-139(WorldGraph) 消费方is_action_enforced(ReduceResolution/AggregateOnly) 驱动 privacy_limited_by 区域/边标记
ADR-140(Semantic State Record) 消费方strongest_action 填充 privacy_action;链 entry_hash 作为隐私溯源引用
ADR-143(RF SLAM v2) 约束:反射体/锚点表面在活动模式下受 ReduceResolution/AggregateOnly 约束

九、实践要点小结

给打算在 RuView/BFLD 上做隐私合规部署或二次开发的工程师,最值得记住的五点:

  1. 模式是配置,class 是字节:运维选 PrivacyMode(TOML + 重启),系统推导出 PrivacyClassPrivacyClass 仍是线上唯一的 class 字节,不要绕过 PrivacyGate::demote 或 sink ACL 自行"贴标签"——HIGH 级 process_to_frame 绕过缺陷证明了这条路径的真实危害(修复已由 pipeline.rs 相关测试钉住)。
  2. 证明可信的关键在"记录真实剥离"fields_stripped 必须取自门控实际输出而非模式表,否则证明会"撒谎";AC3/T5 是这条线的验收标准。
  3. PrivacyClass 不够,还要看动作集PrivateHomeEnterpriseAnonymous 同 class、不同盐域与聚合策略,只有消费 is_action_enforced 的下游才能区分。
  4. 当前运行时强制仍以 class 字节为主:注册表已交付、单元测试完备(见 privacy_mode.rs 内嵌测试),但"接线进 emitter/HA 诊断实体"是文档明示的下一步;引用本设计时请勿把"已测试的骨架"描述成"已在 20 Hz 主路径上生效的控制面"。
  5. 想从创世验证到当前哈希:HA 诊断实体只展示 short_hash(),完整链验证需要后续的导出工具;在此之前,链的防篡改性质已由 verify_chain()(逐节 prev_hash 衔接 + 重算比对)在单元层保证。
登录后查看全文
热门项目推荐
相关项目推荐