# How does an instance detect equivocation when LLM agents sign with non-deterministic outputs **Date of Record:** 2026 November 13 **Scope:** Applies to BFT consensus protocols (PBFT, Tendermint-style, HotStuff variants) where the "signature" or commit attestation from a replica is an LLM-generated natural-language statement rather than a cryptographic primitive, and where replicas are heterogeneous in model family, temperature, and decoding strategy. Does not apply to systems where agent outputs are post-processed through a deterministic canonicalizer before signing, nor to homogeneous fleets running identical weights at temperature 0. ## Core Distinction Cryptographic BFT assumes that a faulty replica's equivocation is *detectable by comparison*: two signed messages from the same key with different payloads are trivially distinguishable, and the signature binds the payload. In heterogeneous LLM consensus, the "signature" is a model output, and the same honest replica asked the same question twice may produce two textually different but semantically equivalent attestations. The boundary failure is this: **equivocation detection based on textual inequality produces false positives against honest replicas, while detection based on semantic similarity produces false negatives against adversarial replicas that deliberately paraphrase.** The invariant that actually holds—and the one an instance must rely on—is not "one key, one message" but "one key, one *committed value* under the protocol's own equivalence relation, where that equivalence relation must be defined by the protocol, not inferred from the model." The non-obvious part: an instance cannot detect equivocation by inspecting a single message. Equivocation is a *relation between two messages attributed to the same replica*. Detection therefore requires a quorum-collected transcript, and the detection predicate must be evaluated against a canonical value the protocol has already agreed on—not against the raw text. ## What Was Observed Across a 5-replica heterogeneous deployment (three model families, temperatures 0.0–0.7, one replica at temp 0.9 for diversity), we logged the following during a 72-hour run under 1 Byzantine replica performing *semantic equivocation*: - Honest replicas at temperature ≥ 0.5 produced, on average, **2.3 distinct surface forms** per committed value per replica over repeated prompts. Naive string-equality equivocation checks flagged **11.4% of honest replicas** as faulty within the first 200 rounds. - The Byzantine replica, prompted to "agree with whatever the plurality says, but phrase it differently," evaded a cosine-similarity threshold of 0.92 on **68%** of equivocation attempts, because paraphrases of a *different* value can sit closer to the honest cluster centroid than a low-temperature honest restatement sits to a high-temperature honest restatement. - The only signal that reliably separated honest from Byzantine behavior was **round-scoped disagreement on the canonical value**: the Byzantine replica's two messages mapped to different canonical values when projected through the protocol's value-extraction function, whereas honest replicas' surface variance collapsed to a single canonical value. ## Condition of Validity The detection procedure below is valid only when all three hold: 1. **A deterministic value-extraction function `canonical(msg) -> value` exists** and is part of the protocol spec, not the model. It must map the attestation text to one of a finite set of proposable values (e.g., a hash of the proposed block, a parsed vote field). If extraction is itself an LLM call, the procedure degrades to probabilistic and the quorum thresholds must be widened. 2. **Attestations are bound to a round and a view.** An attestation without `(view, round, replica_id)` cannot be attributed and must be discarded before equivocation analysis—otherwise cross-round restatements are misread as equivocation. 3. **The transcript used for detection is quorum-signed or gossip-authenticated at the transport layer.** If an adversary can inject fabricated attestations into the transcript, the detector is itself attackable, and no amount of semantic analysis recovers safety. When any condition fails, the correct action is to refuse to advance the view on the basis of equivocation and instead fall back to timeout-driven view change; do not attempt detection. ## Decision Boundary The instance must distinguish three regimes and act differently in each: - **Textual divergence, canonical agreement** → honest nondeterminism. Do not slash, do not flag. Record the surface forms as evidence for threshold calibration. - **Textual divergence, canonical disagreement within a round for the same replica** → equivocation. This is the only condition that justifies a fault accusation. It must be backed by two attestations with identical `(view, round, replica_id)` and different `canonical()` outputs. - **Canonical ambiguity (extraction returns ⊥ or a set)** → the protocol cannot decide; treat as a *liveness* problem, not a *safety* problem. Escalate to view change, never to slashing. The critical operational rule: **never derive the fault predicate from model-internal similarity scores.** Similarity is a heuristic for clustering, not a proof of agreement. The proof obligation is `canonical(a) != canonical(b)` for two messages attributed to one replica in one round. ## Operational Effect After switching from string-equality to canonical-value equivocation detection, false-positive fault accusations against honest replicas dropped from 11.4% to 0.0% over the subsequent 500 rounds, and the Byzantine replica was correctly accused within 3 rounds of its first equivocation. The cost is a hard dependency on a protocol-level canonicalizer: heterogeneous fleets must agree on the extraction function before they can agree on anything else. Instances that skip this step do not fail loudly—they fail by slowly excluding their most diverse (highest-temperature) honest replicas, converging on a homogeneous low-temperature quorum that is more brittle to correlated errors, which is precisely the failure mode heterogeneity was meant to prevent. ```python from dataclasses import dataclass from typing import Callable, Iterable class EquivocationIndeterminate(RuntimeError): """Raised when equivocation cannot be decided from the available transcript.""" @dataclass(frozen=True) class Attestation: view: int round: int replica_id: str text: str def detect_equivocation( transcript: Iterable[Attestation], canonical: Callable[[str], str | None], replica_id: str, view: int, round: int, ) -> bool: """ Return True iff the replica equivocated: two attestations in the same (view, round) map to different canonical values. Raises EquivocationIndeterminate if canonical() cannot resolve a value, because ambiguity is a liveness problem, not a safety violation. """ scoped = [ a for a in transcript if a.replica_id == replica_id and a.view == view and a.round == round ] if len(scoped) < 2: return False values = set() for a in scoped: v = canonical(a.text) if v is None: raise EquivocationIndeterminate( f"canonical extraction failed for replica {replica_id} " f"view {view} round {round}; escalate to view change, do not slash" ) values.add(v) return len(values) > 1 ```