
Artikel unserer Co-Founderin Ariane in IM+io
Artikel für IM+io des August-Wilhelm-Scheer-Instituts unserer Mitgründerin Ariane Scheer-Danielsson über die Notwendigkeit eines Trust Layers für Physical AI.

Die meisten Teams integrieren C2PA, indem sie die Rust- oder C++-Toolkits einbinden. Wir sind den anderen Weg gegangen und haben den gesamten Stack in reinem TypeScript implementiert: JUMBF-Box-Parsing nach ISO 19566-5, COSE-Signierung, Manifest Stores, Claim-Validierung. Läuft im Browser, in Lambda-Funktionen und in React Native, ohne WASM-Toolchain und ohne native Builds.
Diese Entscheidung hat uns durch Teile der Spezifikation gezwungen, die Bibliotheksnutzer nie sehen. Dieser Beitrag ist eine Tour durch die Randfälle der Spec und durch die technischen Entscheidungen, die erst entdeckt werden, wenn der Code echte Dateien überleben muss.
Content Credentials sind im Moment der Erfassung am wertvollsten. Erfasst wird im Browser, auf dem Handy, in serverlosen Funktionen. Braucht die Provenance-Engine eine native Laufzeit, wird jede dieser Umgebungen zum Integrationsprojekt statt zu einem npm install.
Die nahe liegende Frage lautet: Warum nicht das offizielle JavaScript-Tooling nutzen? Die Content Authenticity Initiative pflegt mit c2pa-js (konkret @contentauth/c2pa-web) eine JavaScript-Bibliothek, die jedoch nur die halbe Miete ist. Sie ist rein auf das Lesen und Prüfen im Browser ausgelegt – sie kann Manifeste zum Erfassungszeitpunkt auf dem Endgerät weder erzeugen, noch hashen oder kryptografisch signieren. Zudem ist c2pa-js ein WebAssembly-Wrapper (c2pa.wasm) um die Rust-Codebasis. In eingebetteten mobilen JavaScript-Laufzeiten – insbesondere Apples JavaScriptCore auf iOS – steht WebAssembly entweder gar nicht zur Verfügung oder wird durch restriktive JIT- und Speichersicherheitsrichtlinien des Betriebssystems blockiert.
Indem wir eine reine TypeScript-Bibliothek ohne jede WASM- oder native Binärabhängigkeit gebaut haben, können wir sie gezielt für restriktive Zielsysteme bündeln. In unserem SDK kompiliert esbuild die Bibliothek für target: 'ios16' in ein eingebettetes Bundle für sdk-ios (das direkt in Apples JavaScriptCore ausgeführt wird) sowie ein es2022-IIFE für sdk-android. Die JavaScript-Engine übernimmt JUMBF-Box-Serialisierung, Asset-Parsing und Claim-Hashing; die eigentliche Signierung wird über eine native Bridge an die Hardware-Sicherheitsmodule des Geräts übergeben: die Apple Secure Enclave und den Android Keystore.
Es gibt einen zweiten, leiseren Vorteil. Unser SDK, unsere API und unsere Frontends teilen eine Sprache. Die Typdefinitionen für ein Manifest sind also dieselben Typen, gegen die die API validiert. Typsichere Verträge über die Vertrauensgrenze hinweg eliminieren eine ganze Klasse von Serialisierungsfehlern.

