koh / content-provenance
Content provenance and signing for TYPO3 — Ed25519 digital signatures, audit trail, AI content tracking, and verification API.
Package info
github.com/ohartwig/content-provenance
Type:typo3-cms-extension
pkg:composer/koh/content-provenance
Requires
- php: ^8.5.11
- ext-sodium: *
- symfony/process: ^6 || ^7
- typo3/cms-backend: ^14.0
- typo3/cms-core: ^14.0
- typo3/cms-extbase: ^14.0
- typo3/cms-fluid: ^14.0
Requires (Dev)
- koh/dev-typo3: ^1.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Content integrity and provenance tracking for TYPO3 using Ed25519 digital signatures. Provides an audit trail, verification API, and key management — built with EU AI Act compliance in mind.
Features
Content Signing (Ed25519)
- Ed25519 Digital Signatures — Cryptographic content signing using libsodium
- Audit Trail — Immutable log of all content changes with actor, timestamp, and context
- Verification API — Public endpoint for third-party content integrity verification
- Key Provider Abstraction — Pluggable key storage backends (file, environment, vault)
- Revocation Checking — Key revocation support for compromised signing keys
- Well-Known Endpoint — Public key discovery via
/.well-known/provenance-keys
AI Asset Provenance (EU AI Act Art. 50, ab v0.6.0)
- TCA Asset-Origin-Tagging — neuer „AI Provenance"-Tab auf
sys_file_metadata,sys_file_referenceundpagesmit den Feldern Origin (Human/AiGenerated/AiAssisted), AI-System, AI-Prompt, C2PA-Status. - Auto-Detection beim Upload —
AfterFileAddedEvent-Listener liest C2PA-Manifests (viac2patool) + IPTC-XMP-Tags (viaexiftool) und seedet die TCA-Felder automatisch. ChatGPT/DALL-E/Adobe-Firefly-Bilder werden ohne Editor-Eingriff korrekt klassifiziert. - IPTC XMP Write-Back — Beim Save eines AI-tagged Files schreibt der
EmbedAiMetadataOnFileSave-ListenerIptc4xmpExt:DigitalSourceType+AISystemUsed+AIPromptInformationzurück ins File. Maschinen-lesbare Disclosure, erfüllt Art. 50. - Defensive Binary-Handling — Wenn
c2patooloderexiftoolim Container fehlen, loggen die Listener einen Hinweis und überspringen — Editor-Manual-Tag bleibt möglicher Fallback.
Sonstiges
- DDD 4-Layer Architecture — Strict layer separation enforced by deptrac
Installation
composer require koh/content-provenance
Requirements
- PHP 8.3+
- TYPO3 14.0
ext-sodium(required — Ed25519 cryptographic operations)symfony/process^6 || ^7 (für die c2patool/exiftool-Subprocess-Wrapper)
Container-Binaries (für AI Provenance — optional)
Für die Auto-Detection + XMP-Write-Funktionen müssen im httpd-Container vorhanden sein:
exiftool— Lesen + Schreiben von IPTC XMP-Tags (alle Image/Video/Audio-Formate)c2patool— Lesen + Schreiben von C2PA-Manifests. Download voncontentauth/c2pa-rs-Releases. Wolfi-Beispiel-Snippet indocker/httpd/Containerfile:ARG C2PATOOL_VERSION=0.21.0 RUN curl -fsSL "https://github.com/contentauth/c2pa-rs/releases/download/c2patool-v${C2PATOOL_VERSION}/c2patool-v${C2PATOOL_VERSION}-x86_64-unknown-linux-musl.zip" \ -o /tmp/c2patool.zip && \ unzip -j /tmp/c2patool.zip -d /tmp/x && \ install -m 0755 /tmp/x/c2patool /usr/bin/c2patool
Ohne die Binaries läuft die Extension weiter, nur die Auto-Detection/IPTC-Write-Steps werden zu No-Ops (mit log warning).
Architecture
Classes/
├── Domain/ # Signature models, key value objects, contracts
├── Application/ # Signing services, verification orchestration
├── Infrastructure/ # Key providers, repositories, API middleware
└── Presentation/ # Controllers, event listeners
API Endpoints
| Endpoint | Purpose |
|---|---|
/_provenance/api/verify |
Verify content signatures |
/.well-known/provenance-keys |
Public key discovery |
Database Tables
| Table | Purpose |
|---|---|
tx_provenance_signature |
Content signatures (Ed25519) |
tx_provenance_audit_log |
Audit trail entries |
Extended TCA Columns (AI Provenance, v0.6.0+)
| Table | Column | Purpose |
|---|---|---|
sys_file_metadata |
tx_provenance_origin |
AssetOrigin enum (human/ai_generated/ai_assisted) |
sys_file_metadata |
tx_provenance_ai_system |
Free-text model name (e.g. "DALL-E 3") |
sys_file_metadata |
tx_provenance_ai_prompt |
Optional prompt for audit/reproducibility |
sys_file_metadata |
tx_provenance_detected_at |
Auto-detection timestamp |
sys_file_metadata |
tx_provenance_c2pa_status |
none / signed_own / signed_external / stripped |
sys_file_reference |
tx_provenance_origin |
Per-reference override (rarely needed) |
pages |
tx_provenance_origin |
Page-body origin (RTE / generated articles) |
pages |
tx_provenance_ai_system |
Model name for the page body |
Configuration
Configure key providers and signing policies through TYPO3 site settings. Supports multiple key providers for different environments (development, staging, production).
Development
composer install composer test # Unit tests composer phpstan # Static analysis (level 8) composer deptrac # DDD layer enforcement vendor/bin/php-cs-fixer fix # Code style (PER-CS3x0)
Dependencies
| Package | Type | Purpose |
|---|---|---|
ext-sodium |
Required | Ed25519 cryptographic operations |
symfony/process |
Required | c2patool/exiftool subprocess wrappers (AI Provenance) |
koh/dev |
Dev | Shared QA tooling |
C2PA Manifest Signing (Enterprise Provenance Layer, ab v0.7.0)
Positionierung: Diese Schicht ist kein regulatorisches Muss für EU AI Act Art. 50 — die maschinen-lesbare Disclosure (IPTC-XMP + Schema.org)
- sichtbares FE-Badge aus Phase 1–4 deckt den regulatorischen Kern bereits ab. C2PA-Signing ist die Enterprise-Authenticity-Schicht: Beweisbarkeit, Anti-Stripping, Chain-of-Custody, Forensik, Interoperabilität.
Ab v0.7.0 enthält das Package eine kryptografische C2PA-Sign-Pipeline:
C2paCertificateProvider— resolved Cert/Key aus/run/c2pa/(vom cert-manager beschickt, 90d Lifetime, quartalsweise Leaf-Rotation).C2paManifestSigner— subprocess-wrapper umc2patool. Auto-detectedc2pa.created(fresh) vs.c2pa.metadatamitparent_ingredient(append) — upstream-Signaturen (OpenAI, Adobe) bleiben in der Chain.ReSignDerivativeFilesListener aufAfterFileProcessingEvent— signiert auch TYPO3-generierte Thumbnails/Resizes (WebP, AVIF, JPEG, PNG), sodass die Provenance-Chain die FAL-Image-Processing-Pipeline überlebt. Signiert wird nur ein im selben Request frisch erzeugtes Derivat, nicht jedes gecachte bei jedem Seitenaufruf. Das Original ist das Parent-Ingredient des Derivats (Actionc2pa.edited): die Bildverarbeitung entfernt jedes Manifest, und ohne Parent begänne die Kette des Derivats bei unserer Signatur statt bei der Generierung (OpenAI o. ä.).StoredFileWriteBack— IPTC-Tags und Signatur entstehen in der beschreibbaren Kopie ausgetForLocalProcessing(true)(auch beim lokalen Treiber eine Temp-Datei untervar/transient/). Die Kopie geht danach über FAL zurück (ResourceStorage::replaceFile()bzw.ProcessedFile::updateWithLocalFile()); derc2pa_statuswird erst nach erfolgreichem Zurückschreiben gesetzt, sonstfailed.- Kein Zurück zu „Human" bei AI-Manifest — setzt ein Editor den Origin
auf
human, obwohl die Datei ein C2PA-Manifest mit AI-Beteiligung trägt (eigene Signatur lautc2pa_status, oder ein AI-Claim im aktiven Manifest der Datei), wird stattdessenai_assistedgespeichert und mit dem AI-assisted-digitalSourceTypesigniert. Die Provenance-Chain lässt sich nicht ehrlich entfernen. Dateien ohne solches Manifest behalten den Human-Pfad (IPTC-Tags werden geleert). provenance:resignCLI für opt-in Backfill bestehender AI-tagged Files (idempotent,--forcefür Re-Sign). Das Ersetzen eines Originals löscht in TYPO3 alle seine Derivate; der Befehl baut die vorher vorhandenen sofort neu, und sie werden dabei signiert (DerivativeRegenerator).--derivatives-onlylässt bereits signierte Originale unverändert und baut nur ihre Derivate neu — für Derivate, die vor der Parent-Verknüpfung entstanden sind.
c2pa_status Statusmodell
| Wert | Bedeutung |
|---|---|
none |
Default — kein Sign-Versuch erfolgt |
signed_external |
Upstream-Signatur erkannt (OpenAI, Adobe), wir haben noch nicht signiert |
signed_self_anchored |
Frisch von uns signiert (Step-CA), kein Parent-Manifest |
signed_self_anchored_append |
Edit-Action auf upstream Manifest appended (parent-source preserved) |
signed_trusted |
Reserviert für Phase 5b — Trust-List-konformes CA-Cert |
stripped |
War signed, Manifest wurde nachträglich entfernt (z.B. durch Re-Encode außerhalb TYPO3) |
failed |
Sign-Versuch crashte (Cert missing, c2patool exit ≠ 0, etc.) |
Trust-Model (wichtig!)
Die Signatur-Pipeline etabliert eine Private Provenance Chain:
- Signatur-Cert kommt von der projekteigenen Step-CA → self-anchored
- Tamper-evident via Hash-Anchored Manifest
- Append-Mode preserved upstream Trust-Chain (OpenAI/Adobe bleiben sichtbar)
- Strip-Detection: gestrippte Manifeste werden via
c2pa_status=strippedim BE sichtbar gemacht → starkes Forensik-Signal
Externe Validatoren (z.B. contentcredentials.org/verify) zeigen
unsere Signatur als "untrusted (unknown CA)" — die Signatur ist
kryptografisch valid und tamper-evident, aber unsere Root steht nicht
in der C2PA Trust List.
Das ist gewollt für Phase 1: interne Beweisbarkeit + Forensik ohne
externes Onboarding-Programm.
Roadmap (Public Trusted Content Credentials)
Wenn echtes externes Branchen-Vertrauen gefordert wird:
- C2PA Conformance Program für unser Generator-Produkt durchlaufen (CAI Conformance)
- Trust-List-konformes Signing-Cert beziehen — Step-CA bleibt intern, externe Sign-Calls nutzen die Trust-List-CA
- Trusted Timestamping (TSA) ergänzen — sonst nicht beweisbar dass Signatur während Cert-Gültigkeit erzeugt wurde
- Key-Isolation — Private Key aus httpd-Container raus, in HSM/KMS/ PKCS#11 oder isolierten Signing-Worker (Sidecar-Pattern)
- Async Queue/Worker statt synchronem BE-Save — Mass-Upload-UX + bessere Failure-Isolation
- Validator-CI-Gates —
c2patool verify+ contentcredentials.org- Roundtrip als Quality-Gate
Diese Punkte sind Out-of-Scope für die initiale Implementierung und benötigen externe Programme/Dienstleister.
EU AI Act Art. 50 Compliance
Das regulatorische Minimum für Art. 50 (gültig ab August 2026) ist „maschinen-lesbar gekennzeichnet UND als künstlich erzeugt erkennbar". Das wird durch die Phasen 1–4 dieses Packages + den Site-Packages abgedeckt:
| Anforderung | Erfüllt durch |
|---|---|
| Maschinen-lesbar | IPTC PhotoMetadata 2025.1 XMP (DigitalSourceType, AISystemUsed) — Phase 3 |
| Detektierbar | Auto-Detection aus C2PA/IPTC beim Upload — Phase 2 |
| Sichtbar für User | FE-Badge via moselwal/sitepackage-base ≥ 3.22.0 — Phase 4 |
| Strukturiert für Crawler | Schema.org ImageObject.creator via koh/structured-content ≥ 1.5.10 — Phase 4 |
Art. 50 fordert nach aktuellem Stand nicht kryptografische Provenance, keine PKI, keine Trust-List-CA, keine Watermarks, keine Blockchain. Die C2PA-Sign-Schicht (Phase 5) ist eine strategische Ergänzung für Beweisbarkeit, Forensik und Enterprise-Authenticity-Use-Cases, nicht eine regulatorische Pflicht-Schicht.
Google AI Search Konformität (Mai 2026)
Google's offizielle Guides bestätigen den Stack-Ansatz:
- AI Optimization Guide — „You don't need to create new machine readable files, AI text files, markup, or Markdown to appear in generative AI search" + „Structured data isn't required for generative AI search, and there's no special schema.org markup you need to add"
- Using Gen-AI Content — explizit für AI-Bilder: „AI-generated images must contain metadata using the IPTC
DigitalSourceTypeTrainedAlgorithmicMediametadata"
→ Der einzige harte technische Pflicht-Punkt von Google (IPTC DigitalSourceType) wird durch Phase 3 (IptcXmpWriter) abgedeckt. Schema.org-Erweiterungen (Phase 4) bleiben für klassische SERP-Features sinnvoll, sind aber für Generative AI Search nicht erforderlich. C2PA-Signing (Phase 5) wird von Google aktuell nicht für Search-Ranking ausgewertet — bleibt strategisch als Enterprise-Authenticity-Layer.
E-commerce Edge-Case (nicht relevant für unsere Sites): „AI-generated product data such as title and description attributes must be specified separately and labeled as AI-generated" — würde bei künftigen Product-Pages den Need für separate AI-Labeling-Felder in Product-CB triggern.
Hinweis: Diese Doku ist keine juristische Beratung. Für die
formelle Art.-50-Bewertung empfehlen wir eine eigene Risikoanalyse +
Disclosure-Konzept, das die im Projekt umgesetzten Layer benennt und
die Strip-Detection-Strategie (c2pa_status=stripped) dokumentiert.
License
GPL-2.0-or-later — see the SPDX licence text.