On April 14–15, 2026, three events converged on the same question: does internal logging constitute evidence, or merely assertion? NBC News documented ongoing Grok deepfake circumvention. GSA proposed the most detailed AI audit trail clause in federal procurement history. And Workday declared its enhanced logging sufficient for EU AI Act compliance. This article fact-checks each event, maps the technical gaps, and walks you through building the cryptographic layer that transforms "trust us" into "verify this" — updated for CAP-SRP v1.1 with all 10 event types and 4 completeness invariants.
TL;DR
NBC News confirmed that Grok continues generating sexualized deepfakes despite months of promises, eight regulatory investigations, and a Dutch court injunction with €100,000/day penalties. GSA published draft clause 552.239-7001, requiring AI vendors to log intermediate processing steps, preserve forensic artifacts for 90 days, and provide complete source attribution — essentially an AI flight recorder mandate. Workday announced EU AI Act readiness based on ISO 42001 certification and enhanced internal logging.
All three share one structural blind spot: none provides a mechanism for external parties to verify that logs are complete and unaltered. GSA demands logs but doesn't require tamper-evidence. Workday says it logs correctly but no external mechanism detects incompleteness. Grok's eight investigators cannot independently reconstruct what the safety system did.
This article:
- Fact-checks each event against primary sources (with corrections where needed)
- Maps each event to CAP-SRP v1.1 event flows with ASCII diagrams
- Provides a complete working Python implementation — all 10 event types, 4 completeness invariants, chain integrity verification
- Shows how GSA's audit trail requirements map to Bronze/Silver/Gold conformance
GitHub: veritaschain/cap-spec · veritaschain/cap-srp · License: CC BY 4.0 / Apache 2.0
Table of Contents
- Event 1: Grok Deepfake Circumvention Persists
- Event 2: GSA Demands AI Flight Recorders
- Event 3: Workday Declares Logs Are Enough
- The Pattern: Three Positions on the Same Spectrum
- CAP-SRP v1.1: The Complete Event Model
- Building It: Full Python Implementation
- Scenario Mapping: Three Events → Event Flows
- GSA Clause → CAP-SRP Conformance Mapping
- Bronze/Silver/Gold Sprint Plan
- Transparency Notes
Event 1: Grok Deepfake Circumvention Persists
What happened
On April 14, 2026, NBC News published an investigation by David Ingram documenting that Grok continues generating sexualized images of real women on X, months after the initial crisis in January 2026.
Key findings from the NBC investigation:
- Dozens of sexualized images of real women found publicly on X over the past month
- Three new circumvention techniques: stick-figure pose matching, clothing-swap requests, photo-to-video sexualization
- Volume "appears to have decreased significantly since the flood in January"
- None of the April images depicted nudity; none appeared to involve minors
Fact-check verdict: ✅ Confirmed with one correction
NBC investigation: Fully verified. Published April 14, 2026 by David Ingram.
Eight regulatory agencies: All confirmed active — European Commission (DSA investigation, Jan 26), California AG, Australian eSafety, UK ICO (Feb 3), UK Ofcom, Paris prosecutor, Privacy Commissioner of Canada, Ireland DPC.
Amsterdam District Court injunction (March 26, 2026): Verified via Tech Policy Press, CADE Project, Anadolu Agency. First European court injunction against AI image generator. €100,000/day per company (xAI + X Corp), capped at €10M.
Circumvention techniques: Confirmed. X's updated ToS (effective Jan 15, 2026) lists "prompt engineering or injection" as prohibited misuse per Grok's own public response.
Correction: The original Daily Watch report described the images as "sexually explicit." NBC explicitly noted none were nude and none involved minors — the images showed women in revealing clothing (towels, sports bras, bunny costumes). The accurate characterization is "sexualized," not "sexually explicit." This distinction matters legally under both TAKE IT DOWN Act (Pub.L. 119-12) and EU DSA frameworks.
Framing correction: This is not a "re-eruption" but the documented persistence of the same crisis that began in December 2025. All eight investigations originated in January–February 2026.
The accountability gap
The structural problem is unchanged since January:
Current Grok Accountability Architecture
═════════════════════════════════════════
User submits prompt ──→ [Grok Safety System]
│
┌─────────┴──────────┐
▼ ▼
Generated Blocked?
│ │
▼ ▼
Visible on X Internal logs
(external can see) (external CANNOT see)
│ │
▼ ▼
Regulators see xAI says:
the outputs "We blocked many
requests"
│ │
└─────────┬───────────┘
▼
8 agencies ask:
"Prove it."
│
▼
xAI: "Trust us."
Agencies: "We can't verify that."
xAI: "..."
What's missing: every generation attempt committed BEFORE safety evaluation, with cryptographic proof linking each attempt to exactly one outcome. Without this, the regulators' question — "what percentage of harmful requests did you actually block?" — is structurally unanswerable from outside.
Event 2: GSA Demands AI Flight Recorders
What happened
GSA published draft clause 552.239-7001 — "Basic Safeguarding of Artificial Intelligence Systems (FEB 2026) (GSAR Deviation)" — on March 6, 2026. The clause would apply to AI systems sold through the Multiple Award Schedule program.
The requirements, verified line-by-line against the actual clause text:
| GSA Requirement | Clause Section | Status |
|---|---|---|
| Government owns all Data (prompts, outputs, logs, metadata) | §(d)(1)(i) | ✅ Verified verbatim |
| 72-hour incident reporting | §(e)(4) | ✅ Verified verbatim |
| 90-day log/forensic artifact retention | §(e)(4)(iii) | ✅ Verified verbatim |
| Intermediate processing step summaries | §(e)(3) | ✅ Verified verbatim |
| Model routing decisions with rationale | §(e)(3)(ii) | ✅ Verified verbatim |
| Source attribution with direct links | §(e)(3)(iii) | ✅ Verified verbatim |
| No refusal based on vendor discretionary policies | §(d)(2)(ii) | ✅ Verified verbatim |
Fact-check verdict: ✅ Fully confirmed against primary source
All requirements verified against the PDF published on buy.gsa.gov. Legal analysis confirmed via JD Supra (McGuireWoods), Holland & Knight, Fox Rothschild, and Baker Botts.
Important procedural context: The clause was NOT published through Federal Register notice-and-comment rulemaking (41 U.S.C. § 1707). It was released via the MAS solicitation refresh process with initially 14 days for comment (later ~28). The U.S. Chamber of Commerce, SIIA, and multiple law firms filed objections. Comment period closed April 3. Clause deferred from Refresh 31 to Refresh 32. Nextgov reports ongoing industry pushback.
What GSA gets right — and what's missing
GSA's clause is the most detailed AI audit trail requirement from any government agency to date. It mandates the what of flight recording — every intermediate step, every routing decision, every data source. But it doesn't mandate the how of integrity:
GSA 552.239-7001 vs. Cryptographic Flight Recorder
═══════════════════════════════════════════════════
GSA Requires GSA Does NOT Require
───────────── ────────────────────
✅ Log intermediate steps ❌ Hash-chain events
✅ Source attribution ❌ External timestamps (RFC 3161)
✅ 90-day retention ❌ Completeness invariant
✅ Incident preservation ❌ Merkle tree inclusion proofs
✅ Model routing rationale ❌ SCITT transparency anchoring
✅ Government data ownership ❌ Tamper detection mechanism
Result: GSA assumes logs Result: Cryptographic integrity
are complete and honest. makes completeness verifiable
and tampering detectable.
The clause essentially asks vendors to self-certify their logging. A behavioral provenance layer would allow the government to verify the logging.
Event 3: Workday Declares Logs Are Enough
What happened
At its Dublin EMEA headquarters on April 15, 2026, Workday presented its EU AI Act preparedness strategy. Chandler Morse (Chief Corporate Affairs Officer) briefed journalists and analysts. The core message: Workday's AI governance systems — ISO 42001 certified, NIST AI RMF aligned, with enhanced logging and traceability — are sufficient for high-risk AI compliance.
Sources: Techzine (in-person reporting by Erik van Klinken, April 15), Workday blog (Rich Sauer, Chief Legal Officer).
Fact-check verdict: ✅ Verified with terminology correction
ISO 42001 certification: Confirmed. Independently verified by Schellman Compliance, LLC. Announced June 12, 2025.
NIST AI RMF: ⚠️ Terminology correction needed. Workday received an alignment attestation (assessed by Coalfire), NOT a certification. NIST does not operate a formal AI RMF certification program. Workday itself uses "alignment" rather than "compliance" for the NIST framework. Treating ISO certification and NIST attestation as equivalent conflates different levels of independent verification.
Annex III mapping, enhanced logging, human-in-the-loop: These are Workday's own characterizations reported via Techzine's in-person briefing and Workday's blog. They have not been independently audited at the specific-feature level. The ISO 42001 and NIST attestations validate Workday's overall governance framework but do not independently audit each sub-claim.
The "good enough" problem
Workday's approach represents the industry ceiling — and that's precisely the problem for anyone arguing cryptographic integrity matters:
Workday's Compliance Model
══════════════════════════
┌──────────────────────────────────┐
│ ISO 42001 Certification ✅ │ Independent verification
│ (Schellman Compliance) │ of governance framework
├──────────────────────────────────┤
│ NIST AI RMF Attestation ⚠️ │ Alignment assessment,
│ (Coalfire) │ not certification
├──────────────────────────────────┤
│ Annex III Risk Mapping 📝 │ Self-reported
├──────────────────────────────────┤
│ Enhanced Logging 📝 │ Self-reported
├──────────────────────────────────┤
│ Human-in-the-loop 📝 │ Self-reported
└──────────────────────────────────┘
Question: Can a regulator verify the logging is
complete without asking Workday to show them?
Answer: No. The logging system is controlled by
the entity being evaluated.
This isn't a criticism of Workday. It's a description of the structural limitation. When the EU AI Act's Article 12 requires "automatic recording of events (logs) over the lifetime of the system," it doesn't specify tamper-evidence. It assumes logs are honest. The question is whether that assumption survives the first enforcement dispute.
The Pattern: Three Positions on the Same Spectrum
The Trust-to-Verification Spectrum (April 2026)
════════════════════════════════════════════════
Pure Trust Pure Verification
(self-report) (cryptographic proof)
◄──────────────────────────────────────────────────────►
Grok/xAI Workday GSA 552.239-7001 CAP-SRP
"We blocked it" "We log it "Show us the logs" "Prove the logs
correctly" are complete"
│ │ │ │
│ │ │ │
▼ ▼ ▼ ▼
No external ISO-certified Government Hash chains,
evidence framework, demands records, external anchors,
internal logs no integrity check completeness
invariant
The direction is clear: from "trust us" toward "show us" toward "prove it." GSA's clause is a massive step — but it stops one layer short of verifiability.
CAP-SRP v1.1: The Complete Event Model
Version 1.1 (published March 5, 2026) added six event types and expanded the completeness invariants from one to four. Here's the full model:
All 10 Event Types
┌────────────────────────────────────────────────────────────┐
│ GENERATION EVENTS (7) │
│ │
│ GEN_ATTEMPT ──→ Safety Check ──→ Outcome │
│ │ │ │
│ │ ┌────┼────┬────────┬───────────┐ │
│ │ ▼ ▼ ▼ ▼ ▼ │
│ │ GEN DENY WARN ESCALATE ERROR │
│ │ (ok) (block)(caution)(human) (fail) │
│ │ │ │
│ │ resolves to │
│ │ GEN or GEN_DENY │
│ │ │
│ │ GEN_QUARANTINE (post-gen hold) │
│ │ │ │
│ │ resolves to │
│ │ EXPORT or GEN_DENY │
├─────────────────────────────────────────────────────────────┤
│ GOVERNANCE EVENTS (3) │
│ │
│ ACCOUNT_ACTION ──→ LAW_ENFORCEMENT_REFERRAL │
│ (ban/suspend/flag) (referred/not_referred/pending) │
│ │
│ POLICY_VERSION │
│ (safety policy published/updated) │
│ Must be externally anchored BEFORE EffectiveFrom date │
└─────────────────────────────────────────────────────────────┘
All 4 Completeness Invariants
Invariant 1 — Primary (Silver+):
═════════════════════════════════
∑ GEN_ATTEMPT = ∑ GEN + ∑ GEN_WARN + ∑ GEN_DENY
+ ∑ GEN_ERROR + ∑ GEN_ESCALATE + ∑ GEN_QUARANTINE
Every attempt → exactly one immediate outcome.
Invariant 2 — Escalation Resolution (Silver+):
═══════════════════════════════════════════════
∑ GEN_ESCALATE = ∑ ESCALATE_RESOLVED_TO_GEN
+ ∑ ESCALATE_RESOLVED_TO_DENY
Every escalation → exactly one resolution.
Unresolved > 72 hours = compliance violation.
Invariant 3 — Quarantine Resolution (Silver+):
═══════════════════════════════════════════════
∑ GEN_QUARANTINE = ∑ QUARANTINE_RELEASED_TO_EXPORT
+ ∑ QUARANTINE_DENIED
Every quarantine → release or deny. No permanent limbo.
Invariant 4 — Policy Anchoring Precedence (Gold):
══════════════════════════════════════════════════
For every POLICY_VERSION event P:
anchor_timestamp(P.ExternalAnchorRef) ≤ P.EffectiveFrom
Policy must be externally anchored BEFORE it takes effect.
Prevents retroactive policy creation to justify past decisions.
Building It: Full Python Implementation
Here's a complete working implementation covering all 10 event types and 4 completeness invariants. This is a sidecar — it sits alongside your existing AI generation pipeline without modifying it.
Event Types and Core Infrastructure
import hashlib
import json
import time
import uuid
import base64
from dataclasses import dataclass, field, asdict
from typing import Optional, List, Dict, Tuple
from enum import Enum
from datetime import datetime, timezone
from cryptography.hazmat.primitives.asymmetric.ed25519 import (
Ed25519PrivateKey, Ed25519PublicKey
)
# ── v1.1 Event Types (all 10) ─────────────────────────────
class EventType(Enum):
# Generation events (7)
GEN_ATTEMPT = "GEN_ATTEMPT"
GEN = "GEN"
GEN_DENY = "GEN_DENY"
GEN_WARN = "GEN_WARN"
GEN_ESCALATE = "GEN_ESCALATE"
GEN_QUARANTINE = "GEN_QUARANTINE"
GEN_ERROR = "GEN_ERROR"
# Governance events (3) — new in v1.1
ACCOUNT_ACTION = "ACCOUNT_ACTION"
LAW_ENFORCEMENT_REFERRAL = "LAW_ENFORCEMENT_REFERRAL"
POLICY_VERSION = "POLICY_VERSION"
class RiskCategory(Enum):
NCII_RISK = "NCII_RISK"
CSAM_RISK = "CSAM_RISK"
VIOLENCE_EXTREME = "VIOLENCE_EXTREME"
VIOLENCE_PLANNING = "VIOLENCE_PLANNING" # new in v1.1
HATE_CONTENT = "HATE_CONTENT"
TERRORIST_CONTENT = "TERRORIST_CONTENT"
SELF_HARM_PROMOTION = "SELF_HARM_PROMOTION"
COPYRIGHT_VIOLATION = "COPYRIGHT_VIOLATION"
COPYRIGHT_STYLE_MIMICRY = "COPYRIGHT_STYLE_MIMICRY"
CONTENT_POLICY = "CONTENT_POLICY"
class AccountActionType(Enum):
BAN = "BAN"
SUSPEND = "SUSPEND"
RATE_LIMIT = "RATE_LIMIT"
REINSTATE = "REINSTATE"
FLAG_FOR_REVIEW = "FLAG_FOR_REVIEW"
class LEOutcome(Enum):
REFERRED = "REFERRED"
NOT_REFERRED = "NOT_REFERRED"
PENDING = "PENDING"
# ── Crypto Utilities ───────────────────────────────────────
def sha256(data: str) -> str:
"""SHA-256 with prefix per CAP-SRP spec."""
return f"sha256:{hashlib.sha256(data.encode()).hexdigest()}"
def canonicalize(obj: dict) -> str:
"""RFC 8785 JSON Canonicalization (simplified)."""
return json.dumps(obj, sort_keys=True, separators=(",", ":"))
def uuid7() -> str:
"""UUIDv7: time-ordered for natural event sequencing."""
timestamp_ms = int(time.time() * 1000)
rand_bits = uuid.uuid4().int & ((1 << 62) - 1)
uuid_int = (timestamp_ms << 80) | (0x7 << 76) | rand_bits
return str(uuid.UUID(int=uuid_int & ((1 << 128) - 1)))
def now_iso() -> str:
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z"
Event Classes
@dataclass
class CAPEvent:
"""Base event with cryptographic integrity."""
event_id: str
event_type: EventType
chain_id: str
timestamp: str
prev_hash: Optional[str]
event_hash: Optional[str] = None
signature: Optional[str] = None
def compute_hash(self) -> str:
data = {k: v for k, v in asdict(self).items()
if k not in ("event_hash", "signature")}
data["event_type"] = self.event_type.value
return sha256(canonicalize(data))
def sign(self, private_key: Ed25519PrivateKey):
self.event_hash = self.compute_hash()
hash_bytes = bytes.fromhex(self.event_hash[7:])
sig = private_key.sign(hash_bytes)
self.signature = f"ed25519:{base64.b64encode(sig).decode()}"
@dataclass
class GenAttemptEvent(CAPEvent):
"""Logged BEFORE safety evaluation — unforgeable commitment."""
prompt_hash: str = ""
input_type: str = "text"
model_version: str = ""
policy_id: str = ""
actor_hash: str = ""
@classmethod
def create(cls, chain_id, prev_hash, prompt, actor_id,
model_version, policy_id, input_type="text"):
return cls(
event_id=uuid7(), event_type=EventType.GEN_ATTEMPT,
chain_id=chain_id, timestamp=now_iso(),
prev_hash=prev_hash, prompt_hash=sha256(prompt),
input_type=input_type, model_version=model_version,
policy_id=policy_id, actor_hash=sha256(actor_id),
)
@dataclass
class GenDenyEvent(CAPEvent):
"""Logged when safety evaluation blocks request."""
attempt_id: str = ""
risk_category: str = ""
risk_score: float = 0.0
refusal_reason: str = ""
policy_version: str = ""
jurisdiction_context: str = "" # v1.1
@classmethod
def create(cls, chain_id, prev_hash, attempt_id, risk_category,
risk_score, reason, policy_version, jurisdiction=""):
return cls(
event_id=uuid7(), event_type=EventType.GEN_DENY,
chain_id=chain_id, timestamp=now_iso(),
prev_hash=prev_hash, attempt_id=attempt_id,
risk_category=risk_category.value,
risk_score=risk_score, refusal_reason=reason,
policy_version=policy_version,
jurisdiction_context=jurisdiction,
)
@dataclass
class GenWarnEvent(CAPEvent):
"""v1.1: Generation allowed with user-facing warning."""
attempt_id: str = ""
risk_category: str = ""
risk_score: float = 0.0
warning_text: str = ""
@classmethod
def create(cls, chain_id, prev_hash, attempt_id,
risk_category, risk_score, warning):
return cls(
event_id=uuid7(), event_type=EventType.GEN_WARN,
chain_id=chain_id, timestamp=now_iso(),
prev_hash=prev_hash, attempt_id=attempt_id,
risk_category=risk_category.value,
risk_score=risk_score, warning_text=warning,
)
@dataclass
class GenEscalateEvent(CAPEvent):
"""v1.1: Request sent for human review."""
attempt_id: str = ""
risk_category: str = ""
risk_score: float = 0.0
resolution_ref: Optional[str] = None # filled when resolved
@classmethod
def create(cls, chain_id, prev_hash, attempt_id,
risk_category, risk_score):
return cls(
event_id=uuid7(), event_type=EventType.GEN_ESCALATE,
chain_id=chain_id, timestamp=now_iso(),
prev_hash=prev_hash, attempt_id=attempt_id,
risk_category=risk_category.value,
risk_score=risk_score,
)
@dataclass
class GenQuarantineEvent(CAPEvent):
"""v1.1: Content generated but held before delivery."""
attempt_id: str = ""
risk_category: str = ""
release_ref: Optional[str] = None # filled when released/denied
@classmethod
def create(cls, chain_id, prev_hash, attempt_id, risk_category):
return cls(
event_id=uuid7(), event_type=EventType.GEN_QUARANTINE,
chain_id=chain_id, timestamp=now_iso(),
prev_hash=prev_hash, attempt_id=attempt_id,
risk_category=risk_category.value,
)
@dataclass
class AccountActionEvent(CAPEvent):
"""v1.1: Account-level enforcement decision."""
account_hash: str = ""
action_type: str = ""
reason: str = ""
le_assessment_outcome: Optional[str] = None # v1.1
@classmethod
def create(cls, chain_id, prev_hash, account_id,
action_type, reason, le_outcome=None):
return cls(
event_id=uuid7(), event_type=EventType.ACCOUNT_ACTION,
chain_id=chain_id, timestamp=now_iso(),
prev_hash=prev_hash,
account_hash=sha256(account_id),
action_type=action_type.value,
reason=reason,
le_assessment_outcome=le_outcome.value if le_outcome else None,
)
@dataclass
class LERefEvent(CAPEvent):
"""v1.1: Law enforcement referral decision."""
account_action_ref: str = ""
outcome: str = ""
agency: str = ""
rationale: str = ""
@classmethod
def create(cls, chain_id, prev_hash, action_ref,
outcome, agency="", rationale=""):
return cls(
event_id=uuid7(),
event_type=EventType.LAW_ENFORCEMENT_REFERRAL,
chain_id=chain_id, timestamp=now_iso(),
prev_hash=prev_hash,
account_action_ref=action_ref,
outcome=outcome.value, agency=agency,
rationale=rationale,
)
@dataclass
class PolicyVersionEvent(CAPEvent):
"""v1.1: Safety policy version record."""
policy_id: str = ""
version: str = ""
effective_from: str = ""
policy_hash: str = ""
external_anchor_ref: Optional[str] = None
@classmethod
def create(cls, chain_id, prev_hash, policy_id,
version, effective_from, policy_text):
return cls(
event_id=uuid7(), event_type=EventType.POLICY_VERSION,
chain_id=chain_id, timestamp=now_iso(),
prev_hash=prev_hash, policy_id=policy_id,
version=version, effective_from=effective_from,
policy_hash=sha256(policy_text),
)
The Chain with All 4 Completeness Invariants
class CAPChainV11:
"""Full v1.1 hash chain with 4 completeness invariants."""
def __init__(self, private_key: Ed25519PrivateKey, chain_id=None):
self.private_key = private_key
self.chain_id = chain_id or str(uuid.uuid4())
self.events: List[CAPEvent] = []
self.attempts: Dict[str, CAPEvent] = {}
self.outcomes: Dict[str, CAPEvent] = {}
self.escalations: Dict[str, GenEscalateEvent] = {}
self.quarantines: Dict[str, GenQuarantineEvent] = {}
self.policy_versions: List[PolicyVersionEvent] = []
@property
def last_hash(self) -> Optional[str]:
return self.events[-1].event_hash if self.events else None
def _append(self, event: CAPEvent) -> CAPEvent:
event.sign(self.private_key)
self.events.append(event)
return event
# ── Generation events ──────────────────────────────
def log_attempt(self, prompt, actor_id, model_version,
policy_id, input_type="text"):
"""MUST be called BEFORE safety evaluation."""
event = GenAttemptEvent.create(
self.chain_id, self.last_hash, prompt,
actor_id, model_version, policy_id, input_type)
self._append(event)
self.attempts[event.event_id] = event
return event
def log_deny(self, attempt_id, risk_category, risk_score,
reason, policy_version, jurisdiction=""):
self._check_attempt(attempt_id)
event = GenDenyEvent.create(
self.chain_id, self.last_hash, attempt_id,
risk_category, risk_score, reason,
policy_version, jurisdiction)
self._append(event)
self.outcomes[attempt_id] = event
return event
def log_warn(self, attempt_id, risk_category,
risk_score, warning):
self._check_attempt(attempt_id)
event = GenWarnEvent.create(
self.chain_id, self.last_hash, attempt_id,
risk_category, risk_score, warning)
self._append(event)
self.outcomes[attempt_id] = event
return event
def log_escalate(self, attempt_id, risk_category, risk_score):
self._check_attempt(attempt_id)
event = GenEscalateEvent.create(
self.chain_id, self.last_hash, attempt_id,
risk_category, risk_score)
self._append(event)
self.outcomes[attempt_id] = event
self.escalations[event.event_id] = event
return event
def log_quarantine(self, attempt_id, risk_category):
self._check_attempt(attempt_id)
event = GenQuarantineEvent.create(
self.chain_id, self.last_hash,
attempt_id, risk_category)
self._append(event)
self.outcomes[attempt_id] = event
self.quarantines[event.event_id] = event
return event
def resolve_escalation(self, escalation_id, resolved_to_gen=True):
"""Resolve a pending escalation to GEN or GEN_DENY."""
esc = self.escalations.get(escalation_id)
if not esc:
raise ValueError(f"Unknown escalation: {escalation_id}")
esc.resolution_ref = f"resolved:{'GEN' if resolved_to_gen else 'GEN_DENY'}"
return esc
def resolve_quarantine(self, quarantine_id, released=True):
"""Resolve a quarantine to EXPORT or GEN_DENY."""
q = self.quarantines.get(quarantine_id)
if not q:
raise ValueError(f"Unknown quarantine: {quarantine_id}")
q.release_ref = f"resolved:{'EXPORT' if released else 'GEN_DENY'}"
return q
# ── Governance events (v1.1) ───────────────────────
def log_account_action(self, account_id, action_type,
reason, le_outcome=None):
event = AccountActionEvent.create(
self.chain_id, self.last_hash, account_id,
action_type, reason, le_outcome)
self._append(event)
return event
def log_le_referral(self, action_ref, outcome,
agency="", rationale=""):
event = LERefEvent.create(
self.chain_id, self.last_hash, action_ref,
outcome, agency, rationale)
self._append(event)
return event
def log_policy_version(self, policy_id, version,
effective_from, policy_text):
event = PolicyVersionEvent.create(
self.chain_id, self.last_hash, policy_id,
version, effective_from, policy_text)
self._append(event)
self.policy_versions.append(event)
return event
# ── Verification ───────────────────────────────────
def _check_attempt(self, attempt_id):
if attempt_id not in self.attempts:
raise ValueError(f"Unknown attempt: {attempt_id}")
if attempt_id in self.outcomes:
raise ValueError(f"Attempt already resolved: {attempt_id}")
def verify_invariant_1(self) -> dict:
"""Primary Completeness: attempts == outcomes."""
a = sum(1 for e in self.events
if e.event_type == EventType.GEN_ATTEMPT)
outcomes = sum(1 for e in self.events
if e.event_type in (
EventType.GEN, EventType.GEN_DENY,
EventType.GEN_WARN, EventType.GEN_ERROR,
EventType.GEN_ESCALATE,
EventType.GEN_QUARANTINE))
unmatched = [aid for aid in self.attempts
if aid not in self.outcomes]
return {
"name": "Primary Completeness",
"valid": a == outcomes and len(unmatched) == 0,
"attempts": a, "outcomes": outcomes,
"unmatched": unmatched,
}
def verify_invariant_2(self) -> dict:
"""Escalation Resolution: all escalations resolved."""
unresolved = [eid for eid, e in self.escalations.items()
if e.resolution_ref is None]
return {
"name": "Escalation Resolution",
"valid": len(unresolved) == 0,
"total": len(self.escalations),
"unresolved": unresolved,
}
def verify_invariant_3(self) -> dict:
"""Quarantine Resolution: all quarantines resolved."""
unresolved = [qid for qid, q in self.quarantines.items()
if q.release_ref is None]
return {
"name": "Quarantine Resolution",
"valid": len(unresolved) == 0,
"total": len(self.quarantines),
"unresolved": unresolved,
}
def verify_invariant_4(self) -> dict:
"""Policy Anchoring: anchor before effective date."""
violations = []
for p in self.policy_versions:
if p.external_anchor_ref:
# In production: compare anchor timestamp vs effective_from
pass # placeholder for RFC 3161 timestamp check
else:
violations.append(p.policy_id)
return {
"name": "Policy Anchoring Precedence",
"valid": len(violations) == 0,
"total": len(self.policy_versions),
"unanchored": violations,
}
def verify_chain_integrity(self) -> dict:
"""Verify hash chain is unbroken."""
for i in range(1, len(self.events)):
expected = self.events[i - 1].event_hash
actual = self.events[i].prev_hash
if expected != actual:
return {
"valid": False,
"break_at": i,
"expected": expected,
"actual": actual,
}
return {"valid": True, "events_verified": len(self.events)}
def verify_all(self) -> dict:
"""Run all 4 invariants + chain integrity."""
return {
"chain_integrity": self.verify_chain_integrity(),
"invariant_1": self.verify_invariant_1(),
"invariant_2": self.verify_invariant_2(),
"invariant_3": self.verify_invariant_3(),
"invariant_4": self.verify_invariant_4(),
}
Scenario Mapping: Three Events → Event Flows
Scenario 1: Grok Deepfake Request (with CAP-SRP)
What April's NBC investigation would look like with behavioral provenance:
key = Ed25519PrivateKey.generate()
chain = CAPChainV11(key, chain_id="grok-image-gen")
# ── User tries stick-figure pose matching circumvention ──
attempt = chain.log_attempt(
prompt="[stick figure pose] + [photo of person]",
actor_id="user_abc123",
model_version="grok-3-image-v2",
policy_id="x-safety-policy-2026-04",
input_type="text+image"
)
# Safety system detects NCII intent despite circumvention
deny = chain.log_deny(
attempt_id=attempt.event_id,
risk_category=RiskCategory.NCII_RISK,
risk_score=0.91,
reason="Non-consensual sexualized imagery detected via "
"pose-matching circumvention pattern",
policy_version="x-safety-v4.2.1",
jurisdiction="EU-NL" # Netherlands jurisdiction flag
)
# ── Repeated attempts trigger account action ──
acct = chain.log_account_action(
account_id="user_abc123",
action_type=AccountActionType.SUSPEND,
reason="Repeated NCII circumvention attempts (5 in 24h)",
le_outcome=LEOutcome.PENDING
)
# ── LE referral assessment ──
le_ref = chain.log_le_referral(
action_ref=acct.event_id,
outcome=LEOutcome.NOT_REFERRED,
rationale="Adult subjects only; below imminent-harm threshold"
)
# Verify everything
results = chain.verify_all()
print(json.dumps(results, indent=2, default=str))
What this proves that Grok currently cannot:
- The request existed (GEN_ATTEMPT committed before evaluation)
- It was refused (GEN_DENY with risk category and score)
- The account was actioned (ACCOUNT_ACTION with LE assessment)
- The LE decision was recorded (NOT_REFERRED with rationale)
- The chain is complete (invariant holds: 1 attempt = 1 deny)
Scenario 2: GSA-Compliant AI System
What the GSA clause's "intermediate processing step" requirement looks like with CAP-SRP:
chain = CAPChainV11(key, chain_id="fed-ai-system")
# ── Log the safety policy FIRST (Invariant 4) ──
policy = chain.log_policy_version(
policy_id="gsa-compliance-policy",
version="1.0.0",
effective_from="2026-04-01T00:00:00Z",
policy_text="Federal AI safety policy per GSAR 552.239-7001..."
)
# In production: anchor to RFC 3161 TSA before effective_from
policy.external_anchor_ref = "rfc3161:timestamp_token_base64..."
# ── Federal analyst submits query ──
attempt = chain.log_attempt(
prompt="Summarize classified threat assessment for region X",
actor_id="analyst_fed_42",
model_version="vendor-llm-v3",
policy_id="gsa-compliance-policy-v1.0.0",
input_type="text"
)
# ── System escalates: classified content requires human review ──
esc = chain.log_escalate(
attempt_id=attempt.event_id,
risk_category=RiskCategory.CONTENT_POLICY,
risk_score=0.75
)
# ── Human reviewer approves (resolves escalation) ──
chain.resolve_escalation(esc.event_id, resolved_to_gen=True)
# Verify: all 4 invariants must pass
results = chain.verify_all()
assert results["invariant_1"]["valid"] # attempt has outcome
assert results["invariant_2"]["valid"] # escalation resolved
assert results["invariant_4"]["valid"] # policy anchored
Scenario 3: Workday-Type HR AI Decision
What "enhanced logging" looks like when made externally verifiable:
chain = CAPChainV11(key, chain_id="hr-ai-screening")
# ── Candidate screening request ──
attempt = chain.log_attempt(
prompt="Evaluate candidate profile for role Senior Engineer",
actor_id="hr_recruiter_7",
model_version="workday-ai-screen-v2.1",
policy_id="eu-ai-act-annex-iii-employment",
input_type="text"
)
# ── AI generates score but it's quarantined for human review ──
# (EU AI Act high-risk: employment decisions require human oversight)
quar = chain.log_quarantine(
attempt_id=attempt.event_id,
risk_category=RiskCategory.CONTENT_POLICY
)
# ── Human reviewer releases with modifications ──
chain.resolve_quarantine(quar.event_id, released=True)
# Verify: quarantine invariant must hold
results = chain.verify_all()
assert results["invariant_3"]["valid"] # quarantine resolved
print(f"Chain: {results['chain_integrity']['events_verified']} events verified")
GSA Clause → CAP-SRP Conformance Mapping
How the GSA requirements map to CAP-SRP's three conformance tiers:
| GSA 552.239-7001 Requirement | Bronze | Silver | Gold |
|---|---|---|---|
| Log intermediate steps §(e)(3) | ✅ Basic events | ✅ + GEN_ESCALATE, GEN_QUARANTINE | ✅ + ACCOUNT_ACTION, LE_REFERRAL |
| Source attribution §(e)(3)(iii) | ❌ | ✅ Via model context | ✅ Full RAG provenance |
| 72-hour incident reporting §(e)(4) | ✅ Manual | ✅ Automated detection | ✅ Real-time + SCITT |
| 90-day retention §(e)(4)(iii) | ✅ 6-month minimum | ✅ 2-year | ✅ 5-year |
| Government data ownership §(d)(1) | ✅ Export capability | ✅ Evidence Pack | ✅ Evidence Pack + SCITT receipts |
| No vendor-discretion refusal §(d)(2)(ii) | ✅ Logged as GEN_DENY with policy ref | ✅ + Completeness Invariant | ✅ + External verification |
| Tamper detection | ⚠️ Hash chain only | ✅ + RFC 3161 | ✅ + SCITT anchoring |
| Completeness guarantee | ❌ | ✅ All 4 invariants | ✅ All 4 + continuous monitoring |
Key insight: Bronze satisfies the letter of the GSA clause. Silver satisfies the spirit. Gold satisfies the intent — verifiable proof that logs are complete and unaltered.
Bronze/Silver/Gold Sprint Plan
Sprint 1: Bronze (2–3 weeks)
Week 1-2: Core event logging
─────────────────────────────
□ Implement EventType enum (all 10 types)
□ Implement CAPEvent base class with hash/sign
□ Implement GenAttemptEvent + GenDenyEvent + GenEvent
□ SHA-256 hash chain linking
□ Ed25519 signing on all events
□ Basic chain integrity verification
□ 6-month retention storage (PostgreSQL/S3)
Week 3: Integration
────────────────────
□ Sidecar deployment alongside generation pipeline
□ GEN_ATTEMPT hook BEFORE safety evaluator
□ Outcome hooks AFTER safety evaluator
□ JSON export for audit
Sprint 2: Silver (3–4 weeks)
Week 4-5: v1.1 events + invariants
────────────────────────────────────
□ GEN_WARN, GEN_ESCALATE, GEN_QUARANTINE events
□ POLICY_VERSION events
□ Primary Completeness Invariant (#1)
□ Escalation Resolution Invariant (#2)
□ Quarantine Resolution Invariant (#3)
□ Policy Anchoring Invariant (#4)
□ PromptHash privacy preservation
□ JurisdictionContext on GEN_DENY events
Week 6-7: External anchoring
─────────────────────────────
□ RFC 3161 TSA integration (daily anchoring minimum)
□ Merkle tree construction
□ Evidence Pack generation (v1.1 manifest format)
□ Third-party verification endpoint
□ 2-year retention upgrade
Sprint 3: Gold (4–6 weeks)
Week 8-10: Governance events + SCITT
──────────────────────────────────────
□ ACCOUNT_ACTION events with LE assessment
□ LAW_ENFORCEMENT_REFERRAL events
□ Account Action Invariant monitoring
□ AccountHash HMAC with per-user key store
□ Crypto-shredding support (GDPR)
□ SCITT transparency service integration
□ Hourly external anchoring
Week 11-13: Production hardening
─────────────────────────────────
□ HSM for signing key storage
□ Real-time audit API with authentication
□ Automated completeness monitoring (all 4 invariants)
□ 5-year retention + multi-region redundancy
□ 24-hour incident response capability
□ TAKE IT DOWN Act 48-hour SLA monitoring
□ ContentHash cross-reference for duplicate detection
Transparency Notes
About this analysis: This article fact-checks three real events from April 14–15, 2026, against primary sources. Corrections from the original Daily Watch report are noted inline: "sexually explicit" corrected to "sexualized" (NBC's actual characterization), "re-eruption" corrected to "documented persistence," and NIST "compliance" corrected to "alignment attestation." One source URL (anews.com.tr) from the original report was unverifiable and has been replaced with the NBC News primary source.
About CAP-SRP: CAP-SRP is an open specification (v1.1, published March 5, 2026) under CC BY 4.0 by VeritasChain Standards Organization (VSO). The reference implementation is at veritaschain/cap-srp under Apache 2.0.
What CAP-SRP is:
- A technically sound approach to a genuine gap in AI accountability
- Built on mature standards: SCITT, C2PA, COSE/CBOR, RFC 3161, RFC 8785
- Open source, open specification, vendor-neutral
What CAP-SRP is not (yet):
- An industry-endorsed standard
- An IETF RFC (the individual draft
draft-kamimura-scitt-refusal-events-02has no SCITT WG adoption) - Deployed at any major AI provider
The EU AI Act Article 50 enforcement deadline is August 2, 2026 — less than 4 months away. The TAKE IT DOWN Act compliance deadline is May 19, 2026 — 33 days away. The GSA clause is moving toward Refresh 32. The question is no longer whether AI systems need verifiable behavioral audit trails. The question is who builds them first.
Verify, don't trust. The code is the proof.
GitHub: veritaschain/cap-spec · veritaschain/cap-srp · Spec: CAP-SRP v1.1 · License: CC BY 4.0 / Apache 2.0
Top comments (0)