dewatermark
Remove Unicode text watermarks and mitigate published statistical LLM watermarks with quality-constrained rewriting.
It does not claim to remove an Anthropic/Claude-specific watermark. Anthropic has not publicly documented a deployed Claude text-watermark scheme or detector. Unknown vendor systems, provider-side retrieval, and semantic provenance may remain detectable after rewriting.
Install
Install the latest development version directly from GitHub:
pip install "git+https://github.com/cyzanfar/dewatermark.git"
For published releases:
pip install dewatermark
pip install "dewatermark[local]" # local self-information scorer + BIRA
pip install "dewatermark[eval]" # evaluation models
Quickstart
import dewatermark
# Safe by default: removes unambiguous covert controls without folding valid
# emoji shaping, RTL controls, compatibility characters, or confusables.
clean = dewatermark.sanitize("he\u200bllo")
forensics = dewatermark.analyze(text)
result = dewatermark.remove(text, mode="auto")
print(result.cleaned_text)
print(result.report)
# Explicitly lossy Latin-text canonicalization:
canonical = dewatermark.sanitize(text, profile="aggressive")
Model downloads are also opt-in. Preload one explicitly with
dewatermark download-model, or set allow_model_download=True when constructing
a configuration.
Command line and agents
The CLI never prompts and supports stdin, stable JSON, JSONL batches, dry runs, and capability discovery:
printf 'he\u200bllo' | dewatermark sanitize --format json
dewatermark capabilities
dewatermark remove --mode auto --dry-run --format json
dewatermark remove --mode sanitize --format jsonl < requests.jsonl
dewatermark schema
Python callers can inspect dewatermark.capabilities() and
dewatermark.plan(mode) without network access, model loading, or downloads.
remove_many() preserves batch order and aremove() integrates with async agent
runtimes. Results carry a versioned JSON schema with explicit status, backend,
fallback, warnings, and stage details.
Remote processing is deny-by-default because source text may be sensitive:
from dewatermark import Dewatermark, DewatermarkConfig
dw = Dewatermark(DewatermarkConfig(
lm_backend="fireworks",
fireworks_api_key="fw-...",
allow_remote_processing=True,
))
result = dw.remove(text, mode="bias_inversion", beta=6.0)
Removal modes
sanitize: Unicode cleanup only.safeis the default profile;aggressiveenables lossy NFKC/confusable folding.bias_inversion: BIRA-style surprisal proxy set, negative logit bias, adaptive bias backoff/restarts, and deterministic quality gates.sira: proportional self-information masking, reference rewrite, targeted infill, and quality-gated acceptance.paraphrase/full: structural and cross-lingual rewriting baselines.adversarial: best-of-N SIRA candidates. Its surprisal score is a weak selection heuristic, not proof that a watermark was removed.auto: BIRA first, quality/failure-aware fallback to paraphrasing, then sanitize-only when no rewrite backend is usable.
Long inputs are split at paragraph/sentence boundaries and reconstructed exactly at chunk boundaries. Local models use CUDA/MPS automatically when available. A 7B–14B instruction model is recommended for rewrite quality; the 0.5B default demonstrates the mechanism but should not be expected to reproduce published attack results.
Safety and quality behavior
Every generated candidate is rejected if it is empty, truncated/expanded past configured bounds, repetitive, contains mask placeholders, or drops numbers, URLs, email addresses, or quoted strings. These deterministic checks catch catastrophic failures but do not prove semantic equivalence; production users should add NLI, claim-QA, and human review for consequential content.
Important configuration:
| Environment variable | Default | Purpose |
|---|---|---|
DEWATERMARK_LM_BACKEND |
auto |
local, fireworks, or automatic selection |
DEWATERMARK_LOCAL_LM |
Qwen/Qwen2.5-0.5B-Instruct |
Local scorer/rewriter |
DEWATERMARK_ALLOW_MODEL_DOWNLOAD |
false |
Explicit model acquisition consent |
DEWATERMARK_FIREWORKS_AI_API_KEY |
— | Fireworks scorer/rewriter |
DEWATERMARK_LLM_API_KEY |
— | OpenAI-compatible paraphrase/SIRA endpoint |
DEWATERMARK_ALLOW_REMOTE_PROCESSING |
false |
Consent to transmit source text |
DEWATERMARK_SANITIZE_PROFILE |
safe |
safe or explicitly lossy aggressive |
DEWATERMARK_MAX_CHUNK_CHARS |
12000 |
Rewrite chunk bound |
DEWATERMARK_MAX_INPUT_CHARS |
1000000 |
Per-request input bound |
DEWATERMARK_MAX_REMOTE_CALLS |
16 |
Per-operation remote-call budget |
DEWATERMARK_MAX_OUTPUT_TOKENS |
2048 |
Generated-token budget |
DEWATERMARK_MAX_CONCURRENCY |
4 |
Batch worker bound |
DEWATERMARK_REQUEST_TIMEOUT |
120 |
Per-request timeout ceiling |
DEWATERMARK_QUALITY_MIN_LENGTH_RATIO |
0.70 |
Candidate acceptance bound |
DEWATERMARK_QUALITY_MAX_LENGTH_RATIO |
1.35 |
Candidate acceptance bound |
Unprefixed v0.2 names remain compatibility aliases during the 0.3 release line.
Evaluation
The harness uses matched transformed nulls, refuses to estimate an empirical FPR
without at least ceil(1/FPR) null samples, provides confidence intervals, and
supports length sweeps. Independent official implementations can be connected
through the JSON command-adapter contract in eval/adapters.py.
dewatermark-eval --skip-statistical --output results.md
# Expensive research run. 1e-5 FPR is deliberately reported as not estimable
# unless at least 100,000 matched nulls are supplied.
DEWATERMARK_ALLOW_REMOTE_PROCESSING=true dewatermark-eval \
--samples 100 --null-samples 1000 \
--lengths 100,250,500,1000,2000 \
--modes bias_inversion,sira,full \
--model-revision MODEL_COMMIT --allow-model-download \
--json-output results.json --checkpoint progress.jsonl
The checked-in eval/RESULTS.md explains why no statistically valid v0.2
efficacy result is currently claimed. The runner uses strict failure handling by
default and records configuration, package versions, hardware, prompt hashes,
checkpoints, and machine-readable results.
Extending and contributing
Third-party scorers and rewriters can implement the structural interfaces in
dewatermark.protocols, register in-process, or publish through the
dewatermark.providers entry-point group. See extension documentation,
architecture, and contributor guide.
Scope limitations
- No public Claude-specific scheme is available to target or verify.
- Retrieval-based provenance cannot be removed from text.
- Token surprisal does not reliably identify semantic, post-processing, cryptographic, learned, or undisclosed watermarks.
- Detector success must be stated for a named scheme, key/configuration, operating threshold, text length, and quality constraint—not as universal “watermark removal.”
License
The package code is MIT-licensed; see LICENSE. The generated confusables table incorporates Unicode data distributed under the Unicode License v3.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dewatermark-0.3.0.tar.gz.
File metadata
- Download URL: dewatermark-0.3.0.tar.gz
- Upload date:
- Size: 75.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f7b42f3a17187561655259956dc9c20c2bba47d701d175be2a5ae69ac6d72769
|
|
| MD5 |
33df5be2da6b4e8915da7c5f780ae992
|
|
| BLAKE2b-256 |
04fbbb2be907c47cdde3caa9418a0a729d96a1353f26b3f0b17852cac2983c4e
|
Provenance
The following attestation bundles were made for dewatermark-0.3.0.tar.gz:
Publisher:
publish.yml on cyzanfar/dewatermark
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dewatermark-0.3.0.tar.gz -
Subject digest:
f7b42f3a17187561655259956dc9c20c2bba47d701d175be2a5ae69ac6d72769 - Sigstore transparency entry: 2495155837
- Sigstore integration time:
-
Permalink:
cyzanfar/dewatermark@5a4e8692e43ec0bf337300a25e2a18593eff286a -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/cyzanfar
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5a4e8692e43ec0bf337300a25e2a18593eff286a -
Trigger Event:
release
-
Statement type:
File details
Details for the file dewatermark-0.3.0-py3-none-any.whl.
File metadata
- Download URL: dewatermark-0.3.0-py3-none-any.whl
- Upload date:
- Size: 76.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bd6b5848b79024ec9fe59309ae8be38f23b8ac3491097e4bb2b671c2e7493ab2
|
|
| MD5 |
e1577685a730b462ec0c45671f88e6b6
|
|
| BLAKE2b-256 |
6cc965b0ddce96cd194f727255fe71ff511429068feae69cc17012e7d339c51d
|
Provenance
The following attestation bundles were made for dewatermark-0.3.0-py3-none-any.whl:
Publisher:
publish.yml on cyzanfar/dewatermark
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dewatermark-0.3.0-py3-none-any.whl -
Subject digest:
bd6b5848b79024ec9fe59309ae8be38f23b8ac3491097e4bb2b671c2e7493ab2 - Sigstore transparency entry: 2495155838
- Sigstore integration time:
-
Permalink:
cyzanfar/dewatermark@5a4e8692e43ec0bf337300a25e2a18593eff286a -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/cyzanfar
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5a4e8692e43ec0bf337300a25e2a18593eff286a -
Trigger Event:
release
-
Statement type: