RuView ADR-141 精读:BFLD 隐私控制平面——命名模式、离散动作与哈希链防篡改运行时证明
本文面向部署 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 = 0、Derived = 1、Anonymous = 2、Restricted = 3(见 lib.rs 中PrivacyClass枚举及allows_network()、allows_matter()、as_u8()等成员方法)。这是系统唯一描述"该部署允许发出什么"的词汇表,但它不解释为什么一个节点处于 class 2 而非 class 3,也不记录实际施加了哪些隐私变换。 - privacy_gate.rs 的
PrivacyGate::demote()是单调、清零化的变换器:在每次类切换时剥离compressed_angle_matrix、csi_delta、amplitude_proxy、phase_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.rs 把
privacy_class: PrivacyClass::Anonymous作为构造默认值;Soul Signature 门控是否生效,仅取决于是否注入了一个SoulMatchOracle。
由此产生的真空正是 ADR-141 的出发点:一个部署的隐私姿态被拆进四处(构造出的 PrivacyClass、SoulMatchOracle 有无、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(隐私动作):模式强制的、离散的、可机器检查的隐私变换(如
SuppressIdentity、DropRaw)。动作是"人话模式"与privacy_gate.rs中已有的字节级剥离之间的桥,也是 ADR-140privacy_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。五个模式中有两个(PrivateHome、EnterpriseAnonymous)解析到同一个 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 字段的需求。动作集合与 GateAction(identity_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_by与privacy_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_mode、is_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 |
SuppressIdentity、DropRaw |
EnterpriseAnonymous |
Anonymous (2) |
关 | 多 seed 隔离(signature_hasher.rs 每 seed site_salt) |
同 PrivateHome |
SuppressIdentity、DropRaw、AggregateOnly |
CareWithConsent |
Derived (1) |
开(SoulMatchOracle + SignatureHasher) |
每节点 | 仅 LAN/研究面——class 1 不在公共主题树 | DropRaw、ReduceResolution |
StrictNoIdentity |
Restricted (3) |
关 | 每节点 | 仅存在/运动/计数/置信;identity_risk 不发布 |
SuppressIdentity、ReduceResolution、DropRaw、AggregateOnly |
ADR 对两行做了重点解释:
PrivateHome与EnterpriseAnonymous同为Anonymous,差别在盐隔离而非 class。 Enterprise 开启多 seed 盐(multiseed_salt()),使得共享租户边界两侧、观测同一人的两个 seed 产生互不相关的rf_signature_hash,从而在跨租户共享场景保住 I3(见signature_hasher.rs中 per-seedsite_salt派生与 I3 隔离注释)。单家庭不需要。两者都按既有规则在 class 2 发布identity_risk,但 Enterprise 额外在语义上强制AggregateOnly,压制五个聚合实体之外的任何区域/聚类级表面。CareWithConsent→Derived+ Soul 开。 这是唯一解析到 classDerived的模式,与 lib.rs 中"Required for Soul Signature deployments"的注释一致。它启用soul-signatureCargo feature(见 Cargo.toml)并安装真实SoulMatchOracle,使门控对已登记受试者触发Recalibrate豁免。classDerived不在公共 MQTT 主题树上(公共树要求 >=Anonymous),因此经同意的身份数据只停留在 LAN/研究面,DropRaw与ReduceResolution依然生效——这一点与sink.rs中NetworkSink::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_hash。verify_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时照常发布,额外在 classRaw/Derived的 LAN 研究面上也发布——因为研究/照护部署恰恰最需要在本地展示自己的证明。公共主题树的 class 门控保持不变;诊断模式实体加到本地诊断面,无论 class 如何,保证证明总能在节点上被检查到。 - 严格只读:无
command_topic。模式切换属于运维/配置动作(TOML + 重启,见 2.7),绝非 MQTT 写入——与privacy_gate.rs的"无 promote"姿态保持一致。实体上展示的是最近封链证明的short_hash();持有完整链的验证者(未来通过attestation exportCLI 导出)可以从创世到当前哈希确认连续性。
2.7 将注册表接进 Emitter
BfldEmitter 获得一个拥有的 PrivacyModeRegistry,并在每个发射窗口封一条证明链节。设计是追加式的:既有的 emit()/emit_with_oracle() 签名不变,注册表通过新的 builder(with_mode(mode))配置。其副作用链:
with_mode从mode.target_class()设定privacy_class,按soul_signature_enabled()安装/清除签名哈希器与 Soul oracle,并钉住注册表;- 每次
emit()在门控决策之后调用registry.record_transition(ts); - 构建一个
PrivacyActionRecord,枚举隐私门控实际剥掉的字段(例如在Restricted下,event.rs的apply_privacy_gating()会把identity_risk_score与rf_signature_hash置None,得到fields_stripped = ["identity_risk_score","rf_signature_hash"]); - 调用
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 哈希链的 PrivacyAttestationProof(verify_chain() 可检出篡改),共 6 个测试。源码事实与文档一致:
- 实现集中在单一文件 privacy_mode.rs(而非 ADR 草图设想的
mode.rs+attestation.rs两个文件),模块 doc 明确标注"ADR-141 — BFLD privacy control plane"。 - 活动模式的强制动作用位集表达(
action_bits() -> u8,biti对应PrivacyAction::ALL[i]),并提供enforces(action)判断——比 ADR 草图的Vec<PrivacyAction>更紧凑,也更适合 no_std。 - 注册表以
#[cfg(feature = "std")]门控(审计日志是堆分配的Vec),与 crate 惯例一致——ESP32-S3 的 no_std 单体路径采用固定模式姿态、不带可增长日志(见frame.rs)。 PrivacyAttestationProof直接暴露mode、action_bits、class、prev_hash、hash五元组,compute哈希公式为BLAKE3(prev_hash ‖ mode_byte ‖ action_bits ‖ class_byte);PrivacyModeRegistry提供new(initial)(自动落创世证明)、set_mode()(切换并追加链节)、latest_proof()、audit_log()与verify_chain()。- 文档记载的单元测试可逐一对上:
mode_to_class_mapping、soul_signature_only_in_raw_and_care、action_enforcement、registry_tracks_active_and_actions、attestation_chain_is_continuous_and_verifiable(验证prev_hash逐链衔接、创世 prev 为全零)、tampered_chain_fails_verification(篡改中间节点的mode后verify_chain()失败)——均位于 privacy_mode.rs 内嵌的mod tests。 - crate 版本号已推进到
0.3.1,并在 Cargo.toml 版本注释中标注"ADR-141: privacy control plane (modes/actions/attestation)";lib.rs重新导出PrivacyMode、PrivacyAction、PrivacyAttestationProof及 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.rs 的soul_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 仍直接强制原始PrivacyClass(emitter.rs构造默认Anonymous、BfldEvent::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_action与privacy_limited_by有了具体、可查询的来源(is_action_enforced、strongest_action),语义状态的追溯要求就此闭合。 - 零线上改动。
PrivacyClass字节值、BfldFrameHeader、sink.rsACL 与 MQTT 主题树均未被触碰,模式纯属追加式抽象。
4.2 负面后果
- 两个同 class 模式。
PrivateHome与EnterpriseAnonymous都解析为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 被破坏 | RawResearch 的 DropRaw 与编译期 sink.rs ACL(MIN_CLASS)冗余;注册表 allows_sink::<NetworkKind>() 对 Raw 返回 false,构成运行时第二道防线 |
五、备选方案为何被否决
ADR 逐一记录并否决了五个备选路径,每一条都值得部署决策者了解:
- 把
PrivacyClass扩到五个以上变体——被否:class 是线与 sink 强制原语,字节值序列化进BfldFrameHeader,被sink.rs::check_class、MQTT 路由和 NVS/Matter 表示 switch;两个同 class 模式在字节层不可分,硬分就会给frame.rs/sink.rs发明不存在的字节语义。 - 逐帧证明链——被否:20 Hz × 86,400 s = 每天每节点 170 万条链节,无界增长;按窗口粒度(2.5)在事件发布率量级上证明每一次剥离,不丢证据。
- 复用
GateAction代替新PrivacyAction枚举——被否:GateAction回答的是逐事件风险门控问题,复用之会把"此事件有风险"与"此模式每事件剥离身份"混为一谈,丢失正交性,也破坏 ADR-140 所需的、独立于逐事件风险的稳定privacy_action值。 - 通过 MQTT command_topic 运行时改模式——被否:理由与
privacy_gate.rs无 promote 相同——一个默认未认证的远程 MQTT 写操作若能削弱隐私(如StrictNoIdentity→RawResearch),就是特权提升面。模式是配置期 + 重启决策,诊断实体只读。 - 为精确对齐 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):两个
EnterpriseAnonymousemitter 以不同 per-seed 盐观测相同身份特征,rf_signature_hash汉明距离落在 [112, 144] 位(期望 ≈128);同测试在PrivateHome共享节点盐下不要求隔离(记录二者差异)。 - T7 默认模式向后兼容(单元):未调用
with_mode()的BfldEmitter::new(node_id)行为与今日完全一致(classAnonymous)且注册表报告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.sha256中bfld_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 绿。 - AC4:
EnterpriseAnonymous跨 seed 保住 I3(跨 seed 汉明 ≈128 位)——T6 绿。 - AC5:未迁移的
BfldEmitter::new()除多一条填充的证明链外观测上与今日完全一致——T7 绿;公共 MQTT 树逐字节一致——T8 绿。 - AC6:
is_action_enforced与strongest_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.rs 的 mod tests,如 attestation_chain_is_continuous_and_verifiable、tampered_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.rs 中 process_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_field 的 json_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 映射到 PrivacyClass;EnterpriseAnonymous 把多 seed site_salt 隔离形式化 |
| ADR-121(Identity-Risk Scoring) | 组合:PrivacyAction 与 GateAction 正交;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 上做隐私合规部署或二次开发的工程师,最值得记住的五点:
- 模式是配置,class 是字节:运维选
PrivacyMode(TOML + 重启),系统推导出PrivacyClass;PrivacyClass仍是线上唯一的 class 字节,不要绕过PrivacyGate::demote或 sink ACL 自行"贴标签"——HIGH 级process_to_frame绕过缺陷证明了这条路径的真实危害(修复已由 pipeline.rs 相关测试钉住)。 - 证明可信的关键在"记录真实剥离":
fields_stripped必须取自门控实际输出而非模式表,否则证明会"撒谎";AC3/T5 是这条线的验收标准。 - 看
PrivacyClass不够,还要看动作集:PrivateHome与EnterpriseAnonymous同 class、不同盐域与聚合策略,只有消费is_action_enforced的下游才能区分。 - 当前运行时强制仍以 class 字节为主:注册表已交付、单元测试完备(见 privacy_mode.rs 内嵌测试),但"接线进 emitter/HA 诊断实体"是文档明示的下一步;引用本设计时请勿把"已测试的骨架"描述成"已在 20 Hz 主路径上生效的控制面"。
- 想从创世验证到当前哈希:HA 诊断实体只展示
short_hash(),完整链验证需要后续的导出工具;在此之前,链的防篡改性质已由verify_chain()(逐节prev_hash衔接 + 重算比对)在单元层保证。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0626
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00