Die C2PA-Spec baut auf JUMBF auf, ISO 19566-5, einem generischen Box-Format. Ein Manifest ist eine SuperBox mit Description Box, Claim Box, Assertion-Boxen (JSON oder CBOR) und einer Signatur-Box. Soweit, so aufgeräumt.
In der Praxis verbringt man seine Zeit mit zwei Dingen, über die die Spec hinweggeht. Erstens die C2PA-spezifischen Boxen wie die Salt Box und die Embedded File Boxen für Thumbnails. Zweitens der Transport: In JPEG steckt die JUMBF-Nutzlast in APP11-Segmenten, die gefunden, sortiert und von einer kleinen Zustandsmaschine wieder zusammengesetzt werden müssen. Unsere JPEG.ts macht genau das, läuft durch Segmente bis zum Start-of-Scan und rekonstruiert mehrteilige Manifeste.
Konzeptionell schwer ist das alles nicht. Es ist nur so schlecht dokumentiert, dass jede Implementierung es unabhängig neu entdeckt.
Die Training-and-Data-Mining-Assertion hat ihre Felder verschoben. Frühe Entwürfe waren sich uneinig, ob Einträge auf oberster Ebene oder verschachtelt unter einem entries-Schlüssel liegen. Unser Reader akzeptiert beide Formen, unser Writer schreibt die aktuelle. Der Code verlinkt das zugehörige Spec-Issue, weil der nächste Implementierer diese Brotkrume verdient.
digitalSourceType gibt es in zwei Varianten. Manche Implementierungen präfixen Werte mit https, andere mit http. Wer strikt validiert, bricht die Interoperabilität mit echten Dateien. Wir normalisieren beim Lesen und bleiben beim Schreiben exakt: toleranter Leser, präziser Schreiber.
Claim-Versionierung ist keine Nummer, sondern eine Gabelung. V1- und V2-Claims unterscheiden sich in URN-Präfixen, Action-Assertion-Formen (softwareAgent gegen softwareAgentIndex) und Template-Unterstützung. Unsere ActionAssertion pflegt parallele Mapping-Pfade für beide, und die Claim-Klasse leitet ihr Label aus ihrer Version ab, damit beides nie auseinanderläuft.
Ich hätte angenommen, dass dies abgehandelte Ecken der Spec sind. Waren sie nicht. Wer auf C2PA aufbaut, sollte Zeit für diese Schicht einplanen.
Eine boolesche Antwort gültig/ungültig ist in der Praxis nutzlos, wenn man wissen will, was fehlgeschlagen ist; unsere eigene Re-Signier-Pipeline braucht strukturierte Ergebnisse, um V3-Ingredient-Assertions zu bauen. Also erzeugt die Validierung eine flache Liste spec-konformer Statuscodes, und wir unterscheiden MalformedContentError (die Datei ist strukturell kaputt) von ValidationError (die Datei ist in Ordnung, das Vertrauen nicht).
Ein ehrliches Geständnis: In Manifest.read() gibt es ein TODO für komprimierte Boxen (brob). Wir unterstützen den unkomprimierten Pfad und lassen den Rest aus. Aber, eine ehrliche Lücke zu shippen schlägt stilles Falschverhalten.
Die C2PA-Spec ist bewusst erweiterbar: Eigene Assertion-Typen erlauben es, domänenspezifische Daten in signierte Manifeste einzubetten. Zu eigenen Typen sollte man nur greifen, wenn die Nutzlast tatsächlich domänenspezifisch ist. Eine Sensorszene oder ein verifiziertes KFZ-Kennzeichen rechtfertigen das; ein Titel oder Autorenname nicht.
Die zentrale Abstraktion bildet CustomJsonAssertion<T>, ein abstraktes Generic, das typisierte Inhalte in eine JSON-JUMBF-Box serialisiert, verlustfrei einliest und round-trippt:
abstract class CustomJsonAssertion<TContent> extends Assertion { public uuid: Uint8Array = JSON_UUID; public content?: TContent; readContentFromJUMBF(box: IBox): void { // Validates box type and UUID, then casts JSON content if (!(box instanceof JSONBox) || !uuidMatch(this.uuid, JSON_UUID)) throw new ValidationError( ValidationStatusCode.AssertionJSONInvalid, this.sourceBox, `${this.label} assertion has invalid type`, ); this.content = box.content as TContent; } generateJUMBFBoxForContent(): IBox { const box = new JSONBox(); box.content = this.content; return box; }}Weil die Basisklasse die Box-Verwaltung übernimmt, bleiben konkrete Assertion-Klassen minimal. Unsere TrustLabelAssertion deklariert lediglich ihr Label und erbt alles andere:
const ASSERTION_LABEL = 'com.trustnxt.trust-label';class TrustLabelAssertion extends CustomJsonAssertion<LabelContent> { public label = ASSERTION_LABEL; static loadFromManifest(manifest: Manifest) { return CustomJsonAssertion.load( ASSERTION_LABEL, TrustLabelAssertion, manifest, ); }}Das Label folgt der Reverse-Domain-Notation (com.trustnxt.trust-label), um Namenskonflikte auszuschließen. Der Typ LabelContent bündelt GPS, Orientierung, Beschleunigung, Gravitation, Drehraten, Netzwerkstatus, Geräteinformationen, Kameraeinstellungen und Analyseergebnisse wie Kennzeichenerkennungen. Jedes Sensorfeld enthält ein unavailableReason-Feld: Zu wissen, warum ein Messwert fehlt, ist für Prüfprozesse genauso entscheidend wie das Vorhandensein des Werts selbst.
Schema-Versionierung folgt vier Regeln: Neue optionale Felder können jederzeit ergänzt werden, ohne bestehende Parser zu stören; Felder werden nie entfernt, nur deprecated; Fehlen wird explizit via unavailableReason deklariert; Typänderungen erhalten einen neuen Feldnamen. In gerichtsfesten Beweisketten ist ein signiertes Manifest unveränderlich. Was einmal versiegelt wurde, bleibt untrennbar mit dem Asset verbunden.
Während der EU AI Act strenge Daten-Governance-Pflichten für KI-Pipelines vorschreibt, liefert das C2PA-Ökosystem die passende Antwort: die Training-and-Data-Mining-Assertion, eine signierte Erklärung direkt im Inhalt, ob er für KI-Training herangezogen werden darf. Für jeden Anwendungsbereich deklariert der Urheber eine Auswahl:
interface TrainingAndDataMiningEntry { choice: 'allowed' | 'notAllowed' | 'constrained'; constraintInfo?: string;}// Example: allow data mining, block AI training{ entries: { "c2pa.ai_training": { use: "notAllowed" }, "c2pa.data_mining": { use: "allowed" }, "c2pa.ai_inference": { use: "constrained", constraint_info: "Licensed use only" } }}Weil die Angabe in einem signierten Manifest liegt, reist die Präferenz mit der Datei mit und ist fälschungssicher. Eine robots.txt-Datei kann ignoriert oder abgeschnitten werden. Eine signierte Assertion ist an die Bytes gebunden. Wer sie entfernt, zerstört die Signatur, und das Fehlen einer erwarteten Assertion wird selbst zum Signal.
Die Assertion unterstützt zwei Namespaces. Das CAWG-Label (Creator Assertions Working Group) c2pa.training-mining orientiert sich an der Community-Spezifikation. Das ursprüngliche C2PA-Label bleibt aus Gründen der Abwärtskompatibilität erhalten. Der Konstruktor nutzt standardmäßig CAWG:
constructor(isCAWG = true) { super(); this.isCAWG = isCAWG; this.label = isCAWG ? AssertionLabels.cawgTrainingAndDataMining : AssertionLabels.trainingAndDataMining;}Unsere Haltung zu Einwilligung versus Provenance: Eine Trainings-Einwilligungs-Assertion beschreibt, was mit Inhalten geschehen darf. Unsere Beweispipeline beweist, was tatsächlich mit ihnen geschehen ist, durch versiegelte Erfassung, verifizierte Integrität und signierte Anreicherung. Wenn beide Bausteine zusammenwirken, beantwortet die Datenlieferkette zwei Fragen: Durfte der Inhalt so verwendet werden, und lässt sich beweisen, wie er verarbeitet wurde. Genau diese Transparenz verlangt die Compliance unter dem EU AI Act.
C2PA (Coalition for Content Provenance and Authenticity) ist ein offener Standard, um digitale Inhalte mit kryptografisch signierter Herkunft zu versehen. Ein Manifest bündelt Claims, Assertions (Zutaten, Aktionen, Hashes) und eine Signatur direkt in der Datei. Jedes konforme Werkzeug kann dann prüfen, wer signiert hat, was behauptet wurde und ob sich seitdem etwas geändert hat. Wenn ihr mehr erfahren wollt, schau in unseren C2PA Artikel!
Was ist mit c2pa-js? Gibt es von der Content Authenticity Initiative nicht längst eine JavaScript-Bibliothek? Das offizielle Paket @contentauth/c2pa-web dient im Browser ausschließlich dem Lesen und Analysieren vorhandener Manifeste, nicht deren Erstellung oder Signatur. Darüber hinaus stützt es sich auf ein vorkompiliertes WebAssembly-Modul (c2pa.wasm). WebAssembly lässt sich in restriktiven mobilen Laufzeiten wie Apples JavaScriptCore unter iOS nicht ohne Weiteres betreiben. Unsere Bibliothek ist reines TypeScript und wurde für die lückenlose Manifest-Erstellung, Hashberechnung und Signatur über Browser, Serverless und native mobile Laufzeiten hinweg entwickelt.
Warum nicht einfach c2pa-rs einbinden? Einbinden bedeutet, die Abstraktionsgrenze eines anderen zu übernehmen. Wir brauchten Browser-Ausführung, Streaming großer Assets und eigene Assertion-Typen, und all das liegt unterhalb dieser Grenze. Die vollständige Begründung steht in unserem Build-vs-Buy-Beitrag.
Validiert eine TypeScript-Implementierung wie die Referenzwerkzeuge? Konformität ist eine Frage der Bytes auf der Leitung, nicht der Implementierungssprache. Tolerantes Lesen und exaktes Schreiben sind das, was interoperabel hält.
Kann ich die Bibliothek ohne TrustNXT-Dienste nutzen? Der Kern-Stack (Assets, JUMBF, Manifeste, COSE, Validierung) ist in sich geschlossen und als c2pa-ts auf GitHub verfügbar. Unsere Zertifikats- und Zeitstempeldienste docken über die Interfaces Signer und TimestampProvider an.
Und was ist mit Video? BMFF-Unterstützung existiert mit lazy Box-Parsing und Offset-Patching. Das verdient einen eigenen Beitrag, und es hat einen bekommen: Streaming großer Videos.
Brechen eigene Assertionen fremde Validatoren? Nein. Die Spec verlangt, dass konforme Validatoren unbekannte Assertionen tolerant behandeln. Generische Verifizierer ignorieren sie oder listen sie als unkategorisiert auf; die Signatur bleibt gültig. Domänenspezifische Werkzeuge wie unsere Inspect-API stellen sie vollständig dar.
Ist die Trainings-Assertion rechtlich bindend? Sie ist eine maschinenlesbare, fälschungssichere Willenserklärung des Rechteinhabers. Ihre Durchsetzbarkeit richtet sich nach dem jeweiligen Rechtsraum; ihre Echtheit ist mathematisch gesichert. Unter dem EU AI Act gewinnen solche maschinenlesbaren Signale bei der Prüfung von Trainingsdaten enorme praktische Relevanz.
Der Code ist die ehrliche Version dieser Geschichte. Der Quellcode steht als c2pa-ts auf GitHub bereit. Wer selbst C2PA implementiert: mit der Spec anfangen, die obigen Mehrdeutigkeiten erwarten, die Reader tolerant und die Writer exakt halten.