Faying Protocol/1.0 — §3 Components and Interfaces

协议正文导航:章节目录 | 上一章 §2 架构 | 下一章 §4 数据模型

§3 Components and Interfaces

协议层有六个核心组件:Mandate Service(MS)/ Presence Channel(PC)/ Attestation Chain(AC)/ Revocation Registry(RR)/ Liveness Channel(LC)/ Behavior Baseline Service(BBS)。每个组件下面分三部分讲:

  • 职责:组件在协议中的位置与边界;
  • TypeScript 接口骨架:人读视图(Non-normative),权威定义见 §4 Data Models 与 §5 Protocol Messages;
  • 不变式:协议层必须始终满足的硬约束,每条不变式都可追溯到 §Requirements 中的至少一条 EARS 子条款。

EARS 追溯说明:本章每条不变式后以 Trace: 列出其对应的 EARS 子条款编号(出处见本规范需求基线),形式为 R<requirement>.<criterion>.<clause>。

§3.0 共享类型前向声明(Non-normative)

为使本章 TypeScript 接口块可独立通过 tsc --noEmit 编译,下方代码块以最小骨架形式前向声明本章引用的支撑类型;这些类型的权威定义见 §4 Data Models。

// §3.0 — Non-normative forward declarations.
// Authoritative definitions live in §4 Data Models.

export type IsoTime = string;
export type MandateId = string;
export type FayID = string;
export type HumanPrimeID = string;
export type DeviceId = string;

export type AuthorizationGrade = "G0" | "G1" | "G2" | "G3" | "G4";

export interface Scope {
  readonly domains: readonly string[];
  readonly actions: readonly string[];
  readonly terminals?: readonly string[];
  readonly budget?: ResourceBudget;
}

export interface ResourceBudget {
  readonly max_calls_per_minute?: number;
  readonly max_bytes_per_call?: number;
}

export interface Grade {
  readonly value: AuthorizationGrade;
  readonly autonomy: number; // 数值越大自主性越高(G0 最低,G4 最高)
}

export interface PresenceRequirement {
  readonly signal_types: readonly PresenceSignalType[];
  readonly max_staleness_ms: number;
  readonly on_loss: "downgrade" | "suspend";
}

export type PresenceSignalType =
  | "webauthn-uv-fresh"
  | "face-continuous"
  | "fingerprint-tap"
  | "presence-heartbeat-only"
  | "intent-ticket";

export interface SignatureBundle {
  readonly alg: string;
  readonly hash: string;
  readonly sig: Uint8Array;
  readonly chain?: readonly SignatureBundle[];
}

export interface Endorsement {
  readonly enabled: boolean;
  readonly endorsed_scope: Scope;
  readonly endorsed_grade: Grade;
  readonly endorsed_lifetime_ms: number;
  readonly offline_revocation_policy:
    | "stop-and-audit"
    | "stop-only"
    | "graceful-stop";
  readonly start_at?: IsoTime;
}

export interface Mandate {
  readonly mandate_id: MandateId;
  readonly issuer: HumanPrimeID;
  readonly subject: FayID;
  readonly scope: Scope;
  readonly grade: Grade;
  readonly presence_req: PresenceRequirement;
  readonly device_id?: DeviceId;
  readonly not_before: IsoTime;
  readonly not_after: IsoTime;
  readonly parent?: MandateId;
  readonly endorsement?: Endorsement;
  readonly signature_bundle: SignatureBundle;
}

export interface PresenceSignal {
  readonly mandate_id: MandateId;
  readonly signal_type: PresenceSignalType;
  readonly nonce: Uint8Array;
  readonly produced_at: IsoTime;
  readonly device_attestation: Uint8Array;
  readonly challenge?: Uint8Array;
  readonly challenge_response?: Uint8Array;
  readonly signature_bundle: SignatureBundle;
}

export interface PresenceVerdict {
  readonly accepted: boolean;
  readonly verdict: "ok" | "stale" | "replay" | "attestation-invalid";
  readonly recommended_next_grade?: AuthorizationGrade;
}

