Two layers or it didn't happen: marking AI images for the EU AI Act in pure TypeScript
Since 2 August 2026, the EU AI Act requires AI products to mark generated content in a machine-readable way. The European Commission's Code of Practice adds a practical twist: use at least two layers, because any single
Since 2 August 2026, the EU AI Act requires AI products to mark generated content in a machine-readable way. The European Commission's Code of Practice adds a practical twist: use at least two layers, because any single technique can be stripped. Systems already on the market have until 2 December.
I wanted this to be a one-line change for web developers, so I built Filigrane, an MIT-licensed TypeScript toolkit. Here is what I learned.
Layer 1: C2PA, from the spec
C2PA ("Content Credentials") is the industry standard: a signed manifest embedded in the file. The official SDK is written in Rust, and its npm package ships a native binary. I wanted something that runs in browsers and edge runtimes, so I implemented the parts I needed in TypeScript on WebCrypto: a CBOR codec, JUMBF boxes, COSE_Sign1 signatures and X.509 certificates.
The most fun bug was a chicken-and-egg problem. The manifest contains a hash of the file excluding the manifest itself, so the exclusion range depends on the manifest size, which depends on how the exclusion range is encoded. The fix is a small fixed-point loop: build, measure, rebuild until the layout is stable. It converges in two iterations.
The golden rule: never trust your own validator. Every commit, CI signs JPEG, PNG and WebP files and asks the reference implementation (c2pa-rs, through its Python bindings) to validate them. All are reported "Valid".
Layer 2: IPTC metadata
The IPTC DigitalSourceType field (trainedAlgorithmicMedia) is what search engines and photo tools already read to display "AI-generated". It is a few lines of XMP, and it costs nothing to add.
Layer 3: an invisible watermark
Metadata disappears the moment a social network re-encodes your image. A watermark lives in the pixels. Filigrane's watermark spreads a 64-bit code over thousands of DCT coefficients of a canonical 256-pixel copy of the image, which is why it survives resizing. Embedding is "informed": each coefficient is pushed only as far as needed, and less in flat areas, keeping the change invisible (about 44 dB PSNR).
Measured on five photo-like images, it survives JPEG quality 30, resizing to 25 %, brightness and contrast changes, and metadata stripping, with zero false positives on 20 unmarked images. It does not survive cropping yet; that's next.
The watermark ID is recorded in the C2PA manifest as a soft binding, so the two layers point to each other.
Verification in the browser
The whole engine (C2PA, crypto, JPEG/PNG codecs, watermark) is about 33 kB gzipped and runs in a Web Worker. The online verifier never uploads your file. You can try a demo there: mark an image, "post" it to a simulated social network, and see the watermark keep the signal alive when the other two layers are gone.
Attack your own verifier
Before launching, I attacked my own verifier. The worst finding: an unsigned assertion slipped into a manifest could make a completely modified image look "valid". A certificate that was not a CA could also issue "trusted" certificates under any name, one crafted PNG froze the parser, and a 500 kB PNG inflated to 512 MB in memory. All of it is fixed in 0.1.2, each attack has a regression test, and a fuzzer ran 3,000 corrupted files without a hang.
And the chatbot?
Article 50 also requires telling users they are talking to an AI. <ai-disclosure> is a Web Component that speaks the 24 official EU languages, works with any framework, and leaves a visible "AI" chip after it is dismissed.
Try it
npm install @filigranejs/core @filigranejs/ui
npx filigrane scan .
Repository, docs and benchmark: https://github.com/Sailxe42/filigrane
This is not legal advice. I built Filigrane with an AI coding assistant, trying to back every claim with a test, and this article was drafted by an AI assistant from my notes and checked by me. Feedback and contributions, especially translation reviews, are very welcome.
Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.