Ryan Yannelli

2026-08-3010 minpersonal

Anthropic's watermark controversy sent me down a watermarking rabbit hole

Anthropic's watermark can show probable model use. I wanted to identify the exact issued copy that contained a copied sentence.

AI


Anthropic announced a watermark for Claude on August 14. Future models get it at launch; older models get it over the coming months. It does not hide characters or use steganography. Instead, a key and the few words that came before seed the coin flip whenever Claude has a low-stakes word choice to make. Anthropic says the choice stays random and Claude does not lean toward a specific word. A detector with the right key can then estimate whether Claude touched the passage. This work supports the European Union’s Code of Practice on Transparency of AI-Generated Content.

People quickly argued about what a detector hit proves, but Anthropic gives a narrow answer: probable Claude involvement somewhere in the text. A hit does not identify a user or conversation, and it does not distinguish raw output from text that somebody edited later. Anthropic describes the mechanism in a technical article; its help center gives a simpler explanation.

That nuance will not survive contact with a professor who has 400 essays to grade (or a client checking a contractor’s draft). To them, a flag is a flag. Business Insider reported that some users canceled their subscriptions for this reason.

I got curious about a different problem.

Suppose I create the same document for several customers and change a few phrases so that each copy is distinct but still reads well. Months later, one sentence appears somewhere it should not. By then it may have passed through a browser, a clipboard, an email client, and a CMS field. I want to identify the exact issued copy that contained it.

Anthropic’s watermark asks whether a model was involved. Mine needed to identify one issued copy from all the others, using one sentence that remained after copying.

Why not invisible Unicode?

My first pass used invisible Unicode characters. A 2025 paper from a team led by Yanming Li hides Unicode sequences inside a document. Those sequences let an auditor check whether a model used the document for training. The method works when every transfer preserves every byte.

Copy and paste does not preserve every byte. A selection can travel as HTML, plain text, and several other formats at the same time. The destination application picks one format and drops the rest. Unicode normalization or a security filter can leave a zero-width character intact in Slack and remove it from a Google Doc. The W3C Clipboard API specification says the destination application controls the result.

To keep this short - invisible characters cannot be the main channel. The visible words have to contain the mark.

Where do the alternatives come from?

The generator has to create the watermark while it writes the sentence. It records two or three alternatives at a few carrier sites and checks each one in the full sentence.

The system can {retain | preserve | keep}
the source {marker | fingerprint | signature}
after the passage is {copied | transferred | pasted}
into another application.

A reader sees one normal sentence. The example has three carrier sites with three options each (option 0 at each site is the baseline), and the reader has no reason to notice them.

A thesaurus cannot judge whether a swap changes legal meaning, medical meaning, tone, or certainty, so generate and approve the alternatives while the full sentence is still in context.

Leave names, dates, numbers, negation, quotations, direct commands, and specialized legal or medical language alone because a wrong swap can change a contract or dosage for a small increase in capacity.

Different sources get different words at these carrier sites, so the detector needs something that stays fixed across every copy: a sentence skeleton.

The system can <CARRIER_1>
the source <CARRIER_2>
after the passage is <CARRIER_3>
into another application.

Hash the skeleton and include it in the fingerprint expected for each source. Someone who copies a valid pattern into unrelated text gets a different fingerprint.

The detector also needs the original manifest. The manifest stores:

  • the fixed text;
  • the approved options at each carrier site;
  • the option order; and
  • the normalization rules.

The boundary and skeleton provide enough framing for most sentences. Reserve four carrier sites when you need a stronger frame. Put two near the start and two near the end, with a fixed pattern at each pair. The prose still contains ordinary word choices without literal ones or zeros. Those sites reduce the payload, so short sentences work better without them.

How many sites can change?

Change every carrier site and two copies become easy to distinguish side by side. The prose also starts to sound unnatural. Set a limit: change no more than k of n carrier sites. The code uses both the selected options and the changed positions.

The three-site example above, with a limit of two changes, has 19 patterns (one with no changes, six with one, 12 with two). That is enough for a four-bit payload. The other three rows give payloads 0, 1, and 2 a second candidate each. Four bits is a toy. Apply the same calculation to a 15-site sentence:

j=05(15j)2j=122,027log2122,027=16\sum_{j=0}^{5}\binom{15}{j}2^{j}=122{,}027 \qquad \lfloor\log_{2}122{,}027\rfloor=16

