LLM Text Watermarking: How Claude’s Works, and How to Apply It to Your Open-Source Model
Claude now watermarks every text it generates, and the EU AI Act requires the same of anyone serving a self-hosted open-source model. Here is the mechanism, reproduced on a local model with measured numbers, and what it means if you are the one who has to mark your own output.
On August 11, 2026 Anthropic announced that Claude’s text output carries an invisible watermark, and three days later published a post explaining why. The explanation is long on motivation and short on mechanism, and three wrong readings settled into that gap: that the model was retrained, that the text got worse, that there is a hidden character in the output removable with a regex.
The mechanism is small and sits in a rarely discussed step of how an LLM works. Once that step is clear, the practical consequences follow on their own. Section 5 reproduces the scheme on a 0.5B model, with measured numbers.
The trigger is regulatory: Article 50 of the EU AI Act became enforceable on August 2, 2026 for newly launched systems, and it requires generative AI providers to embed machine-readable marks in their output, text included. Anthropic chose to apply it globally.
Coverage spans the Claude Platform API, claude.ai, Claude Code, Claude Cowork, Claude Tag, and Claude accessed through AWS, Google Cloud and Microsoft Foundry. Models launched from August 2 onward already carry it; earlier ones roll out over the following months.
The method is a version of SynthID-Text, published by Google DeepMind in Nature in 2024. Anthropic’s own description sums up the mechanism: what changes is “the source of the randomness used to pick among words”. The next three sections spell that sentence out.
The mark is applied at this step, so it needs to be clear before the rest.
The prompt becomes token IDs and passes through the model. What comes out is a score for every token in the vocabulary — around 250,000, called logits. A function called softmax turns those scores into probabilities that sum to 100%, and only then does a separate step draw a word, weighted by those probabilities.
That draw is why the same prompt returns different answers on each call. It is deliberate, because at many positions in a sentence there is more than one good answer. After “this meeting could have been an”, both email and voice note work. The model spreads 50% and 30% across them, and the draw decides.
The draw happens after the model. By the time it begins, the weights have finished their work.
The rest of this post depends on one concept: the seed.
Computers do not really draw at random. Every “random” number comes from a pseudorandom generator, which takes an initial value — the seed — and from it produces a sequence that looks random, has all the statistical properties of random, and is entirely determined by that initial value. Two runs with seed 42 produce exactly the same numbers, in the same order.
That is why random.seed(42) shows up at the top of every script that needs to be reproducible. The numbers still behave as random; what is gained is the ability to repeat the experiment.
A fixed seed keeps the draw exactly as random as before, and makes it reproducible for whoever knows the seed.
The watermark is applied at that point. Instead of the seed coming from the system clock, it comes from a computation: Anthropic’s secret key, combined with the last tokens generated, goes through a hash function. Every position in the text gets a different seed, because the preceding context changes with every word. And whoever holds the key can recompute all of them afterwards.
The diagram sums up the idea. Section 4 shows how it is actually implemented, and why in a more elaborate way. But three consequences already follow from it, and they answer the three wrong readings from the opening:
No weight is changed. The mark is applied at a step that runs outside the network. The same weights that ran before the announcement run after it; no retraining was needed.
There is no hidden character to strip. The mark lives in the pattern of the choices — which of several equally plausible words came out at each position. Nothing was added to the text, so there is nothing for a regex to find.
The quality argument holds up. Anthropic states that the mark had no measurable effect on content, creativity or readability, and the mechanism explains why: it only acts where the model was already indifferent. When the question is the capital of Germany, there is no second candidate to promote, and that position goes unmarked. “It makes the text worse” is the weak objection.
The idea in section 3 would be enough to mark: seed the generator with the key, draw normally. Except that checking a text would require rerunning the model — knowing which model produced it, having the original prompt, and paying for the inference, for every text to verify. For text found loose on the internet that is unworkable, and unworkable detection detects nothing.
The draw is therefore replaced with a construction that can be redone without the model: tournament sampling. The diagram shows one full round, and the text follows its numbering.
Steps 0 and 1: the candidates come from the model’s distribution. It is the most important step and the one most often left out of explanations. With M layers, 2^M candidates are drawn from p(next token), weighted and with repeats allowed — in the example, M=2, four candidates, and email came up twice. Memo, at 15%, did not come up this round, and so it cannot win: only what the model drew gets to compete.
Every bit in the diagram comes from a function G. It is the only new piece in the scheme — everything else is ordinary sampling — and so it deserves detail.
G takes four inputs: the secret key, the H preceding tokens, the candidate and the layer number. It concatenates them, runs the result through a cryptographic hash function, and returns the first bit of the digest. In the reference implementation in section 5, it is literally that:
g(candidate, layer) = first bit of SHA-256(key ‖ layer ‖ preceding tokens ‖ candidate)
It is section 3’s seed in operation: the same hash(key, context), except that instead of feeding a number generator it produces the bits of the contest directly. Four properties of the hash make the scheme work:
In the paper, the requirement on G is that its outputs be statistically indistinguishable from independent coin flips to anyone without the key. A keyed hash is the simplest way to get that, and it is what implementations use.
Steps 2 and 3: the knockout. In layer 1 the candidates face off in pairs and the G1 bit decides who advances; ties are a coin flip. In layer 2 it is G2. The last one standing is the generated token.
That order — draw first, rank second — carries the quality argument. A word the model would never choose cannot win the tournament, because it never enters it. The tournament only reorders among draws the model itself made, which is why the scheme is described as non-distortionary: the aggregate output distribution is preserved.
Detection is where the construction earns its keep.
To check a text, every position is run through the same G functions with the same key and the lit bits are counted. The average is the score: in the paper, MS(x) = 1/(T·M) · Σ g(x_t).
Text nobody marked scores 0.5, because each bit is a fair coin. Marked text scores above that, because the winner of each tournament had to carry bit 1 in every layer to get through. In the diagram’s example, 11 of 14 lit bits give 0.79; swapping three words, 7 of 14 give 0.50 — the swaps also wipe the bits of the following positions, because they change their seed. Compare against a threshold and the answer comes out, with no model, no prompt, and no inference cost. That was the tournament’s purpose.
The limits, stated by Anthropic itself:
The scheme has attack literature too: a 2026 paper shows this simple mean is vulnerable to layer inflation, and proposes a Bayesian score in its place.
The most important limitation is the asymmetry: absence of a watermark proves nothing. Another model, an older Claude, a rewrite, a translation through a second model — all of it produces unmarked text. The mark is evidence when present and silence when absent, which is a weaker tool than the coverage suggested.
Because the mark lives in sampling, it does not depend on a frontier model. Any model whose generation loop is under control will do.
The reference implementation, candidomark, is about 150 lines of Python with no dependency beyond torch and transformers, running a 0.5B Qwen2.5 on a Mac. The core:
def g_bit(ctx_ids, layer, token_id, key=KEY):
"""Pseudorandom bit for (token, layer), seeded by the key + context."""
h = hashlib.sha256()
h.update(key)
h.update(struct.pack("<I", layer))
for t in ctx_ids:
h.update(struct.pack("<I", int(t)))
h.update(struct.pack("<I", int(token_id)))
return h.digest()[0] & 1
def tournament(cands, ctx_ids, rng, m):
"""In each layer candidates face off in pairs; the higher bit advances."""
cur = list(cands)
for layer in range(m):
nxt = []
for i in range(0, len(cur), 2):
a, b = cur[i], cur[i + 1]
ga, gb = g_bit(ctx_ids, layer, a), g_bit(ctx_ids, layer, b)
nxt.append(a if ga > gb else b if gb > ga else
(a if rng.random() < 0.5 else b))
cur = nxt
return cur[0]
And the generation loop, with the single line that separates marked from unmarked:
p = top_p_probs(logits, temp, top_p)
if watermark:
cands = rng.choice(len(p), size=2 ** m, p=p) # candidates come from the distribution
nxt = int(tournament(cands, ctx_tail[-hh:], rng, m))
else:
nxt = int(rng.choice(len(p), p=p))
Twelve texts of 160 tokens, each generated twice from the same prompt — once with the tournament, once with ordinary sampling — then scored by the detection function.
Unmarked text scores 0.4973. That is what theory predicts: each bit is a fair coin, and the average of fair coins is one half. Marked text scores 0.6327 — eleven standard deviations above it, with no overlap between the two distributions across the twelve samples.
The editing attack behaves as described. Swapping 5% of the positions drops the score to 0.6062 and the text stays clearly detectable. At 15% it falls to 0.5585, reaching the edge of the noise. At 30%, 0.5240: the average is still above chance, but the distributions now overlap, and with a single text no decision is possible.
M is the depth of the tournament: 2^M candidates, M contests, M bits per position. The per-bit ceiling is 0.75 — in a contest between two independent bits, the winner carries bit 1 in three of the four cases, (1,1), (1,0) and (0,1). The measured 0.63 is that mixed with the positions where the model was certain: there the 2^M draws are nearly all the same word, there is nothing to choose, and the bit sits at 0.5.
More layers only put more bits into the average, leaving that ceiling where it is. With M=8:
| M=4 | M=8 | |
|---|---|---|
| unmarked | 0.4973 ± 0.0121 | 0.5000 ± 0.0089 |
| marked | 0.6327 ± 0.0218 | 0.6225 ± 0.0218 |
| separation | 11.2 σ | 13.7 σ |
The marked mean stayed where it was; the unmarked spread shrank. The better separation comes from a more precise measurement; the mark itself did not get stronger. That is why the reference recommends 20 to 30 layers.
H is how many preceding tokens feed the seed. More context makes the mark more specific and more detectable, and also more brittle: one edit changes the seed of the H positions that follow. The reference uses 5.
The text quality here is that of a 0.5B model, and that has nothing to do with the mark. The comparison that matters is marked against unmarked on the same model, and in it nothing degrades perceptibly.
The result confirms the central point: the watermark asks nothing of the model. It asks for the sampling loop, a key and a hash function. That is why Anthropic switched it on without retraining anything, and why it is reproducible on any model in a few hours.
The announcement was read as news about Anthropic. But Article 50 of the AI Act does not talk about frontier labs: it talks about the provider of a generative AI system — whoever places the system on the EU market under their own name or trademark. Open and self-hosted models are not exempt, and nobody marks the output of Llama, Qwen or a fine-tune on behalf of whoever serves them.
The recommendation depends on each company’s role.
Anyone consuming a model over an API — Claude, GPT, Gemini — receives output already marked by the provider and inherits the compliance. The one check worth making is whether there is a second rewriting step in the pipeline, such as tone adjustment, trimming to a character limit or translation through another model, because that step erases the mark without anyone having decided to. Beyond that, no action: no workstream, no roadmap item.
Anyone serving their own model — self-hosted open source, fine-tuned or not — into the EU market, under their own brand, is the provider. The duty to mark belongs to that company, and today nobody in the path is discharging it. The formal recommendation:
That is the work we do at Planorama with teams serving their own model: choosing the insertion point in the inference server, standing up the verification service, and producing the audit evidence, in weeks, without touching the weights. It starts with the cheapest question: how much of the company’s text output today would go out with no mark at all? For that answer in a concrete case, get in touch.
One caveat: the above is an engineering reading of the regulation; it does not replace legal advice. Whether a company is the provider in its concrete case is a question for its counsel. The engineering question — being able to mark once the answer is yes — is the one that can be settled first.
Thiago is Planorama’s Director of AI, a university professor and practitioner with over a decade of applied experience across computer vision, traditional machine learning, and generative AI, along with the data structures those systems rest on, vector and graph storage among them. Most of that work has been on the engineering side of requirements delivery, proving out whether AI can actually solve a given problem rather than assuming it can. He writes about the mechanisms underneath these systems, often with the code and the actual measurements from his own team’s evaluation.