by Leutenegger
Remove multi-vendor AI provenance traces: Unicode text sanitization, statistical rewriting techniques, and C2PA/metadata stripping from PNG/JPEG/SVG/PDF/DOCX/HTML/MD files
# Add to your Claude Code skills
git clone https://github.com/Leutenegger/watermarks-removerGuides for using cli tools skills like watermarks-remover.
watermarks-remover is an open-source cli tools skill for AI coding assistants such as Claude Code, Codex CLI, and ChatGPT, built by Leutenegger. Remove multi-vendor AI provenance traces: Unicode text sanitization, statistical rewriting techniques, and C2PA/metadata stripping from PNG/JPEG/SVG/PDF/DOCX/HTML/MD files. It has 498 GitHub stars.
watermarks-remover's catalog security scan is still queued. You can run an instant dependency and prompt-injection check now with the "Scan for vulnerabilities" button above.
Clone the repository with "git clone https://github.com/Leutenegger/watermarks-remover" and add it to your Claude Code skills directory (see the Installation section above).
watermarks-remover is primarily written in Python. It is open-source under Leutenegger on GitHub, so you can review or fork the full source.
Yes. SkillsLLM lists many other CLI Tools skills you can browse and compare side by side. Open the CLI Tools category from the badge at the top of this page, or use the Related Skills and comparison links further down to weigh watermarks-remover against similar tools.
No comments yet. Be the first to share your thoughts!
Top skills in this category by stars
Unlocks once the catalog security scan passes (runs nightly).
⚠️ Third-Party Software Notice
This skill is third-party open-source software developed and hosted independently on GitHub. SkillsLLM is an informational directory and does not control or maintain the underlying repository.
Any security checks, ratings, or warnings displayed by SkillsLLM are automated and limited in scope. They do not constitute a security certification or guarantee that the software is safe, error-free, or free from malicious code, vulnerabilities, compromised dependencies, or prompt-injection risks.
Review the source code, permissions, dependencies, and configuration before installing or running any third-party skill. Use is at your own risk. To the maximum extent permitted by applicable law, SkillsLLM is not liable for losses arising from third-party software.
The deep catalog scan for this skill is still queued. Run an instant dependency check now instead.
_ _ _ ____ ___ ____ ____ _ _ ____ ____ _ _ ____ ____ ____ _ _ ____ _ _ ____ ____
| | | |__| | |___ |__/ |\/| |__| |__/ |_/ [__ __ |__/ |___ |\/| | | | | |___ |__/
|_|_| | | | |___ | \ | | | | | \ | \_ ___] | \ |___ | | |__| \/ |___ | \
Agent skill + stdlib Python service to strip multi-vendor AI provenance marks from text and files — for privacy and hygiene on content you own. The skill is a thin client: it drives the machinery over HTTP, so the agent host needs no Python.
| Layer | Target | How |
|---|---|---|
| A | Invisible Unicode, exotic spaces, bidi, tag chars | Deterministic Python scripts |
| B | Statistical (token-sampling) text watermarks | Agent rewrite + optional rewrite_text.py hook |
| Files | C2PA / EXIF / XMP / doc props | PNG, JPEG, WebP, AVIF, HEIC, BMP, GIF, TIFF, SVG, PDF, DOCX, XLSX, PPTX, EPUB, ODT, HTML, Markdown, MP4/MOV/M4A/M4V, WAV, MP3 |
Vendors / ecosystems (class-level): Claude, Gemini / SynthID-Text, OpenAI provenance surfaces, open-LLM Kirchenbauer-style marks.
Latest release: v0.5.0
Skill path: skills/remove-ai-marks/
Service path: service/
(migration: formerly remove-claude-marks; slash alias /remove-claude-marks still documented)
The skill ships no code — it calls the service over HTTP. Install the skill (markdown only) and start the service, then set WATERMARKS_SERVICE_URL if it is not http://127.0.0.1:8765.
# Grok Build / project-local
mkdir -p .grok/skills
ln -sfn "$(pwd)/skills/remove-ai-marks" .grok/skills/remove-ai-marks
# User-global Grok
mkdir -p ~/.grok/skills
ln -sfn "$(pwd)/skills/remove-ai-marks" ~/.grok/skills/remove-ai-marks
Invoke with /remove-ai-marks or ask to “strip AI watermarks / C2PA / Claude marks / SynthID-class text.”
skills/clean-user-facing-text/ is a
self-contained Cursor skill for authorized manuscripts, documentation, and web
copy. It excludes image, C2PA, service, and external-model tooling.
Install it into ~/.cursor/skills/clean-user-facing-text:
python3 install_skill.py
On Windows, use py install_skill.py. The install-skill.sh wrapper is
provided for macOS/Linux shells. Existing installations are preserved unless
you pass --force; replacement is staged first and the previous install is
kept as a uniquely named backup.
Skill invocation is model-selected. Projects that explicitly adopt this workflow can also copy the optional rule:
mkdir -p /path/to/project/.cursor/rules
cp integrations/cursor/clean-user-facing-text.mdc \
/path/to/project/.cursor/rules/clean-user-facing-text.mdc
For all projects, put the same instruction in Cursor User Rules instead. Rules improve consistency but remain model instructions; Cursor does not expose a deterministic pre-send filter for final chat responses.
The fastest path is a local HTTP server (Python 3.10+ stdlib only — no deps, no Docker):
make serve # http://127.0.0.1:8765
# or directly:
python3 service/scripts/server.py --host 127.0.0.1 --port 8765
See docs/windows-autostart.md for auto-starting the service at Windows login without Docker.
For the whole infra (core + optional harness/heavy backends), see Docker / compose below.
Optional system tools (auto-used when present — preinstalled in the core Docker image):
| Tool | Role |
|---|---|
c2patool |
Inspect C2PA manifests |
exiftool |
Residual metadata strip (esp. PDF) |
qpdf |
Structural PDF rebuild — required for a real PDF strip (see below) |
Core scripts need Python 3.10+ stdlib only. Layer B model calls are optional.
SCRIPTS=service/scripts
# Unified inspect / clean
python3 "$SCRIPTS/inspect_file.py" draft.md
python3 "$SCRIPTS/clean_file.py" draft.md -o draft.cleaned.md
python3 "$SCRIPTS/clean_file.py" photo.png -o photo.cleaned.png
python3 "$SCRIPTS/clean_file.py" notes.docx -o notes.cleaned.docx
# Text Layer A
python3 "$SCRIPTS/inspect_text.py" draft.md
python3 "$SCRIPTS/clean_text.py" draft.md -o draft.cleaned.md --stats
# Layer B rewrite hook (default: print prompt only — no model required)
python3 "$SCRIPTS/rewrite_text.py" draft.md --backend print-prompt --strength paraphrase
# Optional local Ollama (loopback only by default — remote endpoints require
# WATERMARKS_REWRITE_ALLOW_REMOTE=1 or --allow-remote):
# WATERMARKS_REWRITE_BACKEND=ollama WATERMARKS_REWRITE_MODEL=llama3.2 \
# python3 "$SCRIPTS/rewrite_text.py" draft.md -o draft.rewritten.md
# API keys are read from WATERMARKS_REWRITE_API_KEY only (never argv).
# Images
python3 "$SCRIPTS/inspect_image.py" shot.png
python3 "$SCRIPTS/clean_image.py" shot.png -o shot.cleaned.png
inspect_text.py, clean_text.py and rewrite_text.py operate on text. Pointed
at a .docx, .pdf or image they used to decode the compressed bytes and report
whatever codepoints fell out — noise that tracks the compression, not the
content — and clean_text.py then wrote those mangled bytes back, destroying the
file. They now refuse binary input and name the tool that handles it:
python3 "$SCRIPTS/inspect_text.py" report.docx
# refusing to treat report.docx as text: it looks like a ZIP container (DOCX, ODT, …).
# Use inspect_file.py / clean_file.py, which route by format,
# or pass --force-text to scan the raw bytes anyway.
Detection is by magic number plus a control-byte ratio, so text in encodings
other than UTF-8 keeps working. --force-text overrides it everywhere.
classify() labels bytes that match no supported text, image or container
format as unknown — it no longer falls back to "text". In auto mode
clean_file.py refuses such files (exit 2, no output written) instead of
decoding them as UTF-8 and writing back mangled bytes; --as text or
--force-text are the explicit opt-ins. inspect_file.py reports the file
as unknown (exit 0), and the HTTP service answers /inspect with
kind: "unknown" but rejects /clean of unknown formats (400 — send a
filename with a known extension, e.g. notes.txt).
The same machinery runs as a stdlib HTTP service (service/scripts/server.py) — the interface the skill uses and the way any web app can integrate without vendoring:
| Method | Path | Body | Returns |
|---|---|---|---|
| GET | /health |
— | {"ok": true, "version": ...} |
| GET | /capabilities |
— | optional tools / backends present |
| GET | /openapi.json |
— | dynamically generated OpenAPI 3.0.3 spec |
| POST | /inspect |
{"file": "<base64>", "name": "notes.md"} |
{"ok", "kind", "suspicious", "report"} |
| POST | /detect |
{"file": "<base64>", "name": "notes.txt"} |
{"ok", "kind", "detections": [...]} |
| POST | /clean |
{"file": "<base64>", "name": "notes.md", "options": {...}} |
{"ok", "kind", "cleaned": "<base64>", "report"} |
| POST | /inspect/batch |
{"files": [{"file": "<base64>", "name": "notes.md"}, ...]} |
{"ok", "results": [{"name", "ok", "kind", "suspicious", "report"}, ...]} |
| POST | /clean/batch |
{"files": [{"file": "<base64>", "name": "notes.md", "options": {...}}, ...]} |
{"ok", "results": [{"name", "ok", "kind", "cleaned": "<base64>", "report"}, ...]} |
Batch endpoints loop the same per-file pipeline as /inspect and /clean, capped at WATERMARKS_MAX_BATCH_FILES files per request (default 50). A malformed entry (bad base64, unknown option, unrecognized format) surfaces as that entry's "ok": false with an "error" string — it never aborts the rest of the batch.
WM="http://127.0.0.1:8765"
curl -s "$WM/health" # {"ok": true, "version": "..."}
curl -s "$WM/openapi.json" # machine-readable OpenAPI 3.0.3 contract
curl -s -X POST "$WM/clean" -H 'Content-Type: application/json' \
-d "{\"file\": \"$(base64 < notes.md | tr -d '\n')\", \"name\": \"notes.md\"}"
The service routes by filename extension then magic bytes, so text / image / container are auto-detected. Set WATERMARKS_SERVER_API_KEY to require Authorization: Bearer <key> on every request. Loopback-only bind by default (--host to override); intended for a trusted network.
/detect and detect_before / detect_after)Detection is a separate step from cleaning — the service never calls vendor APIs unless you ask it to:
POST /detect runs the configured watermark detectors on a file.
Text → vendor detectors + stylometry; image → SynthID pixel score./inspect accepts an opt-in "detect": true flag that appends
detector results to the text report (and can flip suspicious)./clean accepts "detect_before" / "detect_after" options to
score the input and th