A sentence with 15 carrier sites, two alternatives beyond the baseline, and no more than five changes has 122,027 permitted patterns. That is enough for 16 bits. Seven binary sites with no more than three changes give 64 patterns and six bits. Ten sites with two alternatives and no more than four changes give 4,521 patterns and 12 bits.

I would rather accept a smaller payload than twist a sentence to meet a quota it cannot carry.

How does each source get its pattern?

Give each customer or distribution channel a seed. Use the string that already identifies it, such as customer/1042 or publication/channel-a. Derive a source key with a keyed-hash message authentication code (HMAC). The master key is the HMAC key, and the message is the literal string source followed by the seed.

Use the source key for the second HMAC. Its message contains the literal string frame, the sentence hash, the document identifier, a collision counter, and the carrier profile. Length-prefix each field before concatenation so two different inputs cannot produce the same message bytes. Then truncate the result to the available payload length.

Ks=HMACK(sources)K_{s}=\operatorname{HMAC}_{K}(\texttt{source}\parallel s) ps,c=Truncb(HMACKs(framehdcprofile))p_{s,c}=\operatorname{Trunc}_{b}\bigl(\operatorname{HMAC}_{K_{s}}(\texttt{frame}\parallel h\parallel d\parallel c\parallel\mathrm{profile})\bigr)

I use the HMAC construction from NIST’s specification for the cryptographic part.

The words and source name are public. Keep the master key secret. Anyone who has the words, source name, and method still needs the master key to calculate the expected source pattern.

Short payloads collide sometimes, so check before distribution. If another source already owns the payload for that sentence and release, increment the counter and run the second HMAC again. Store the successful counter under (source, d, h), because collision handling and detection both operate on that frame (my first draft kept one counter per source, and bumping it reshuffled every other sentence that source had).

List every valid sparse pattern in a fixed order: fewest changes first, then position, then option number. Give each row a zero-based rank. For the three-site example:

rank  sites changed            words at the three sites
0     none                     retain / marker / copied
1     site 1 -> option 1       preserve / marker / copied
2     site 1 -> option 2       keep / marker / copied
3     site 2 -> option 1       retain / fingerprint / copied
...
12    sites 1,3 -> options 1,2 preserve / marker / pasted
...
18    sites 2,3 -> options 2,2 retain / signature / pasted

A payload p selects every rank congruent to p modulo 2^b. My first draft mapped one payload to one row and threw the leftover rows away (56,491 of them in the 15-site example). Those extra rows let you pick the better-sounding sentence. Each 16-bit payload has about 1.86 candidate rows in this example (122,027 / 65,536). Score those candidates, choose the lowest-cost row, and break ties by rank. This example uses the number of changes as its cost; a naturalness score is TBD.

In short, the payload picks a small set of rows and you pick the best one in the set.

xq=p+q2bp^=Rank(z^)mod2bx_q=p+q2^b \qquad \hat p=\operatorname{Rank}(\hat{\mathbf z})\bmod 2^b

Keep only values of q that put the rank inside the codebook.

Use the three-site sentence for a four-bit worked case. Suppose the HMAC for customer/1042 truncates to 12. Ranks congruent to 12 modulo 16 are 12 and 28; only 12 exists, so 1042 gets “The system can preserve the source marker after the passage is pasted into another application.” Suppose customer/1043 truncates to 2. Ranks 2 and 18 both exist. Rank 2 changes one site and rank 18 changes two, so 1043 gets “The system can keep the source marker after the passage is copied into another application.”

The key, seed, document identifier, counter, manifest, and profile together fix the sentence. The server adds no random step when it serves the result.

How do you read a sentence back?

The detector normalizes the text to plain characters and ordinary whitespace, then finds the fixed parts of a stored template. It maps each carrier slot to its option number, ranks the resulting vector, and takes that rank modulo 2^b. The result is the payload to compare with each source in the selected registry.

Run it on the 1043 sentence: “keep” is option 2 at site 1, “marker” and “copied” are the baseline, so the vector is (2, 0, 0), the rank is 2, and 2 modulo 16 is 2. That matches the payload stored for customer/1043.

Count a payload match as a hit. Reject values that do not match exactly. One changed carrier word changes the rank. The payload changes unless the new rank is congruent to the old rank modulo 2^b.

For one comparison between a 16-bit sentence and an incorrect source, the chance of a false match is between 1 in 122,027 and 1 in 61,014, depending on whether the payload owns one row or two. Call it one in 65,536.

