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 集合內的相關條款)。