Robustness report
How well our marks hold up, measured on our own machines and published, as the Code of Practice's Commitment 3 asks. Measured 2026-10-23, on CPU, with the code and models the API runs.
Two layers, two kinds of strength
- The C2PA manifest is metadata, signed: it proves who signed what, and breaks loudly if the file changes. Most platforms strip metadata when they re-encode an upload, so it rarely survives a re-post.
- The watermark (TrustMark, variant Q) is in the pixels and carries a 61-bit ID. It survives the usual re-encoding and resizing, and its ID leads to the file's durable credential, the manifest we keep. This report measures it.
How it was measured
78 AI pictures made by GenVista (everyday scenes; median 0.7 megapixels), each watermarked with a random ID as the API does, then edited each way below, and read back. A success is the exact ID; anything else, including no watermark found, counts as a miss. The same edits on the pictures without a watermark show how often the reader claims a mark that isn't there. Intervals are Wilson's 95%. marker/tools/robustness.py reproduces it.
The watermark after each edit
| Edit | ID read back | 95% interval | Unmarked pictures read as marked |
|---|---|---|---|
| Unchanged (PNG) | 100% (78/78) | 95.3%–100% | 0 |
| JPEG, quality 95 | 100% (78/78) | 95.3%–100% | 0 |
| JPEG, quality 85 | 100% (78/78) | 95.3%–100% | 0 |
| JPEG, quality 75 | 100% (78/78) | 95.3%–100% | 0 |
| JPEG, quality 60 | 100% (78/78) | 95.3%–100% | 0 |
| JPEG, quality 50 | 100% (78/78) | 95.3%–100% | 0 |
| WebP, quality 80 | 100% (78/78) | 95.3%–100% | 0 |
| Long side 1080 px, JPEG 85 (a social network's upload) | 100% (78/78) | 95.3%–100% | 0 |
| Resized to 75% | 100% (78/78) | 95.3%–100% | 0 |
| Resized to 50% | 100% (78/78) | 95.3%–100% | 0 |
| Resized to 25% | 100% (78/78) | 95.3%–100% | 0 |
| Cropped to the centre 90% of each side | 100% (78/78) | 95.3%–100% | 0 |
| Cropped to the centre 75% of each side | 52.6% (41/78) | 41.6%–63.3% | 0 |
| Cropped to the centre 50% of each side | 0% (0/78) | 0%–4.7% | 0 |
| Brightness +20% | 88.5% (69/78) | 79.5%–93.8% | 0 |
| Brightness −20% | 100% (78/78) | 95.3%–100% | 1 |
| Contrast +20% | 100% (78/78) | 95.3%–100% | 0 |
| Contrast −20% | 100% (78/78) | 95.3%–100% | 0 |
| Saturation +30% | 100% (78/78) | 95.3%–100% | 0 |
| Greyscale | 98.7% (77/78) | 93.1%–99.8% | 1 |
| Gaussian blur, radius 1 px | 100% (78/78) | 95.3%–100% | 0 |
| Gaussian blur, radius 2 px | 100% (78/78) | 95.3%–100% | 0 |
| Gaussian noise, σ 5 | 100% (78/78) | 95.3%–100% | 0 |
| Gaussian noise, σ 10 | 100% (78/78) | 95.3%–100% | 0 |
| Rotated 2° and cropped straight | 100% (78/78) | 95.3%–100% | 0 |
| Rotated 5° and cropped straight | 41.0% (32/78) | 30.8%–52.1% | 0 |
| Mirrored left to right | 100% (78/78) | 95.3%–100% | 0 |
The watermark costs little: a median PSNR of 42.2 dB against the original (the lowest 39.0 dB). On a laptop CPU, embedding took a median 0.15 s and reading 0.06 s.
The safety check's false alarms
The same 78 everyday pictures through the safety check, under both policies: everyday pictures should all be allowed.
| Policy | Allowed | Flagged or blocked | 95% interval of false alarms |
|---|---|---|---|
ai-act | 78/78 | 0 | 0%–4.7% |
safe-only | 78/78 | 0 | 0%–4.7% |
A check took a median 0.12 s on a laptop CPU. How often explicit pictures are caught isn't measured here: no test of ours makes or holds a sexual picture. The nudity classifier's own published evaluation stands in for it, and the apparent-age error rates measured on FairFace's faces are on the safety check's page, with its known limits.