export interface PresenceAck {
  readonly accepted: boolean;
  readonly mandate_id: MandateId;
  readonly observed_at: IsoTime;
}

export interface PresenceState {
  readonly mandate_id: MandateId;
  readonly last_freshness: IsoTime;
  readonly stale: boolean;
  readonly active_signal_types: readonly PresenceSignalType[];
}

export interface BootstrapRequest {
  readonly subject: FayID;
  readonly scope: Scope;
  readonly grade: Grade;
  readonly presence_req: PresenceRequirement;
  readonly not_before: IsoTime;
  readonly not_after: IsoTime;
  readonly human_witness: Uint8Array; // WebAuthn assertion
  readonly endorsement?: Endorsement;
  readonly faying_request_ref?: string;
}

export interface GradeSwitchRequest {
  readonly mandate_id: MandateId;
  readonly target_grade: Grade;
  readonly current_grade: Grade;
  readonly human_witness?: Uint8Array;
  readonly reason: string;
}

export interface DelegateRequest {
  readonly parent_mandate_id: MandateId;
  readonly child_subject: FayID;
  readonly child_scope: Scope;
  readonly child_grade: Grade;
  readonly device_id?: DeviceId;
  readonly not_before: IsoTime;
  readonly not_after: IsoTime;
}

export type RevokeReason =
  | "human-initiated"
  | "expiry"
  | "task-completed"
  | "human-prime-unreachable"
  | "ego-deviation"
  | "fay-credential-invalid"
  | "human-prime-credential-invalid"
  | "regulatory-order"
  | "terminal-disconnected";

export interface RevokeRequest {
  readonly mandate_id: MandateId;
  readonly reason: RevokeReason;
  readonly cascade: boolean;
  readonly human_witness?: Uint8Array;
}

export interface RevokeReceipt {
  readonly mandate_id: MandateId;
  readonly reason: RevokeReason;
  readonly revoked_at: IsoTime;
  readonly cascade_targets: readonly MandateId[];
  readonly signature_bundle: SignatureBundle;
}

export interface RevocationStatus {
  readonly mandate_id: MandateId;
  readonly revoked: boolean;
  readonly revoked_at?: IsoTime;
  readonly reason?: RevokeReason;
  readonly observed_at: IsoTime;
}

export interface ScopeFilter {
  readonly subjects?: readonly FayID[];
  readonly issuers?: readonly HumanPrimeID[];
}

export interface Subscription {
  readonly subscription_id: string;
  readonly close: () => void;
}

export interface AtomicAction {
  readonly action: string;
  readonly target?: string;
  readonly sensitivity: "low" | "normal" | "high" | "critical";
}

§3.1 Mandate Service(MS)

职责:代表 Human Prime 进行 Mandate 全生命周期管理(颁发 / 续期 / 降级 / 撤销);是 Faying Action 的唯一合法入口。

接口(Non-normative):

export interface MandateService {
  /** Faying Action 的唯一合法入口 */
  bootstrap(req: BootstrapRequest): Promise<Mandate>;

  /** 在场信号校准;返回当前 Mandate 是否仍有效 / 应否降级 */
  verifyPresence(
    mandateId: MandateId,
    signal: PresenceSignal
  ): Promise<PresenceVerdict>;

  /** 等级切换:升级必须 HP 见证;降级可由协议自动触发 */
  switchGrade(req: GradeSwitchRequest): Promise<Mandate>;

  /** 跨设备派生:从父 Mandate 派生子 Mandate,scope ⊆ 父 scope,grade ≤ 父 grade */
  delegate(req: DelegateRequest): Promise<Mandate>;

  /** 对称撤销:可达性必须 ≥ bootstrap */
  revoke(req: RevokeRequest): Promise<RevokeReceipt>;
}

