{"slug": "watermarks-remover-strip-multi-vendor-ai-provenance-marks", "title": "Watermarks-remover: Strip multi-vendor AI provenance marks", "summary": "Watermarks-remover v0.3.2, an open-source agent skill and Python script suite by Guillaume Meyer, strips multi-vendor AI provenance marks from text and files, including Claude, Gemini/SynthID-Text, OpenAI, and open-LLM Kirchenbauer-style marks, targeting invisible Unicode, statistical watermarks, and C2PA/EXIF/XMP metadata. The tool, requiring Python 3.10+ and optional external tools like c2patool and exiftool, offers deterministic scripts for Layer A text cleaning, an optional Layer B rewrite hook, and image cleaning with optional pixel-domain SynthID scoring via an external reverse-SynthID checkout.", "body_md": "\n\n```\n_ _ _ ____ ___ ____ ____ _  _ ____ ____ _  _ ____    ____ ____ _  _ ____ _  _ ____ ____\n| | | |__|  |  |___ |__/ |\\/| |__| |__/ |_/  [__  __ |__/ |___ |\\/| |  | |  | |___ |__/\n|_|_| |  |  |  |___ |  \\ |  | |  | |  \\ | \\_ ___]    |  \\ |___ |  | |__|  \\/  |___ |  \\\n```\n\nAgent skill + stdlib Python scripts to strip **multi-vendor AI provenance marks** from text and files — for privacy and hygiene on content **you own**.\n\n| Layer | Target | How |\n|---|---|---|\nA |\nInvisible Unicode, exotic spaces, bidi, tag chars | Deterministic Python scripts |\nB |\nStatistical (token-sampling) text watermarks | Agent rewrite + optional `rewrite_text.py` hook |\nFiles |\nC2PA / EXIF / XMP / doc props | PNG, JPEG, SVG, PDF, DOCX, ODT, HTML, Markdown |\n\nVendors / ecosystems (class-level): **Claude**, **Gemini / SynthID-Text**, **OpenAI** provenance surfaces, **open-LLM** Kirchenbauer-style marks.\n\n**Latest release:** [v0.3.2](https://github.com/guillaumemeyer/watermarks-remover/releases/tag/v0.3.2)\n\nSkill path: `skills/remove-ai-marks/`\n\n(migration: formerly `remove-claude-marks`\n\n; slash alias `/remove-claude-marks`\n\nstill documented)\n\n```\n# Grok Build / project-local\nmkdir -p .grok/skills\nln -sfn \"$(pwd)/skills/remove-ai-marks\" .grok/skills/remove-ai-marks\n\n# User-global Grok\nmkdir -p ~/.grok/skills\nln -sfn \"$(pwd)/skills/remove-ai-marks\" ~/.grok/skills/remove-ai-marks\n```\n\nInvoke with `/remove-ai-marks`\n\nor ask to “strip AI watermarks / C2PA / Claude marks / SynthID-class text.”\n\nOptional system tools (auto-used when present):\n\n| Tool | Role |\n|---|---|\n`c2patool` |\n\n`exiftool`\n\nCore scripts need **Python 3.10+** stdlib only. Layer B model calls are optional.\n\n```\nSCRIPTS=skills/remove-ai-marks/scripts\n\n# Unified inspect / clean\npython3 \"$SCRIPTS/inspect_file.py\" draft.md\npython3 \"$SCRIPTS/clean_file.py\" draft.md -o draft.cleaned.md\npython3 \"$SCRIPTS/clean_file.py\" photo.png -o photo.cleaned.png\npython3 \"$SCRIPTS/clean_file.py\" notes.docx -o notes.cleaned.docx\n\n# Text Layer A\npython3 \"$SCRIPTS/inspect_text.py\" draft.md\npython3 \"$SCRIPTS/clean_text.py\" draft.md -o draft.cleaned.md --stats\n\n# Layer B rewrite hook (default: print prompt only — no model required)\npython3 \"$SCRIPTS/rewrite_text.py\" draft.md --backend print-prompt --strength paraphrase\n# Optional local Ollama (loopback only by default — remote endpoints require\n# WATERMARKS_REWRITE_ALLOW_REMOTE=1 or --allow-remote):\n# WATERMARKS_REWRITE_BACKEND=ollama WATERMARKS_REWRITE_MODEL=llama3.2 \\\n#   python3 \"$SCRIPTS/rewrite_text.py\" draft.md -o draft.rewritten.md\n# API keys are read from WATERMARKS_REWRITE_API_KEY only (never argv).\n\n# Images\npython3 \"$SCRIPTS/inspect_image.py\" shot.png\npython3 \"$SCRIPTS/clean_image.py\" shot.png -o shot.cleaned.png\n```\n\n`inspect_image.py`\n\nand `clean_image.py`\n\ncan report a pixel-domain SynthID\nconfidence score when an external checkout of\n[ aloshdenny/reverse-SynthID](https://github.com/aloshdenny/reverse-SynthID)\nis available. The scorer is\n\n**not bundled**: it is loaded at runtime from your checkout, and its code remains under the upstream project's non-commercial Research License.\n\n```\nSCRIPTS=skills/remove-ai-marks/scripts\n\n# Clones upstream, creates a venv, and installs scorer-only dependencies.\n\"$SCRIPTS/setup_synthid.sh\"\n\n# Score an image (default checkout: ~/reverse-SynthID).\nREVERSE_SYNTHID_DIR=~/reverse-SynthID \\\n~/reverse-SynthID/.venv/bin/python \"$SCRIPTS/score_synthid.py\" shot.png\n\n# Or surface the score from inspect / clean (same venv Python).\nREVERSE_SYNTHID_DIR=~/reverse-SynthID \\\n~/reverse-SynthID/.venv/bin/python \"$SCRIPTS/inspect_image.py\" shot.png\n```\n\n`setup_synthid.sh`\n\naccepts `--dir PATH`\n\n, `--ref REF`\n\n, and `--full`\n\n(install the\nfull upstream `requirements.txt`\n\n, which adds `torch`\n\n/`diffusers`\n\nfor the\nupstream VAE bypass this project does not use).\n\n```\nmake docker-synthid-build\n# Run unprivileged and with a read-only rootfs; the scorer only needs to read\n# /data and write to stdout/tmp.\ndocker run --rm \\\n  --user \"$(id -u):$(id -g)\" \\\n  --read-only --tmpfs /tmp \\\n  -v \"$(pwd):/data\" \\\n  watermarks-remover-synthid-scorer /data/shot.png\n```\n\nThe image is built locally from the upstream source at build time. It is not published, so it does not redistribute the upstream code.\n\nV4 scoring uses `artifacts/spectral_codebook_v4.npz`\n\nfrom the upstream checkout\n(~220 MB). This is **detection/scoring only** — it does not remove pixel\nwatermarks.\n\n| Channel | Claude | Gemini/SynthID | OpenAI | Open-LLM |\n|---|---|---|---|---|\n| Unicode / edit-based text | Layer A | Layer A | Layer A | Layer A |\n| Statistical sampling text | Layer B best-effort | Layer B best-effort | Layer B if present | Layer B best-effort |\n| C2PA / file metadata | Yes (listed formats) | Yes when present | Yes when present | Yes when present |\n| Pixel image marks | Out of scope | Optional SynthID score (external); removal out of scope | Out of scope | Out of scope |\n| Training backdoors | Out of scope | Out of scope | Out of scope | Out of scope |\n\nDetails: [ skills/remove-ai-marks/references/vendor-notes.md](/guillaumemeyer/watermarks-remover/blob/main/skills/remove-ai-marks/references/vendor-notes.md),\n\n[.](/guillaumemeyer/watermarks-remover/blob/main/skills/remove-ai-marks/references/mark-classes.md)\n\n`mark-classes.md`\n\nModern LLM watermarks often hide a signal in **which tokens are chosen** (generative / sampling bias), not only in invisible characters. Edit-based schemes inject Unicode or synonym rules. File schemes attach **C2PA** or generator metadata.\n\n**Layer A** removes edit-based Unicode carriers (testable).**Layer B** attacks sampling watermarks via heavy rewrite (best-effort; literature-standard attacks such as paraphrase / back-translation).**File cleaners** strip C2PA/XMP/props from supported containers.\n\nUntil vendors ship public detectors and keys, **no tool can honestly certify** “this fails the official check.” Reports must separate verifiable vs best-effort work.\n\nPrefer a **non-origin** model for Layer B (do not rewrite Claude text with Claude if you are trying to avoid re-stamping).\n\nText watermarks live in **the wording itself**: the signal is spread across token choices, so nearly every sentence carries a little of it. Two consequences follow, and they are why Layer B is honestly described as *best-effort* rather than a magic eraser.\n\n-\n**Removal means rewording, not restructuring.** Shuffling paragraphs, changing headings, or light touch-ups barely move the signal. Stripping a statistical mark requires rewriting a substantial fraction of the text — sentence by sentence, not section by section. -\n**Rewording degrades the copy.** Any rewrite replaces the original word choices with the rewriting model's, which flattens tone, voice, and precision. On production copy (SEO, marketing, client work) that degradation is real and often visible to the people who care most about the writing. It is like taking text from a top-tier model and asking a less capable model to rewrite it from scratch: the result cannot exceed the rewrite model's ceiling.\n\nWhich leads to the honest full-circle question:\n\nIf the plan is to rewrite the text with a cheaper model anyway, why pay for a premium model in the first place? Generating directly with the cheaper model is simpler, cheaper, and produces the same — or better — end result.\n\nLayer B makes sense when you specifically want the premium model's **thinking and drafting** and accept a rewrite pass to satisfy a hygiene or privacy requirement — not as a cheap route to mark-free text.\n\n**When to skip Layer B:**\n\n**Quality matters more than hygiene:** use the lossless path — Layer A Unicode scrub plus the file metadata cleaners — and keep the original prose.**Rewriting anyway:** use a**non-origin** model (rewriting with the origin model can re-stamp the text), and remember residual risk remains — no tool can certify a vendor detector will fail.\n\n| Format | Inspect | Clean |\n|---|---|---|\n| PNG / JPEG | C2PA chunks / APP11, AI XMP hints | Drop metadata segments |\n| SVG | `<metadata>` , XMP |\nStrip blocks |\n| Byte/XMP + optional tools | exiftool preferred; degraded without it |\n|\n| DOCX | docProps / customXml | Scrub props, drop customXml |\n| ODT | meta.xml | Drop generator / AI-ish meta |\n| HTML | meta, JSON-LD, data-ai* | Strip tags/attrs |\n| Markdown | YAML frontmatter AI keys | Drop keys + Layer A body |\n\nPixel-domain watermark **removal** and **C2PA soft binding** (in-content watermark that can re-link a remote Content Credentials manifest after metadata is stripped) remain **out of scope**. Stripping hard-bound C2PA does **not** clear those channels. An optional local SynthID scorer is available for detection only (see above).\n\nThis tool reports **verifiable** removals (Unicode counts, metadata actions) and **best-effort** Layer B rewrites. It cannot certify that vendor detectors will fail.\n\nTo check residual signals yourself (optional, external):\n\n| Channel | What we remove | What may remain | External check (examples) |\n|---|---|---|---|\n| Hard-bound C2PA / EXIF / XMP | Yes | Soft-bound / pixel marks |\n|\n\n[Google SynthID](https://deepmind.google/science/synthid/)/ Vertex detector where offered); optional local[reverse-SynthID](https://github.com/aloshdenny/reverse-SynthID)scorerIndustry two-layer context (C2PA + imperceptible watermark): [Institute of AI PM guide](https://www.institutepm.com/knowledge-hub/ai-content-provenance-watermarking).\n\n| Option | Removes | Notes |\n|---|---|---|\n| Unicode scrub (Layer A) | ZWSP, bidi, tags, exotic spaces, … | Safe default for text |\n| Rewrite (Layer B) | Statistical token marks (best-effort) | Always offered by skill; costs style — see\n|\n\nMatrix: [ skills/remove-ai-marks/references/removal-matrix.md](/guillaumemeyer/watermarks-remover/blob/main/skills/remove-ai-marks/references/removal-matrix.md).\n\nSee [ skills/remove-ai-marks/references/ethics.md](/guillaumemeyer/watermarks-remover/blob/main/skills/remove-ai-marks/references/ethics.md). For privacy and research on\n\n**your** content — not academic fraud or false “human-written” claims.\n\n```\npython3 -m venv .venv && .venv/bin/pip install pytest\n.venv/bin/python -m pytest          # or: make test\nmake smoke                          # quick CLI smoke on fixtures\n```\n\n[v0.3.2](https://github.com/guillaumemeyer/watermarks-remover/releases/tag/v0.3.2) — security hardening (safe writes, HTTP client, CI supply chain)\n\n**Safe, atomic output writes**: every cleaner now writes via temp-file + atomic rename (`safe_write_bytes`\n\n/`safe_write_text`\n\n), refuses symlinked destinations, and creates`.bak`\n\nbackups through the same safe path — pre-placed symlinks (e.g. in`/tmp`\n\nor download dirs) can no longer redirect a clean write onto an arbitrary file: redirects are refused outright, so an API key in the`rewrite_text.py`\n\nHTTP client hardening`Authorization`\n\nheader can never be re-sent to an unvalidated host; non-loopback endpoints are**denied by default**(opt in with`--allow-remote`\n\nor`WATERMARKS_REWRITE_ALLOW_REMOTE=1`\n\n); only http(s) schemes are accepted;`--api-key`\n\nwas removed — keys are env-only via`WATERMARKS_REWRITE_API_KEY`\n\n**Resource caps**: default max input 1 GiB → 256 MiB, new 64 MiB stdin cap, DOCX/ODT zip budget 512 MiB → 128 MiB, and`RLIMIT_AS`\n\n/`RLIMIT_FSIZE`\n\napplied to exiftool/c2patool/SynthID subprocesses (all caps env-overridable)**Supply chain**: CI actions SHA-pinned with`permissions: contents: read`\n\n, pinned dev deps (`requirements-dev.txt`\n\n), a`pip-audit`\n\nstep, and a new CodeQL workflow; the Docker image now runs as an unprivileged user with pip pinned**Scorer deps**: Pillow bumped 10.4.0 → 12.3.0 (24 known CVEs); API usage verified against the pinned upstream commit- Tests: 18 new security regression tests (60 total, all passing)\n\n[v0.3.1](https://github.com/guillaumemeyer/watermarks-remover/releases/tag/v0.3.1) — stronger Layer B statistical-watermark rewrite\n\n`rewrite_text.py`\n\ndefault paraphrase now performs an explicit**word-choice + syntax** attack (clause order, connectors, transition words, sentence boundaries, function words) rather than a generic rewrite- New\n`--strength humanize`\n\n: zero-shot \"write like a human\" pass targeting formulaic AI-style phrasing - New\n`--strength code`\n\n: rewrites comments, docstrings, and string literals, and renames local identifiers while preserving behavior and public API names - Structural pass now emits \"natural, varied human prose\" instead of AI-typical \"clear professional style\"\n- New\n`--temperature`\n\n(default`0.9`\n\n) for both Ollama and OpenAI-compatible backends - New\n`--candidates N`\n\n: generates N rewrites and selects the most lexically diverged (bigram Jaccard distance) with a length-drift guard - Stronger model hygiene: prefer local open-weight models and avoid any known-watermarked vendor, not just the suspected origin\n- Residual-risk reporting now distinguishes short/highly predictable text (lower risk) from long, high-entropy prose (higher risk)\n- Docs updated in\n`SKILL.md`\n\n,`removal-matrix.md`\n\n, and`vendor-notes.md`\n\n; tests cover new prompts, divergence scoring, and candidate selection\n\n[v0.3.0](https://github.com/guillaumemeyer/watermarks-remover/releases/tag/v0.3.0) — optional SynthID pixel scoring\n\n- Optional pixel-domain SynthID scorer via an external\ncheckout (`aloshdenny/reverse-SynthID`\n\n`score_synthid.py`\n\n); surfaced in`inspect_image.py`\n\n/`clean_image.py`\n\nwith`REVERSE_SYNTHID_DIR`\n\nor`--synthid-dir`\n\n`setup_synthid.sh`\n\nbootstrap (scorer-only dependencies;`--full`\n\ninstalls upstream requirements);`Dockerfile.synthid`\n\nplus`make docker-synthid-build`\n\n/`docker-synthid-help`\n\n- Makefile\n`smoke-synthid`\n\nand`bootstrap-synthid`\n\ntargets - Tests for the scorer adapter, CLI unavailable path, JSON parsing, and runtime errors\n- Docs: detection/scoring only (no pixel removal); upstream code is not bundled and remains under its non-commercial Research License\n\n[v0.2.0](https://github.com/guillaumemeyer/watermarks-remover/releases/tag/v0.2.0) — c2patool false-positive fix\n\n`image_meta.py`\n\n:`has_manifest`\n\nno longer flags`Error: No claim found`\n\n/`No JUMBF data found`\n\nas a manifest (operator-precedence bug: the negative markers now veto every positive branch)- New\n`tests/test_c2patool_report.py`\n\n(4 cases: no claim, no JUMBF, genuine manifest, tool absent) - Docs: fixed\n`c2patool`\n\nlinks (repo moved to`contentauth/c2pa-rs`\n\n); added a disclaimer on the quality cost of text-watermark removal\n\n[v0.1.0](https://github.com/guillaumemeyer/watermarks-remover/releases/tag/v0.1.0) — packaging polish + provenance honesty\n\n`Makefile`\n\n(`test`\n\n/`smoke`\n\n/`install-skill`\n\n) and`pytest.ini`\n\n- Fixture samples for Markdown, HTML, SVG; PDF degraded-clean test\n- Docs: industry\n**two-layer** model (hard-bound C2PA vs soft binding / SynthID-media) - README residual-risk table + links to external verify tools\n- Reference: Institute of AI PM C2PA/SynthID guide\n- Soft-binding and pixel/audio/video watermarks explicitly out of scope in skill/matrix/ethics\n\n[v0.0.1](https://github.com/guillaumemeyer/watermarks-remover/releases/tag/v0.0.1) — initial multi-vendor release\n\n- Agent skill\n`remove-ai-marks`\n\n(replaces Claude-only`remove-claude-marks`\n\n) **Layer A:** invisible Unicode / bidi / tag chars / space homoglyphs (`inspect_text`\n\n/`clean_text`\n\n)**Layer B:** rewrite guidance + optional`rewrite_text.py`\n\n(print-prompt, Ollama, OpenAI-compatible)**Files:** C2PA/AI metadata strip for PNG, JPEG, SVG, PDF, DOCX, ODT, HTML, Markdown- Unified\n`inspect_file.py`\n\n/`clean_file.py`\n\n- Multi-vendor docs (Claude, Gemini/SynthID-class, OpenAI, open-LLM)\n- Stdlib-first scripts; optional\n`c2patool`\n\n/`exiftool`\n\nMIT — see [LICENSE](/guillaumemeyer/watermarks-remover/blob/main/LICENSE).\n\n[How Claude marks AI-generated content](https://support.claude.com/en/articles/16266773-how-claude-marks-ai-generated-content)(Anthropic)- Dathathri et al.,\n(SynthID-Text, Nature 2024)*Scalable watermarking for identifying large language model outputs* - Google AI for Developers,\n(Gemini API docs)*SynthID safeguards* [C2PA](https://c2pa.org/)/[c2patool](https://github.com/contentauth/c2pa-rs/tree/main/cli)- Kirchenbauer et al.,\n*A Watermark for Large Language Models* - Zhang et al.,\n(ICML 2024)*Watermarks in the Sand: Impossibility of Strong Watermarking for Generative Models* [google-deepmind/synthid-text](https://github.com/google-deepmind/synthid-text)(research reference; not used for detection here)[aloshdenny/reverse-SynthID](https://github.com/aloshdenny/reverse-SynthID)(research reference)- Institute of AI PM,\n(two-layer industry model: C2PA + imperceptible watermark / soft binding; SB 942 / EU AI Act Art. 50 context)*AI Content Provenance and Watermarking: The PM's Guide to C2PA and SynthID*", "url": "https://wpnews.pro/news/watermarks-remover-strip-multi-vendor-ai-provenance-marks", "canonical_source": "https://github.com/guillaumemeyer/watermarks-remover", "published_at": "2026-08-13 05:21:36+00:00", "updated_at": "2026-08-13 05:40:58.061579+00:00", "lang": "en", "topics": ["ai-tools", "ai-ethics", "ai-policy"], "entities": ["Guillaume Meyer", "Watermarks-remover", "Claude", "Gemini", "SynthID-Text", "OpenAI", "Kirchenbauer", "reverse-SynthID"], "alternates": {"html": "https://wpnews.pro/news/watermarks-remover-strip-multi-vendor-ai-provenance-marks", "markdown": "https://wpnews.pro/news/watermarks-remover-strip-multi-vendor-ai-provenance-marks.md", "text": "https://wpnews.pro/news/watermarks-remover-strip-multi-vendor-ai-provenance-marks.txt", "jsonld": "https://wpnews.pro/news/watermarks-remover-strip-multi-vendor-ai-provenance-marks.jsonld"}}