Skip to main content
AI tutorials

Remove AI Provenance Marks from Your Own Text and Files

This tutorial explains how to install and run watermarks-remover, a Python service and agent skill for inspecting and removing Unicode carriers, document metadata, and supported provenance marks from content you own or are authorized to process.

Remove AI Provenance Marks from Your Own Text and Files

What watermarks-remover does

watermarks-remover is an agent skill and Python service for inspecting and cleaning AI provenance marks from text and supported file formats. It is intended for privacy, research, and content hygiene on material you own or are authorized to process.

The agent skill is a thin HTTP client. It does not contain the cleaning implementation and does not require Python on the agent host. Instead, it sends work to a separately running service built with the Python standard library.

The project divides its work into three areas:

  • Text Layer A: deterministically removes invisible Unicode carriers, exotic spaces, bidirectional controls, and tag characters.
  • Text Layer B: uses substantial rewriting to reduce statistical token-sampling watermarks. This is best-effort and may change style or voice.
  • File cleaning: removes supported C2PA, EXIF, XMP, document-property, and container metadata from many file types.

No tool can guarantee that a vendor detector will stop identifying content. Keep original files, review every cleaned result, and use the project only on content you own or have permission to process.

Key features and supported formats

The core scripts require Python 3.10 or newer and use only the standard library. Optional tools such as c2patool, exiftool, and qpdf are used when available.

Supported cleaning targets

  • Images: PNG, JPEG, WebP, AVIF, HEIC, BMP, GIF, TIFF, and SVG.
  • Documents: PDF, DOCX, XLSX, PPTX, EPUB, and ODT.
  • Text and web files: HTML and Markdown, including supported metadata, JSON-LD, AI frontmatter keys, and Layer A text marks.
  • Media containers: MP4, MOV, M4A, M4V, WAV, MP3, and FLAC.

Unrecognized formats are not cleaned automatically. Text-specific commands also reject binary files and direct you to the general file tools.

Important limitations

Core cleaning does not cover soft-bound C2PA or pure pixel, audio, and video watermarks. PDF cleaning needs qpdf for a structural strip because an incremental metadata edit can leave recoverable bytes. An optional Ghostscript pass can address metadata inside embedded PDF images.

Detection is separate from cleaning. Optional detectors for MarkLLM, keyed-Gumbel, Claude seams, and image SynthID scoring are opt-in and designed to fail softly when unavailable.

Install an agent skill

Clone or download the repository, enter its directory, and run the installer. The following command installs the full service-backed skill for a personal Claude Code configuration:

python3 install_skill.py --skill remove-ai-marks --target claude-code

Available installation targets include:

  • --target claude-code installs under ~/.claude/skills/<skill>.
  • --target claude-project --project-dir PATH installs under PATH/.claude/skills/<skill>.
  • --target cowork produces dist/<skill>.zip for upload through the cloud Skills interface.
  • --target cursor installs under ~/.cursor/skills/<skill>.

Use python3 install_skill.py --list to list shipped skills. The repository provides the full remove-ai-marks skill and a self-contained, text-only clean-user-facing-text skill. Existing installations are backed up unless you use --force. For live editing from the checkout, add --link.

Install the Claude Code plugin

Claude Code users can install the marketplace plugin directly:

/plugin marketplace add guillaumemeyer/watermarks-remover
/plugin install watermarks-remover@watermarks-remover

The skills then load as /watermarks-remover:remove-ai-marks and /watermarks-remover:clean-user-facing-text. Update the plugin with:

/plugin marketplace update watermarks-remover

Link the skill for Grok

mkdir -p ~/.grok/skills
ln -sfn "$(pwd)/skills/remove-ai-marks" ~/.grok/skills/remove-ai-marks

Start the service

Run the local HTTP service from the repository:

make serve

Alternatively, launch the server script explicitly:

python3 service/scripts/server.py --host 127.0.0.1 --port 8765

The default address is http://127.0.0.1:8765. If the service runs elsewhere, set WATERMARKS_SERVICE_URL for the skill. The server binds to loopback by default, which avoids exposing it directly to other machines.

Check that the service is responding:

curl -s http://127.0.0.1:8765/health

The health endpoint returns an object containing ok and the service version.

Inspect and clean files from the command line

The safest workflow is to inspect a file first, write the cleaned output to a new path, and compare the result with the original.

SCRIPTS=service/scripts

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

Use inspect_file.py and clean_file.py for both text and binary containers. These commands choose the appropriate supported cleaner based on the file.

Clean Layer A text marks

For plain-text input, inspect invisible and edit-based Unicode carriers with:

python3 "$SCRIPTS/inspect_text.py" draft.md

Then create a cleaned copy and print statistics:

python3 "$SCRIPTS/clean_text.py" draft.md -o draft.cleaned.md --stats

Layer A is deterministic and intended to be lossless with respect to the visible prose. Do not pass DOCX, PDF, images, or other binary input to these text commands; use the file commands instead.

Prepare a Layer B rewrite

Statistical text watermarks require rewriting rather than simple character removal. By default, the rewrite command prints a prompt:

python3 "$SCRIPTS/rewrite_text.py" draft.md --backend print-prompt --strength paraphrase

Layer B is iterative and best-effort. It can reduce fidelity to the original style and voice. When quality matters more than watermark hygiene, the project recommends using Layer A and file cleaning while preserving the original prose.

Use the HTTP API

The service exposes inspection, detection, cleaning, batch processing, capability discovery, and an OpenAPI specification.

  • GET /health reports service health and version.
  • GET /capabilities reports available optional tools and backends.
  • GET /openapi.json returns the OpenAPI 3.0.3 specification.
  • POST /inspect returns the input kind, suspicious status, and report.
  • POST /detect returns configured detector results.
  • POST /clean returns cleaned base64 data and a report.
  • POST /inspect/batch and POST /clean/batch process up to 50 files per request.

The following example base64-encodes a Markdown file and sends it to the cleaning endpoint:

WM="http://127.0.0.1:8765"
curl -s "$WM/health"
curl -s -X POST "$WM/clean" -H 'Content-Type: application/json' \
  -d "{\"file\": \"$(base64 < notes.md | tr -d '\n')\", \"name\": \"notes.md\"}"

Set WATERMARKS_SERVER_API_KEY to require bearer authentication. If you deploy beyond a local development machine, retain authentication and appropriate network controls.

Run the service with Docker

Build and run the core image with a read-only filesystem and a temporary directory:

make docker-core-build
docker run --rm -p 127.0.0.1:8765:8765 --read-only --tmpfs /tmp watermarks-remover

Docker Compose supports several profiles:

docker compose up -d
docker compose --profile harness up -d
docker compose --profile heavy up -d
  • The default starts the core service.
  • The harness profile adds MarkLLM and MarkDiffusion components.
  • The heavy profile adds locally built CtrlRegen and SynthID components.

Core, MarkLLM, and MarkDiffusion images are published on GHCR. CtrlRegen and the SynthID scorer remain local-only because of upstream licensing. Copy .env.example to .env for optional configuration; basic text cleaning requires no additional settings.

Enable automatic checks with a hook

An agent skill is an instruction, so the model decides whether to invoke it. A hook is more deterministic: it runs whenever a matching tool call occurs.

The Claude Code plugin registers a PostToolUse hook for Write, Edit, MultiEdit, and NotebookEdit. It invokes service/scripts/hook_written_file.py in one of two modes:

  • check: the default mode reports marks but does not modify the file.
  • clean: removes supported marks in place and notifies the model.

Choose the mode through the plugin's Hook mode setting or set:

WATERMARKS_HOOK_MODE=clean

Without the plugin, you can register the hook manually in ~/.claude/settings.json. Hooks cover files written by the agent and the pre-commit gate, but cleaning text directly in chat transcripts remains best-effort and depends on the skill.

Add a pre-commit check

To stop marked files before they enter a Git commit, add the repository's check hook to .pre-commit-config.yaml:

repos:
  - repo: https://github.com/guillaumemeyer/watermarks-remover
    rev: v0.5.0
    hooks:
      - id: watermarks-remover-check

The watermarks-remover-check hook fails when marks are found. The alternative watermarks-remover-clean hook cleans files in place and is explicitly opt-in.

Advanced tips

Preserve originals and compare results

Write cleaned data to a separate output path whenever possible. This is especially important for Layer B rewrites and complex document containers, where processing may affect prose, embedded assets, or application-specific properties.

Install the right PDF tools

Use qpdf when you need a real structural PDF strip. The README warns that exiftool alone performs an incremental update that can leave old bytes recoverable. Use the optional Ghostscript deep pass when metadata may be present inside embedded images.

Treat detection and cleaning as different operations

A clean operation removes supported carriers and metadata, while a detector estimates whether a configured watermark scheme is present. For example, MarkLLM verification is meaningful only under the same configuration, and keyed-Gumbel replay requires the generation key. These tools are not universal vendor oracles.

Choose Layer B carefully

If you use Layer B, the project recommends a non-origin model to reduce the chance of applying the same kind of mark again. Because heavy paraphrasing can damage tone and precision, skip it when preserving writing quality is the higher priority.

Explore optional backends

Bootstrap scripts under service/scripts/ can configure reverse-SynthID for image scoring, CtrlRegen for pixel-domain removal, MarkLLM for configured text-watermark verification, and MarkDiffusion for image-watermark experiments. These backends have different requirements and limitations; consult their setup scripts before enabling them.

Responsible use

Use watermarks-remover only for privacy, research, and hygiene on content you own or are authorized to process. It is not intended for academic fraud or false claims that AI-generated material was written by a human. Follow applicable laws and institutional policies.

The project is available under the MIT License. For releases and source code, visit the watermarks-remover GitHub repository.

Conclusion

watermarks-remover combines deterministic text cleanup, metadata removal across many file containers, optional detection backends, agent integration, hooks, and an HTTP API. Start with inspection, retain the original, apply the narrowest suitable cleaning layer, and review the result rather than treating any detector outcome as guaranteed.