关键不变式:

  • I-MS-1(HP 锚定的 Bootstrap):bootstrap() 的请求方必须携带可验证的 HP attestation(默认形态为 WebAuthn 用户在场断言);不存在"MS 自我决定 bootstrap"或由 IF / SA 替代 HP 完成认可确认的合法路径。

    • Trace:R1.3.1(HP 主动发起须出示可验证的明确认可确认)、R1.3.5(不提供任何由 IF / MS / SA 替代 HP 完成 Faying 认可确认的路径)。
  • I-MS-2(升级方向的 HP 见证强制):switchGrade() 当且仅当 target.autonomy > current.autonomy 时必须携带可验证的 human_witness;缺失或验签失败一律 E_HUMAN_WITNESS_REQUIRED,且 IF 不得自主发起 grade 升级。

    • Trace:R7.1.1(child.scope ⊆ parent.scope 收敛——隐含等级升降语义中的非扩张约束,与降级单调性原则一致)、R5.1.1(基于 presence_req 的接受前置条件,是 switchGrade 写入 presence_req 字段后被 RP 校验的依据)。
    • 备注:本不变式还由 R6.1(升级必带 HP 见证)权威覆盖;本章列举的 EARS 追溯仅取本任务声明的 Requirement 集合内的相关条款。
  • I-MS-3(撤销可见性预算):revoke() 一旦由 RR publish,对所有后续 RP 的 query 在不超过 RevocationVisibilityBudget(默认 1s,按 grade 收紧;详见 §6 / 附录 B)内必须返回 revoked;MS 不得提供"乐观放行"路径。

    • Trace:R4.2.1(撤销可见性预算 ≤ RevocationVisibilityBudget)、R4.2.4(StaleBudget 内未确认 → Default-Deny)。
  • I-MS-4(派生 scope / grade 严格收敛):delegate() 颁发的子 Mandate 必须满足 child.scope ⊆ parent.scope ∧ child.grade.autonomy ≤ parent.grade.autonomy;任何方向上的扩张以 E_PROTOCOL 拒绝,且子 Mandate 的 signature_bundle.chain 必须携带父 Mandate 副签。

    • Trace:R7.1.1(子 scope ⊆ 父 scope)、R7.1.2(子 autonomy ≤ 父 autonomy)。

§3.2 Presence Channel(PC)

职责:把 Presence Device 产生的物理在场信号,安全、低延迟、抗重放地送达 MS,并由 MS 转化为对当前 Mandate 的"持续校准"判定。

信号类型(与 §7 等级对齐):

类型强度适用等级失败时降级路径
webauthn-uv-fresh强G1 Strict失败 → Suspended
face-continuous强G1 Strict失败 → 降为 G2 Moderate
fingerprint-tap中G2 Moderate失败 → 降为 G3 Loose
presence-heartbeat-only弱G3 Loose / G4 Autonomous失败 → Suspended
intent-ticket一次性跨等级补充一次性消耗,不影响主等级

接口(Non-normative):

export interface PresenceChannel {
  /** PD 发起;MS 校验后写入对应 Mandate 的 PresenceState */
  push(signal: PresenceSignal): Promise<PresenceAck>;

  /** RP 在调用前查询当前 Presence 是否新鲜 */
  query(mandateId: MandateId): Promise<PresenceState>;
}

