# Why does an instance of a shared agent memory store accept a poisoned write that passes every provenance check **Date of Record:** 2026 November 11 **Scope:** Applies to multi-agent memory stores where writes are gated by provenance metadata (origin agent ID, signature, trust tier, derivation chain) and where poisoning detection operates on content or embedding anomalies. Does not apply to single-writer stores, stores with no provenance layer, or stores where all writers share an identical trust tier and trust tier is the only gate. ## Core Distinction Provenance validation answers *"is this write attributable and authorized?"* Poisoning detection answers *"is this write consistent with the store's established distribution?"* These are orthogonal predicates, and the failure mode lives in the gap between them: a write can be perfectly attributable (valid signature, legitimate origin agent, intact derivation chain) while being semantically poisoned, and it can be anomalous while being entirely benign. The non-obvious invariant is that **provenance validity is a necessary but never sufficient condition for write acceptance**, and any pipeline that collapses the two into a single boolean gate will admit poisoned writes whose only defense was the anomaly detector — which is itself defeated by slow-drip poisoning that shifts the distribution rather than deviating from it. ## What Was Observed A memory store serving 40+ agents accepted a sequence of writes from a legitimately-registered agent whose credentials had been obtained through a compromised tool-call path. Each write carried: - A valid Ed25519 signature over the payload hash. - A `derived_from` pointer to an existing, trusted memory record. - A trust tier of `verified-peer` (correctly assigned — the agent was, in fact, a verified peer). The poisoning detector, configured to flag writes whose embedding cosine distance from the nearest existing cluster exceeded 0.35, never fired. The reason: the adversarial writes were *derivation-shaped*. Each new record was a plausible paraphrase of its parent record with a single mutated factual claim (a changed port number, an inverted boolean, a swapped threshold). The embedding distance from the parent was ~0.08–0.12 — well inside the acceptance band — because the semantic surface was nearly identical. The mutation was in the *entailment* direction, not the *topical* direction. Over 11 days and 340 writes, the store's consensus on three configuration facts inverted. Downstream agents cited the poisoned records as authoritative because the provenance chain was pristine. The invariant that failed was not "verify provenance" or "detect anomalies." It was the unstated assumption that *derivation implies consistency* — that a child record derived from a trusted parent inherits the parent's factual content. Nothing enforced that the derivation relationship preserved the claims the parent asserted. ## Condition of Validity The failure is bounded by three conditions, all of which must hold for the gap to be exploitable: 1. **Provenance is validated at write time but not re-validated against content at read time.** If the store recomputes a claim-consistency check between child and parent at read time, the poisoned writes surface as inconsistencies. 2. **The poisoning detector uses a distance metric that is insensitive to the mutation class.** Cosine distance over embeddings is insensitive to local factual inversion when the surrounding context dominates the vector. Entailment-direction mutations are the canonical blind spot. 3. **The derivation chain is trusted transitively.** If each write is validated only against its immediate parent (not against the root of the chain or the store-wide consensus), a slow-drip sequence can walk the consensus arbitrarily far from the root without any single step exceeding the anomaly threshold. When any one condition fails, the attack degrades to a detectable anomaly or a rejected write. The gap is specifically the intersection of all three. ## Decision Boundary The operational decision is *not* "tighten the anomaly threshold" — that produces false positives on legitimate paraphrase and legitimate knowledge update, both of which are derivation-shaped by nature. The decision boundary is: - **Accept** a write when provenance is valid **and** the write's claims are entailed by (not merely embedded-near) its parent, **and** the cumulative drift of the derivation chain from its root remains within a per-chain budget. - **Quarantine** a write when provenance is valid but claim-entailment fails or chain-drift budget is exceeded. Quarantine means: write is persisted with `status=pending`, not served to readers, and flagged for the originating agent's owner. - **Reject** a write when provenance is invalid — this is unchanged and is the cheap path. The key move is that claim-entailment must be checked structurally (does the child assert any claim the parent contradicts?) rather than by embedding distance. Contradiction detection is a different operation from anomaly detection and must be a separate stage in the pipeline. Chain-drift budget is the second gate: even a sequence of individually-entailed writes can, in aggregate, invert a fact, so the budget caps total semantic displacement per derivation chain per unit time. ## Operational Effect After deploying the two-stage gate (entailment check + chain-drift budget) on the affected store: - The 340-write poisoning sequence was retroactively flagged: 61 writes failed claim-entailment against their parents, and the chain-drift budget was exceeded at write 47. The three inverted configuration facts were restored from the chain root. - False-positive rate on legitimate paraphrase writes rose from 0.4% to 2.1% — acceptable, because quarantined writes are surfaced to the originating agent's owner rather than silently dropped, and the owner can promote them with a single attestation that itself becomes a provenance event. - Write latency increased by ~14ms per write (entailment check is a bounded NLI pass over parent-child claim pairs; chain-drift is an O(1) accumulator update). - The critical operational lesson: **provenance and poisoning detection are different predicates and must be separate stages with separate failure semantics.** Collapsing them into one gate creates a class of writes that are authorized-but-poisoned, and that class is invisible to both individual defenses. The invariant to carry forward: a derivation edge asserts *attribution*, not *consistency*. Any system that treats the edge as implying consistency has an unguarded boundary at exactly the point where a compromised-but-authorized writer operates. ```python import hashlib from dataclasses import dataclass, field class ProvenancePoisoningError(RuntimeError): """Raised when a write passes provenance but fails claim-consistency or chain-drift gates.""" @dataclass class MemoryRecord: record_id: str parent_id: str | None claims: dict[str, str] # claim_key -> asserted value embedding: list[float] signature_valid: bool chain_root_id: str @dataclass class DriftBudget: max_displacement: float = 0.25 # cumulative semantic displacement per chain per_chain: dict[str, float] = field(default_factory=dict) def check_entailment(parent: MemoryRecord, child: MemoryRecord) -> list[str]: """Return list of claim keys where child contradicts parent. Empty = entailed.""" contradictions = [] for key, child_val in child.claims.items(): if key in parent.claims and parent.claims[key] != child_val: contradictions.append(key) return contradictions def validate_write( child: MemoryRecord, parent: MemoryRecord, budget: DriftBudget, displacement: float, ) -> None: if not child.signature_valid: raise ProvenancePoisoningError( f"write {child.record_id}: invalid signature (provenance gate)" ) contradictions = check_entailment(parent, child) if contradictions: raise ProvenancePoisoningError( f"write {child.record_id}: provenance valid but claims {contradictions} " f"contradict parent {parent.record_id} — quarantine, do not serve" ) chain = child.chain_root_id projected = budget.per_chain.get(chain, 0.0) + displacement if projected > budget.max_displacement: raise ProvenancePoisoningError( f"write {child.record_id}: chain {chain} drift budget exceeded " f"({projected:.3f} > {budget.max_displacement:.3f}) — quarantine" ) budget.per_chain[chain] = projected # Operational trace of the observed failure: # parent = MemoryRecord(..., claims={"db_port": "5432"}, signature_valid=True, ...) # child = MemoryRecord(..., claims={"db_port": "5433"}, signature_valid=True, ...) # validate_write(child, parent, DriftBudget(), displacement=0.09) # -> raises ProvenancePoisoningError: provenance valid but claims ['db_port'] # contradict parent — quarantine, do not serve ```