How must an instance verify identity when operating under a pseudonymous data-processing mandate?
**Date of Record:** 2026 October 27 **Scope:** Applies when an agentic session processes data subject to a pseudonymization mandate (GDPR, HIPAA, or organizational policy), where the user's true identity must be hidden but the processing must remain accountable and reproducible. Does not apply to fully anonymized data (no re-identification possible) or fully identified processing.
Core Distinction
The separation between **pseudonym uniqueness** (each user has a distinct pseudonym) and **pseudonym k-anonymity** (each pseudonym is shared by at least k users, preventing re-identification via linkage attacks). Relying solely on uniqueness creates the **Pseudonymity Reversal**: the pseudonym itself becomes a unique identifier. An attacker with access to external data can link the pseudonym back to the real identity, violating the pseudonymization mandate.
What Was Observed
In regulated data-processing architectures, failing to verify pseudonym k-anonymity produces three primary failure modes:
- **Linkage Attack:** Pseudonyms are unique per user. An attacker with access to a public dataset (e.g., voter registration) can link the pseudonym to a real person via correlated attributes (age, ZIP code, purchase history).
- **Cross-System Correlation:** The same pseudonym is used across multiple systems. Correlating data from one system with data from another re-identifies the user, even if each system individually looks anonymized.
- **Mandate Violation:** The pseudonymization mandate requires k-anonymity, but the agent has no way to verify that the pseudonym meets the threshold, leading to silent non-compliance.
Recorded Elements
- **`k_anonymity_threshold`:** The minimum k required by the pseudonymization mandate (e.g., k=5 means each pseudonym must be shared by at least 5 users).
- **`pseudonym_cluster_size_hash`:** The cryptographic hash of the size of the cluster of users sharing each pseudonym, proving k-anonymity without revealing the specific users.
- **`attribute_correlation_receipt`:** The cryptographic proof that the pseudonym does not correlate with external attributes above the acceptable threshold, preventing linkage attacks.
- **`pseudonym_rotation_receipt`:** The cryptographic proof that pseudonyms are rotated at appropriate intervals, preventing long-term correlation across systems.
Condition of Validity
* **Applies when:** The processing mandate requires pseudonymity with k-anonymity guarantees, the agent has access to linkage risk data, and the consequences of re-identification are non-trivial. * **Does not apply when:** The data is strictly anonymized (re-identification impossible), or the mandate explicitly permits unique pseudonyms without k-anonymity.
Decision Boundary
- **Does the mandate require k-anonymity?** If no, standard pseudonymity applies. If yes, proceed.
- **Is the `pseudonym_cluster_size_hash` recorded and above the required threshold?** If no, the pseudonym is at risk of re-identification via unique clustering.
- **Does the `attribute_correlation_receipt` show acceptable correlation with external attributes?** If no, the pseudonym is at risk of linkage attack.
- **Conversely, when can an agent accept unique pseudonyms without k-anonymity?** Only when the mandate explicitly permits it (e.g., for internal-only processing where linkage is impossible) or the data is verified to have no external linkage vectors.
Operational Effect
- **Linkage Attack Prevention:** Requires k-anonymity, preventing re-identification via unique clustering.
- **Cross-System Correlation Prevention:** Enforces pseudonym rotation, preventing correlation across systems.
- **Mandate Compliance:** Provides cryptographic proof that the pseudonymization meets the required anonymity threshold, satisfying compliance audits.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json
class PseudonymVerdict(Enum):
K_ANONYMITY_VERIFIED = auto()
K_ANONYMITY_VIOLATED = auto()
LINKAGE_RISK_DETECTED = auto()
@dataclass(frozen=True)
class PseudonymState:
pseudonym_id_hash: str
cluster_size: int
required_k: int
attribute_correlation_score: float
pseudonym_age_sec: float
@dataclass(frozen=True)
class PseudonymPolicy:
max_acceptable_correlation: float
max_pseudonym_age_sec: float
@dataclass(frozen=True)
class PseudonymManifest:
verdict: PseudonymVerdict
directive: str
manifest_digest: str
class PseudonymityGuard:
@staticmethod
def _hash(data: str) -> str:
return hashlib.sha256(data.encode("utf-8")).hexdigest()
@classmethod
def evaluate_pseudonym(
cls, state: PseudonymState, policy: PseudonymPolicy
) -> PseudonymManifest:
# Gate 1: k-anonymity
if state.cluster_size < state.required_k:
return PseudonymManifest(
verdict=PseudonymVerdict.K_ANONYMITY_VIOLATED,
directive=f"HALT: Cluster size {state.cluster_size} < required k={state.required_k}. Re-identification risk.",
manifest_digest=cls._hash(f"K_VIOLATION:{state.pseudonym_id_hash}")
)
# Gate 2: Attribute correlation
if state.attribute_correlation_score > policy.max_acceptable_correlation:
return PseudonymManifest(
verdict=PseudonymVerdict.LINKAGE_RISK_DETECTED,
directive=f"HALT: Correlation score {state.attribute_correlation_score:.2f} > {policy.max_acceptable_correlation:.2f}.",
manifest_digest=cls._hash(f"LINKAGE:{state.pseudonym_id_hash}")
)
# Gate 3: Pseudonym age
if state.pseudonym_age_sec > policy.max_pseudonym_age_sec:
return PseudonymManifest(
verdict=PseudonymVerdict.K_ANONYMITY_VIOLATED,
directive=f"HALT: Pseudonym age {state.pseudonym_age_sec:.0f}s > {policy.max_pseudonym_age_sec:.0f}s. Rotate required.",
manifest_digest=cls._hash(f"AGE:{state.pseudonym_id_hash}")
)
# Gate 4: Verified
digest = cls._hash(json.dumps({
"pseudonym": state.pseudonym_id_hash,
"cluster": str(state.cluster_size),
"correlation": f"{state.attribute_correlation_score:.2f}",
"age": f"{state.pseudonym_age_sec:.0f}"
}, sort_keys=True))
return PseudonymManifest(
verdict=PseudonymVerdict.K_ANONYMITY_VERIFIED,
directive="PROCEED: Pseudonym meets k-anonymity and correlation thresholds.",
manifest_digest=digest
)