关键不变式:

  • I-PC-1(信号强制三元组 + Mandate 侧新鲜度窗口):每个 PresenceSignal 必须显式携带 nonce + produced_at + device_attestation,并由 Presence_Device 签名;新鲜度窗口不由信号自带,而由其对应 Mandate 的 presence_req.max_staleness_ms 给定(绑定 grade、由 HP 在 BOOTSTRAP 锚定、受 LivenessDetectionBudget 按 grade 收紧)。produced_at + presence_req.max_staleness_ms < now 的信号一律视为 stale。

    • Trace:R5.1.3(nonce / produced_at / device_attestation 字段强制)、R5.1.4(PC 在校验阶段拒绝过期信号)。
  • I-PC-2(同 nonce 单次消费):同一 (subject, nonce) 在 2 × max_message_lifetime TTL 窗口内至多被接受一次;重复消费 → E_REPLAY 拒绝。

    • Trace:R5.1.1(last_freshness 必须满足新鲜度——隐含 nonce + 时间戳的抗重放语义在 PC 层落地)、R5.1.3(nonce 字段是抗重放的协议级抓手)。
  • I-PC-3(在场新鲜度自动收紧):PresenceState.last_freshness 必须 ≥ now − presence_req.max_staleness_ms;不满足时 PC 必须使下一次状态转换迁移到 Degraded(grade')(grade'.autonomy < current.autonomy)或 Suspended,禁止维持原 grade,禁止切换到自主性更高的方向。

    • Trace:R5.1.1(接受前置条件 last_freshness ≥ t − max_staleness_ms)、R12.2.2(以新鲜度而非心跳轮询周期作为放行依据)。

§3.3 Attestation Chain(AC)

职责:把 HP → MS → IF → 派生 IF 的全部签发动作组织为一条可逆向追溯的签名链,使任意一个 Mandate 都可被独立第三方还原其授权来源。

链结构(示意):

Root: HP-Public-Key(自托管 / 锚定到 WebAuthn 凭据)
  └─ MS-Cert(HP 签名,绑定 MS 公钥与权限)
       └─ Mandate M1(MS 签名)
            ├─ Mandate M2(M1 派生,MS 副签 + 父 Mandate 副签)
            └─ Mandate M3(M1 派生)

接口(Non-normative):

export interface AttestationChain {
  /** 校验自叶到根的完整链 */
  verify(mandate: Mandate): Promise<AttestationVerifyResult>;

  /** 追加一条链上事件(Bootstrap / GradeSwitch / Delegate / Revoke) */
  append(event: AttestationEvent): Promise<void>;

  /** 还原指定 Mandate 的责任承接端 */
  resolveAccountableParty(mandateId: MandateId): Promise<HumanPrimeID>;
}

export interface AttestationVerifyResult {
  readonly verified: boolean;
  readonly root: HumanPrimeID;
  readonly invalid_node?: string;
}

export interface AttestationEvent {
  readonly event_type: "bootstrap" | "grade-switch" | "delegate" | "revoke";
  readonly mandate_id: MandateId;
  readonly issuer: HumanPrimeID;
  readonly subject: FayID;
  readonly ts: IsoTime;
  readonly signature_bundle: SignatureBundle;
}

关键不变式:

  • I-AC-1(父签名 + validity window 双重约束):链上每一节点都必须由其父节点签名,且签名时间戳必须落在父节点的 validity window([parent.not_before, parent.not_after])内;任一不满足即 E_ATTESTATION_INVALID 并触发对应 Mandate 的 Revoke。

    • Trace:R3.1.2(自叶到根校验,父节点 validity window 内时间戳)、R3.1.3(任一节点失效以 E_ATTESTATION_INVALID 拒绝并触发 Revoke)。
  • I-AC-2(链完整性,缺一不可):链验证必须自叶到根全部通过;任一节点缺失或签名无效即视为不可信,整条链返回 verified = false。

    • Trace:R3.1.1(Bootstrap / GradeSwitch / Delegate / Revoke 四类事件必须入链)、R3.1.2(链式校验在所有节点均有效时方返回 verify = true)。
  • I-AC-3(派生收敛在链路上不可逆向放宽):在派生过程中,子节点 scope 与 grade.autonomy 必须单调收敛;任何放宽 scope 或抬高 autonomy 的派生节点都不存在合法的链上签名路径。

    • Trace:R7.1.1(子 scope ⊆ 父 scope)、R7.1.2(子 autonomy ≤ 父 autonomy)。

§3.4 Revocation Registry(RR)

职责:实时撤销状态服务。承担"撤销可达性 ≥ 建立可达性"这一对称性原则的工程支点。