The risk that matters is misattribution. Someone edits one carrier word to another approved option, the new rank modulo 2^b happens to equal a different source’s payload, and the detector names the wrong customer. With 200 sources and 16 bits, that happens on about 200 of 65,536 single edits (0.3%). The exact-match rule does not catch it, because the edited sentence matches exactly, just to the wrong source.

Mark four sentences independently and require all four to identify the same source. That gives 64 bits:

2645.42×10202^{-64}\approx 5.42\times 10^{-20}

That value describes one comparison with one source. Multiply it across every source and candidate document in the search to get the full false-positive rate.

What survives, and what breaks it?

The mark survives the boring stuff: a paste from a browser into an editor, rich text collapsing to plain text, line wraps, mangled whitespace, and stripped hidden characters. None of those changes touches the carrier words.

Change one carrier word to something outside the approved options and the sentence frame fails. Change it to another approved option and the frame still parses, which is the misattribution case above. Paraphrasing or translating changes the carrier words and removes the code. A screenshot adds OCR noise that can erase a slot or two, and a partial sentence can lose the frame entirely.

A miss does not prove that the text was never marked, because ordinary editing can remove the mark without any intent to hide it. A hit ties the passage to one issued copy, but it cannot identify the person who put that copy online. A recipient can forward a document in seconds, and the mark cannot distinguish that recipient from another person who received it later.

Several recipients can also compare their copies word for word, identify the carrier sites, and assemble a combination that nobody received. A one-sentence mark cannot prevent that attack (two customers and a diff tool are enough). A fingerprint spread across many sentences or documents requires the recipients to find and alter more carrier sites, but it still does not prove who published the text.

Invisible Unicode and punctuation choices can still be backup channels. A surviving hidden sequence can add checksum bits, and an approved comma or contraction can add a few more states. Do not rely on them, because normal transfer can remove them.

The individual pieces already exist in published research. LexiMark uses selected word substitutions to check whether a document appeared in model training data. SemaMark ties its mark to sentence meaning instead of exact words, so it can survive a paraphrase that removes this design’s mark.

This design combines:

  • a source seed;
  • a sparse enumerative code;
  • a stored variant manifest;
  • HMAC tied to the text; and
  • an optional word frame.

I got curious and worked through the design. I did not do the academic or patent search needed to claim that this combination is new, and I am not presenting it as a paper.

Try it

A working build of this design runs at ryanyannelli.com/watermark. Paste any text. The page suggests carrier sites from a substitution lexicon (WordNet 3.1 synsets cross-checked against the Moby thesaurus, filtered by word frequency, then reviewed row by row) and skips names, numbers, negation, and quoted text.

Each sentence becomes its own frame, and the generated template stays editable, which is the approval step above. The page then issues a distinct copy to each source and reads a pasted sentence back to the copy it came from. The lexicon, the codebook, the HMAC derivation, and the detector run in your browser. The master key stays on the page.

Sources

  1. Anthropic. How Claude’s text watermark works. August 14, 2026.
  2. Anthropic. How Claude marks AI-generated content. Help Center.
  3. European Commission. Code of Practice on Transparency of AI-generated Content.
  4. Claude Users Cancel Subscriptions, Citing Anthropic’s New AI Watermark. Business Insider, August 2026.
  5. Yanming Li, Cédric Eichler, Nicolas Anciaux, Alexandra Bensamoun, Lorena González Manzano, and Seifeddine Ghozzi. Data Provenance Auditing of Fine-Tuned Large Language Models with a Text-Preserving Technique. arXiv:2510.09655, 2025.
  6. World Wide Web Consortium. Clipboard API and events.
  7. National Institute of Standards and Technology. FIPS 198-1: The Keyed-Hash Message Authentication Code (HMAC). July 2008.
  8. Eyal German, Sagiv Antebi, Edan Habler, Asaf Shabtai, and Yuval Elovici. LexiMark: Robust Watermarking via Lexical Substitutions to Enhance Membership Verification of an LLM’s Textual Training Data. arXiv:2506.14474, 2025.
  9. Jie Ren, Han Xu, Yiding Liu, Yingqian Cui, Shuaiqiang Wang, Dawei Yin, and Jiliang Tang. A Robust Semantics-based Watermark for Large Language Model against Paraphrasing. In Findings of the Association for Computational Linguistics: NAACL 2024, pages 613-625.