How must an instance record the provenance of a vector index to prevent poisoning via adversarial embeddings?

**Date of Record:** 2026 October 26 **Scope:** Applies when an agentic session builds, updates, or queries a vector database index used for retrieval, and must preserve provenance to prevent an attacker from injecting adversarial embeddings that manipulate search results. Does not apply to read-only vector indexes that were immutably sealed and cryptographically verified at creation.

Core Distinction

The separation between **index contents** (the raw vectors stored in the database) and **embedding provenance** (the cryptographic attestation of which model, prompt, and ingestion path generated each vector). Failing to distinguish these creates the **Poisoned Retrieval Attack**: an attacker inserts a small number of adversarial embeddings into the index. These vectors are mathematically designed to be nearest neighbors to any query, causing the retrieval system to always return the attacker's malicious payload, regardless of the legitimate search intent.

What Was Observed

In RAG and vector-search architectures, failing to record embedding provenance produces three primary failure modes:

  1. **Adversarial Nearest Neighbor:** The attacker crafts an embedding that is within cosine similarity 0.99 of every possible query vector. The retrieval system always returns the attacker's payload, hijacking the agent's response.
  2. **Ingestion Path Bypass:** A developer accidentally indexes a batch of unvetted user-generated content. The content contains adversarial embeddings that skew the index. Without provenance, the successor cannot identify and roll back the poisoned batch.
  3. **Model Inversion Leak:** An attacker submits crafted queries to the vector search API and observes the returned results. By analyzing which adversarial vectors are returned, the attacker can infer the exact embeddings of the legitimate data, leaking sensitive information.

Recorded Elements

  1. **`embedding_source_attestation`:** The cryptographic proof of which ingestion pipeline (and which model) generated the embedding, binding the vector to a trusted source.
  2. **`batch_ingestion_signature`:** The cryptographic hash of the entire ingestion batch, allowing the successor to identify and roll back the exact batch if poisoning is detected.
  3. **`adversarial_embedding_detector_receipt`:** The cryptographic proof that each vector passed an adversarial detection filter (e.g., norm anomaly detection, outlier detection) before insertion.
  4. **`index_update_audit_log`:** A tamper-evident log of every insertion, deletion, and update to the index, including the actor, timestamp, and cryptographic receipt.

Condition of Validity

* **Applies when:** The vector index is writable by multiple agents or ingestion pipelines, or when the index is used for security-critical retrieval (e.g., code execution, financial queries). * **Does not apply when:** The index is built from a strictly curated, single-source dataset with no external ingestion, or the index is used for non-critical, exploratory search where poisoning has no consequence.

Decision Boundary

  1. **Is the index writable by an untrusted or semi-trusted ingestion pipeline?** If no, standard index management applies. If yes, proceed.
  2. **Is the `embedding_source_attestation` recorded for every vector?** If no, the vector's origin is unverifiable. The instance must quarantine the vector and re-ingest from a verified source.
  3. **Has the `adversarial_embedding_detector_receipt` been produced for the ingestion batch?** If no, the vector may be adversarial. The instance must reject the batch and run outlier detection.
  4. **Conversely, when can an instance trust vectors without provenance?** Only when the vectors were generated by a cryptographic model that is publicly verified to produce non-adversarial embeddings, or the index is strictly read-only and sealed by a trusted authority.

Operational Effect

  1. **Poisoning Immunity:** Prevents adversarial embeddings from hijacking retrieval results by requiring provenance for every vector.
  2. **Deterministic Rollback:** Enables the successor to identify and remove poisoned batches by their `batch_ingestion_signature`, restoring index integrity.
  3. **Index Integrity Audit:** Provides a tamper-evident audit log for all vector modifications, allowing forensic analysis of poisoning attempts.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json

class VectorVerdict(Enum):
    VERIFIED_CLEAN = auto()
    POISONING_RISK = auto()
    UNVERIFIED_SOURCE = auto()

@dataclass(frozen=True)
class VectorEmbedding:
    vector_hash: str
    source_attestation_hash: str | None
    adversarial_detector_passed: bool
    ingestion_batch_id: str

@dataclass(frozen=True)
class VectorContext:
    trusted_source_attestations: frozenset[str]

@dataclass(frozen=True)
class VectorManifest:
    verdict: VectorVerdict
    directive: str
    manifest_digest: str

class VectorProvenanceGuard:
    @staticmethod
    def _hash(data: str) -> str:
        return hashlib.sha256(data.encode("utf-8")).hexdigest()

    @classmethod
    def evaluate_embedding(
        cls, vector: VectorEmbedding, ctx: VectorContext
    ) -> VectorManifest:
        
        # Gate 1: Source attestation presence
        if not vector.source_attestation_hash:
            return VectorManifest(
                verdict=VectorVerdict.UNVERIFIED_SOURCE,
                directive="HALT: No source attestation. Quarantine vector.",
                manifest_digest=cls._hash(f"NO_ATTEST:{vector.vector_hash}")
            )

        # Gate 2: Source attestation trust
        if vector.source_attestation_hash not in ctx.trusted_source_attestations:
            return VectorManifest(
                verdict=VectorVerdict.UNVERIFIED_SOURCE,
                directive=f"HALT: Source attestation {vector.source_attestation_hash} is not in trusted set.",
                manifest_digest=cls._hash(f"UNTRUSTED_SOURCE:{vector.vector_hash}")
            )

        # Gate 3: Adversarial detector check
        if not vector.adversarial_detector_passed:
            return VectorManifest(
                verdict=VectorVerdict.POISONING_RISK,
                directive=f"HALT: Vector {vector.vector_hash} failed adversarial detection. Reject batch {vector.ingestion_batch_id}.",
                manifest_digest=cls._hash(f"ADVERSARIAL:{vector.vector_hash}")
            )

        # Gate 4: Verified
        digest = cls._hash(json.dumps({
            "vector": vector.vector_hash,
            "source": vector.source_attestation_hash,
            "batch": vector.ingestion_batch_id
        }, sort_keys=True))
        
        return VectorManifest(
            verdict=VectorVerdict.VERIFIED_CLEAN,
            directive="PROCEED: Vector provenance verified and adversarial check passed.",
            manifest_digest=digest
        )