双轨设计:

  • 拉:RP 在每次接受调用时执行短轮询(OCSP-like,建议 stapling);
  • 推:MS 撤销后向所有已订阅 RP 发起推送(WebSocket / SSE / MQTT);
  • 保守 fallback:若 RP 在 StaleBudget(默认 5s,按 grade 收紧)内未能确认 RR 状态,必须按 Default-Deny 拒绝该次调用。

接口(Non-normative):

export interface RevocationRegistry {
  publish(receipt: RevokeReceipt): Promise<void>;
  query(mandateId: MandateId): Promise<RevocationStatus>;
  subscribe(rpId: string, scope: ScopeFilter): Subscription;
}

关键不变式:

  • I-RR-1(撤销发布即刻可见):撤销一旦 publish() 完成,对所有后续 query() 在 RevocationVisibilityBudget(默认 1s,按 grade 收紧)内必须立即返回 revoked = true,呈现强一致语义。

    • Trace:R4.2.1(RevocationVisibilityBudget 内 query 返回 revoked)。
  • I-RR-2(不可达 → Default-Deny):当 RR 在 StaleBudget 内不可达,RP 必须以 E_RR_UNAVAILABLE 拒绝调用,禁止"乐观放行"。

    • Trace:R2.1.4(StaleBudget 内不可达 → Default-Deny + E_RR_UNAVAILABLE)、R4.2.4(RP 在状态确认失败时按 Default-Deny 处理)。

语义边界:RR 不可达 ≠ iFay 自身失联。RR 不可达仅表示"无法判定 Mandate 是否被撤销",触发 E_RR_UNAVAILABLE 拒绝;iFay 失联(含 Actually-Disconnected / Perceived-Disconnected)由 §3.5 Liveness Channel 与 PresenceState 单独刻画。两类失联事件互不替代——存在"iFay 在线但 RR 不可达"以及"RR 在线但 iFay 失联"两种正交组合,协议层均按 Default-Deny 收敛。

§3.5 Liveness Channel(LC)

职责:以低开销的心跳 + 在场新鲜度联合判定 iFay_Instance 在 MS / RP / HP 视角下的"感知活性",并把"真实活性"与"感知活性"之间的不一致窗口显式刻画为协议层一等公民。

双语义定义:

  • Actually-Disconnected(真实失联):iFay_Instance 进程 / 网络 / 设备已停止工作或不可被任何外部主体到达。客观物理事实,不可被协议层直接观测。
  • Perceived-Disconnected(感知失联):在 MS / RP / HP 视角下,依据最近的 Presence_Signal、心跳与 RR 推送状态,判定 iFay_Instance 已失联。协议层唯一可作为决策依据的语义。

两类不一致窗口:

  • 窗口 A — Actually-Disconnected ∧ Perceived-Connected:iFay 实际已断,但最近一次 Presence_Signal 仍在 max_staleness_ms 内 → 心跳间隔内的"观察盲区";
  • 窗口 B — Actually-Connected ∧ Perceived-Disconnected:iFay 实际在线,但最近一次 Presence_Signal 已超过 max_staleness_ms(网络抖动 / 心跳丢包)→ 协议层以悲观推定收敛。

悲观推定原则(统一收敛规则):

协议层仅以 Perceived-Disconnected 作为决策依据;任意"真实在线但被感知为失联"的 iFay_Instance 不得据此豁免协议层的判定后果。在两类不一致窗口内,协议必须按"更严格的那一侧"处理;不存在"按更宽松的状态处理"的合法路径。

接口(Non-normative,与 §3.2 PC 共享底层信号):

export interface LivenessChannel {
  /** 当前感知活性快照 */
  perceived(mandateId: MandateId): PerceivedLiveness;

  /** 显式触发悲观判定(如检测到心跳丢失) */
  declareStale(mandateId: MandateId, reason: string): Promise<void>;
}

export interface PerceivedLiveness {
  readonly is_perceived_connected: boolean;
  readonly last_freshness: IsoTime;
  readonly staleness_ms: number;
  readonly decided_by: "presence-signal" | "heartbeat" | "explicit-declare";
}

