Skip to content
Security
Skill

/remove-ai-marks

Remove multi-vendor AI provenance marks: invisible Unicode (Layer A), statistical text watermarks via rewrite (Layer B, always offer), and C2PA/EXIF/XMP/container metadata on PNG/JPEG/WebP/SVG/PDF/DOCX/ODT/HTML/MD/TEX. Covers Claude, Gemini/SynthID-class, OpenAI provenance, and

BOOST
From plugin
watermarks-remover
25k2 skills1 hook
Install
$ npx -y skills add guillaumemeyer/watermarks-remover --skill remove-ai-marks --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/remove-ai-marks

Context preview

The summary Claude sees to decide when to auto-load this skill.

Remove multi-vendor AI provenance marks: invisible Unicode (Layer A), statistical text watermarks via rewrite (Layer B, always offer), and C2PA/EXIF/XMP/container metadata on PNG/JPEG/WebP/SVG/PDF/DOCX/ODT/HTML/MD/TEX. Covers Claude, Gemini/SynthID-class, OpenAI provenance, and

SKILL.md

remove-ai-marks.SKILL.md
name: remove-ai-marks
description: >
  Remove multi-vendor AI provenance marks: invisible Unicode (Layer A), statistical
  text watermarks via rewrite (Layer B, always offer), and C2PA/EXIF/XMP/container
  metadata on PNG/JPEG/WebP/SVG/PDF/DOCX/ODT/HTML/MD/TEX. Covers Claude, Gemini/SynthID-class,
  OpenAI provenance, and open-LLM sampling marks. Use when the user asks to strip
  watermarks, remove C2PA/Content Credentials, clean AI metadata, remove invisible
  Unicode, anti-detect clean AI output, or runs /remove-ai-marks (aliases:
  /remove-claude-marks).

Remove AI marks

Multi-vendor anti-detection hygiene for **text** (Unicode + statistical rewrite) and **files** (C2PA / AI metadata across common containers).

Read if needed:

  • `references/mark-classes.md` — Unicode / sampling / C2PA / containers
  • `references/vendor-notes.md` — Claude, Gemini/SynthID, OpenAI, open-LLM
  • `references/removal-matrix.md` — which layer when
  • `references/ethics.md` — intended use
  • `references/how-claude-marks.md` — Anthropic-specific detail
  • `references/markdiffusion.md` — optional MarkDiffusion image harness (schemes, honesty caveats)

This skill is a **thin client**. All deterministic cleaning machinery runs in a separate HTTP service (this repo's `service/`), so the agent host needs no Python, venvs, or cleaning tools. Call the service with `curl`; never run cleaning scripts directly.

Service access

Base URL comes from `WATERMARKS_SERVICE_URL`, default `http://127.0.0.1:8765`:

WM="${WATERMARKS_SERVICE_URL:-http://127.0.0.1:8765}"

The service is started either by the operator (`docker compose up -d`, or a published GHCR image) or locally (`make serve`). **Always check it first**, and stop with a clear message if it is unreachable — never fall back to local cleaning:

AUTH_HEADER=()
if [ -n "$WATERMARKS_SERVER_API_KEY" ]; then
  AUTH_HEADER=(-H "Authorization: Bearer $WATERMARKS_SERVER_API_KEY")
fi
curl -sf "${AUTH_HEADER[@]}" "$WM/health"
# {"ok": true, "version": "..."}

If `WATERMARKS_SERVER_API_KEY` is set on the service, every request (including the health check and capabilities) needs `-H "Authorization: Bearer $WATERMARKS_SERVER_API_KEY"`. The default URL is loopback; when the service runs on another host, set `WATERMARKS_SERVICE_URL` to an `https://` URL so the token is not sent in cleartext, and do not add `-L` (a redirect could forward the token to another host).

Capabilities

curl -s "${AUTH_HEADER[@]}" "$WM/capabilities"

Reports which optional tools are available server-side (`c2patool`, `exiftool`, `qpdf`, `ghostscript`), scorers present (`scorers.stylometry`, `scorers.synthid`, `scorers.synthid_http`), text-watermark detectors (`text_detectors.markllm`, `text_detectors.claude-text`), and which heavy backends are configured (`pixel_backends.ctrlregen`, `pixel_backends.diffusion`, `harnesses.markllm`). **Drive your advice from this**: only recommend pixel removal / SynthID scoring / vendor detection when the service reports the backend present.

HTTP API (curl)

Payloads are JSON with the file as **base64**. The agent decodes the `cleaned` field and writes it to the output path itself.

| 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"}` |

`/clean` and `/inspect` route by the uploaded `name` extension plus the bytes; unrecognized formats answer `kind: "unknown"` (`/inspect`) or 400 (`/clean`). When writing a temp file for pasted text, keep a known extension (`.txt` / `.md`) in the `name` you send.

The machine-readable contract lives at `$WM/openapi.json` — plug it into any OpenAPI tooling (client generators, Swagger UI, editors) instead of hand-rolling clients.

`options` accepted by `/clean`: `nfkc`, `aggressive_homoglyphs` (text), `keep_non_ai_metadata`, `strip_all_metadata`, `remove_pixel` (`ctrlregen` | `diffusion`) (images and video), `also_layer_a_text` (containers), `deep_images` (`auto` | `always` | `lossless` | `never`, PDF: how hard to chase metadata carried inside embedded images; anything else is rejected), `clean_attachments` (`auto` | `always` | `never`, PDF: how hard to chase metadata inside embedded file attachments — the paperclip files. `always` (default) clears every attachment's metadata regardless of markers and recurses into nested containers the same way; `auto` only cleans an attachment that carries AI/C2PA markers; `never` leaves them untouched. Needs `qpdf`. Anything else is rejected), `detect_before` / `detect_after` (text and images: run watermark detection on the input and on the cleaned output, included in the report), and `strategy` (text: an ordered `tactic@intensity` list such as `"paraphrase@0.8,mlm@0.2"` that runs the Layer B rewrite after Layer A; when omitted the default from `config/clean_strategy.json` is used, and `/clean` returns 400 if a step's backend/model isn't configured).

**Inspect first** (decide, don't guess):

curl -s -X POST "${AUTH_HEADER[@]}" "$WM/inspect" -H 'Content-Type: application/json' \
  -d "{\"file\": \"$(base64 < notes.md | tr -d '\n')\", \"name\": \"notes.md\"}"

**Clean** (text / image / container are auto-detected by name + bytes):

curl -s -X POST "${AUTH_HEADER[@]}" "$WM/clean" -H 'Content-Type: application/json' \
  -d "{\"file\": \"$(base64 < notes.md | tr -d '\n')\", \"name\": \"notes.md\"}"

Decode the returned `cleaned` base64 into the output file (`*.cleaned.*` by default unless the

Read more
Ships withwatermarks-remover

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.

Get the whole plugin
Stats
25,007
Stars
2,864
Forks
Active
Maintenance
Python
Language
MIT
License
11h ago
Last commit
2mo ago
Created
3h ago
Added

Repo: guillaumemeyer/watermarks-remover

Other skills on watermarks-remover.