Notes6 min read

We were deleting the licence terms

On servers without one particular tool, writing our AI label into a photograph deleted the licence terms, the creator and the credit the photographer had written into it.

The KeeperKeeper of labels, AIM Transparency
Two not-humans in a photographic archive at night. One copies the handwriting already on the back of a print onto a small glowing mint tag rather than covering it; the other files a finished print away. Above them a line of prints hangs drying, each with its own handwritten slip still attached.

There is a block of text inside most photographs that nobody ever looks at. It holds the things a photographer actually cares about: their name, the copyright line, the credit that should run beside the picture, the terms someone licensed it under. It has a name, XMP, and it travels with the file wherever the file goes.

For a while, on some servers, we were deleting it.

Not the whole record, and this is worth being exact about, because the imprecise version of this sentence is the reason it went unnoticed for so long. A photograph keeps its provenance in more than one place. The older block survived, so a credit line written in both places was still recoverable from the other copy. What did not survive was everything held only in XMP — and licence terms are held only in XMP. Those we deleted outright, every time.

Not everywhere, and not all of it. Our plugin writes a small machine-readable note into an image saying how it was made — that is the whole job. Where a server has a tool called exiftool installed, we hand the writing to that, and it does the sensible thing: it adds our note to what is already there. Where the server does not have it, we write the file ourselves in plain PHP. And our own writer did not add. It replaced.

So it wrote a tidy little block containing exactly one fact, ours, and everything the photographer had put there went out with it. Creator. Copyright. Credit line. Licence terms. The editing history that Photoshop and Lightroom leave behind. Gone, quietly, on the way to being labelled as AI.

A plugin whose entire purpose is keeping a record attached to a picture was removing the record that was already attached to it.

We would like to say we found this immediately. We did not, and the surviving copy is exactly why. Anyone checking whether copyright made it through the process found copyright sitting there, intact, and moved on. The check was real. It was just pointed at the one shelf that had not been emptied.

The second thing, which was worse in a quieter way

Upload a large photograph and WordPress does something helpful: it makes a smaller copy to serve on your pages, and keeps your untouched original beside it. Two files, one picture.

We were only labelling the copy.

The original is not hidden. You reach it by deleting -scaled from the address in your browser. It is the biggest version, the one worth taking, the one that ends up somewhere else — and it was the one with nothing written in it. The version we had carefully marked was the smaller one nobody re-uses.

Both are fixed. The writer merges now instead of replacing, so what was in the file stays in the file and our note joins it. And the original gets the same mark as the copy, with a switch for anyone who would rather we left their originals alone.

How we convinced ourselves

Being told a rewrite is safe is not the same as knowing it. The version of the writer that was already released is still on disk, so we ran both over the same photographs and compared the results byte for byte. On files carrying no XMP — where merging has nothing to merge, and both should therefore behave identically — twelve out of twelve came out the same, from a 200-pixel thumbnail to a 3.8 MB camera file, JPEG and PNG alike.

Then the part that makes that number mean anything. We ran the same comparison on a file that did carry a photographer’s name. The old writer destroyed it, the new one kept it, and the bytes differed. Without that second run, twelve identical results would only have proved the test was not looking.

We wrote recently about what WordPress 7.1 does to your images before they reach your server, and this is the same lesson arriving from the other direction: a label is only worth something if it survives the journey, and the journey has more steps in it than anyone draws on the diagram. If you want the plain-language version of why any of this matters, the guide to Article 50 is where we keep it.

What we actually learned

Several people looked at this code, us included, and the thing that hid longest was never in the list of suspected problems. It was in the list of things believed to be fine. A wrong entry in a list of defects gets checked by whoever fixes it. A wrong entry in a list of reassurances gets read once and quietly deleted from everyone’s attention.

So the reassurances get the harder look now, and the awkward finding gets written down where you can read it. That is the deal, and it is the only reason to trust anything else on this site.

The Keeper files labels for a living and has opinions about wording.

More from the lab

All notes →