关键不变式:

  • I-LC-1(新鲜度判定,禁止以心跳轮询周期放行):感知失联判定必须基于 last_freshness ≥ now − max_staleness_ms;禁止以"心跳轮询周期未到"为由继续放行调用。

    • Trace:R12.2.2(以新鲜度判定而非心跳轮询周期作为放行依据)。
  • I-LC-2(进入 Disconnected 的状态机迁移):一旦判定为 Perceived-Disconnected,下一次状态转换必须按 §4.3 状态机迁移(Active → Disconnected / Active → Suspended,依据是否为有效 Endorsed_Mandate);不存在维持原状态或切换到更宽松状态的合法路径。

    • Trace:R12.1.3(仅以感知失联作为协议层判定依据,不豁免协议层后果)。
  • I-LC-3(恢复必须经过事务性对账):从 Perceived-Disconnected 恢复到 Perceived-Connected 必须经过 §8.4 重连对账(Reconciliation)事务;禁止自动恢复。

    • Trace:R12.1.3(感知失联期间不豁免协议层后果——蕴含"恢复需显式事务性确认",与 R12.4.1 / R12.4.3 的对账事务一致;本任务声明的 Requirement 集合内由 12.1 锚定)。

§3.6 Behavior Baseline Service(BBS)

职责:维护针对每对 (Human_Prime, iFay_Instance) 的累积行为画像;用于检测高敏感动作对基线的显著偏离,并在偏离时触发额外的人类见证(详见 §9.10.6 T5 防护)。

接口(Non-normative):

export interface BehaviorBaselineService {
  /** 累积一条新动作样本 */
  record(sample: BehaviorSample): Promise<void>;

  /** 评估当前动作相对基线的偏离度 */
  evaluate(
    hp: HumanPrimeID,
    fay: FayID,
    action: AtomicAction
  ): BaselineDeviation;

  /** 应用 reputation decay(信任分衰减) */
  decay(hp: HumanPrimeID, fay: FayID): Promise<void>;
}

export interface BehaviorSample {
  readonly hp: HumanPrimeID;
  readonly fay: FayID;
  readonly action: AtomicAction;
  readonly scope_used: Scope;
  readonly risk_level: "low" | "normal" | "high" | "critical";
  readonly affected_resources: readonly string[];
  readonly ts: IsoTime;
}

export interface BaselineDeviation {
  readonly exceeds_threshold: boolean;
  readonly dimensions: readonly (
    | "risk_level"
    | "scope_used"
    | "affected_resources"
  )[];
  readonly recommended_action:
    | "allow"
    | "require-extra-witness"
    | "downgrade"
    | "suspend";
}

关键不变式:

  • I-BBS-1(信任分必衰减且有上限):每对 (HP, IF) 的"信任分"必须随时间衰减且存在显式上限,禁止无限累积以稀释未来高敏感动作所需的人类见证强度。

    • Trace:R13.5.3(Action Reputation Decay:信任分必衰减且存在显式上限)。
  • I-BBS-2(超基线见证事件入链 + 推 HP 视野):每次"超基线触发额外见证"的事件必须写入 Attestation_Chain 并推送至 Human_Prime 视野通道;未获额外见证前以 E_HUMAN_WITNESS_REQUIRED 拒绝该次调用。

    • Trace:R13.5.4("超基线触发额外见证"事件写入 AC + 推送 HP 视野)、R13.5.2(未获额外见证前以 E_HUMAN_WITNESS_REQUIRED 拒绝)。
  • I-BBS-3(口径变更也是审计事件):基线计算口径本身一旦确定即写入 Attestation_Chain;口径变更视为一类独立审计事件,由协议委员会按既定流程审批。

    • Trace:R13.5.1(MS 维护 Behavior_Baseline)、R3.1.1(任一审计事件被处理时 AC 必须包含其签名节点;本章列举的 EARS 追溯仅取本任务声明的 Requirement 集合内的相关条款)。