techbuzzz/ai-watermark-remover

A local-first .NET 10 CLI and HTTP API for removing AI watermarks and metadata from text, images, and documents. Detects AI-specific text watermarks (DeepSeek, Grok, Mistral), strips EXIF/XMP/C2PA from images, and cleans document metadata (PDF, DOCX, PPTX). 100% offline, no telemetry.

C#

3

102 commits

updated Sep 14, 2026

See the code

README

🧹 WatermarkRemover

A cross-platform, single-binary .NET 10 toolkit for stripping AI-provenance watermarks from text, markdown, files (metadata), and images — with first-class Russian support.

GitHub stars Forks Build & Test Release

Latest release MIT License .NET 10 Commit activity Open issues Contributors

🐛 Report a bug · ✨ Request a feature · ❓ FAQ · 💬 Discussions


✨ Why WatermarkRemover?

AI providers increasingly embed invisible provenance signals into the content they generate — invisible Unicode characters, statistical token biases, hidden C2PA manifests, EXIF/XMP metadata, visual logos, and per-vendor "SynthID"-style fingerprints. Sometimes you want to clean your own output, normalize a corpus, or run forensic analysis.

WatermarkRemover is a single .NET 10 binary (no Node, no Python, no Electron) that:

  • 🧽 Cleans text through three complementary layers (Unicode hygiene → statistical rewrite → vendor heuristics)
  • 📝 Cleans markdown while preserving fenced code blocks and configurable structure
  • 🗂️ Strips metadata from JPEG / PNG / WebP / HEIF / AVIF / MP4 / MOV / 3GP / PDF / DOCX / PPTX / XLSX / HTML / EPUB / RTF — pixel-preserving, byte-level. (TIFF support was retired in the SkiaSharp migration — SkiaSharp has no TIFF codec.)
  • 🖼️ Inpaints visual watermarks with the LaMa ONNX model
  • 🌐 Speaks Russian natively — synonym dictionary, homoglyph-safe Unicode normalisation
  • 🚀 Runs everywhere — Windows / Linux / macOS / x64 / ARM64
  • 🐳 Hosts as a microservice — built-in HTTP API with auth, rate limiting, multipart uploads

⚠️ Responsible use. This tool is for cleaning content you own or have permission to modify, for security research, and for forensic analysis. Removing watermarks from third-party content to evade attribution may violate terms of service or law. See SECURITY.md → Responsible use.


📑 Table of contents


🚀 Quick start

The promise: clone, build, run — and the API + web UI are both live on http://localhost:5080/. Or docker run and the UI is immediately available from the container. No extra steps, no separate ports.

# 1. Clone & build (one-time, needs .NET 10 SDK + Node 20+)
git clone https://github.com/techbuzzz/ai-watermark-remover.git
cd ai-watermark-remover
dotnet build                           # .NET pipeline
(cd web && npm install && npm run build)   # web UI (bundled into the .NET publish)

# 2. Strip invisible characters from a text blob
dotnet run --project src/WatermarkRemover.CLI -- clean-text "Hello‍‎world"   # ZWSP, ZWJ, LRM
# → "Helloworld"

# 3. Strip metadata from a folder of photos (recursive)
dotnet run --project src/WatermarkRemover.CLI -- clean-file ./photos --recursive

# 4. Remove a visual watermark from an image (auto-detected mask + LaMa inpainting)
dotnet run --project src/WatermarkRemover.CLI -- download-model
dotnet run --project src/WatermarkRemover.CLI -- clean-image photo.png -o clean.png

# 5. Spin up the HTTP API + web UI on :5080 (UI at http://localhost:5080/)
dotnet run --project src/WatermarkRemover.CLI -- serve --port 5080 --api-key s3cret

Or the one-command variants:

# Linux / macOS (uses Make)
make build && make serve

# Windows (PowerShell)
powershell -ExecutionPolicy Bypass -File scripts\build.ps1 -Serve

Or just Docker — single command, UI included:

docker run --rm -p 5080:5080 techbuzzz/watermarkremover:latest
# → open http://localhost:5080/

🌐 Web UI

watermarkremover serve ships a plug-and-play Astro web UI on the same port — a single-page "box" with four tabs (Text, Markdown, File, Image) that wrap every endpoint in the API. The UI is a pure-static bundle (no Node server, no framework runtime), so it deploys anywhere a folder of files can be served.

After building once (see Quick start), the UI is automatically mounted on the same port as the API:

dotnet run --project src/WatermarkRemover.CLI -- serve --port 5080
# → open http://localhost:5080/

To re-point the UI at a different API (e.g. a remote server you already have running), rebuild with a different URL:

cd web
PUBLIC_API_URL=https://api.other-host.example.com npm run build

For full configuration, dev loop, deployment recipes (Vercel / Netlify / GH Pages / nginx), and security notes, see 📘 docs/WEB-UI.md.


📦 Installation

Every install path produces a working API and web UI on the same port. Pick the path that fits your environment.

Download the latest self-contained single-file executable for your platform from GitHub Releases. No .NET runtime required. Web UI is embedded in the binary.

PlatformArchitectureAsset
🐧 Linuxx64watermarkremover-linux-x64.zip
🐧 LinuxARM64watermarkremover-linux-arm64.zip
🪟 Windowsx64watermarkremover-win-x64.zip
🍎 macOSx64watermarkremover-osx-x64.zip
# Linux / macOS — one binary, UI included
unzip watermarkremover-linux-x64.zip
sudo mv watermarkremover /usr/local/bin/
watermarkremover serve --port 5080
# → open http://localhost:5080/  (UI + API on the same port)
# Windows (PowerShell) — one .exe, UI included
Expand-Archive .\watermarkremover-win-x64.zip
& .\watermarkremover\watermarkremover.exe serve --port 5080
# → open http://localhost:5080/  (UI + API on the same port)

2. Docker (one command, UI included)

# Build the image (multi-stage; the Astro web UI is built into the same image)
docker build -t watermarkremover .
docker run --rm -p 5080:5080 -e WATERMARKREMOVER_API_KEY=s3cret watermarkremover
# → open http://localhost:5080/

Or with docker compose (single-service dev loop with the ./models volume mounted):

docker compose up
# → open http://localhost:5080/

See 🐳 Docker and docker-compose.yml.

3. From source

Requires the .NET 10 SDK and Node.js 20+ (for the web UI only).

git clone https://github.com/techbuzzz/ai-watermark-remover.git
cd ai-watermark-remover

# One command — builds the web UI + .NET, then runs the server with the UI on :5080
# Linux / macOS
make serve

# Windows PowerShell
powershell -ExecutionPolicy Bypass -File scripts\build.ps1 -Serve

Or the manual three-liner if you prefer:

dotnet build
(cd web && npm install && npm run build)
dotnet run --project src/WatermarkRemover.CLI -- serve --port 5080
# → open http://localhost:5080/

4. As a library (planned)

WatermarkRemover.Core, .Text, .Metadata, .Image will be published to NuGet once the package surface stabilises — see BACKLOG.md → Distribution. Track progress on the P1 board.

5. As a dotnet tool (WR-P011)

If you have the .NET 10 SDK (or the ASP.NET Core runtime) installed and want the smallest, most idiomatic .NET install path, the watermarkremover CLI is also published as a global tool:

dotnet tool install -g watermarkremover
watermarkremover --version
watermarkremover clean-text "Hello‍‎world"   # → Helloworld

The tool ships the same serve + serve-mcp + clean-* + detect-* commands as the pre-built binary. The Astro web UI is bundled so watermarkremover serve works out of the box (pass --no-ui to skip it). Update with dotnet tool update -g watermarkremover; uninstall with dotnet tool uninstall -g watermarkremover.


🧠 How it works

Text — three layers

The text pipeline runs three independent layers in order. Each can be toggled via config.yaml or the CLI.

  • Layer A · Unicode hygiene — strips zero-width spaces / joiners, BOM, soft hyphens, bidi controls, variation selectors; applies NFKC normalisation and homoglyph folding only between Latin letters (so genuine Cyrillic text is never mangled).
  • Layer B · Statistical rewrite — swaps "green-list" tokens for synonyms using built-in English and Russian dictionaries, or back-translates through an Ollama-compatible LLM endpoint for higher-quality rewrites.
  • Layer C · Vendor detectors — best-effort heuristics for Claude, Gemini/SynthID, OpenAI, DeepSeek ( tag leak + fullwidth-ASCII CJK fingerprint), Grok (emoji burst + em-dash cluster), and Mistral (chat-template marker leak) invisible-carrier patterns. These are heuristic because the underlying schemes are key-based and not publicly verifiable.

Markdown

20+ toggleable transforms; preserves fenced code blocks (only forced invisible-character cleanup runs inside them); strips AI-specific artifacts (frontmatter, "As an AI" lines, emoji-driven sign-offs) when enabled.

Metadata

Byte-level, pixel-preserving cleaners:

  • JPEG — segment parser (APP0–APP15, COM); strips EXIF / XMP / IPTC / ICC / C2PA
  • PNG — chunk filter (tEXt, zTXt, iTXt, eXIf); preserves IHDR / IDAT / IEND
  • PDF — PdfPig rebuild without document info / XMP metadata
  • DOCX / PPTX / XLSX — OpenXML core/extended/custom properties; DOCX also strips revision history; PPTX also strips per-slide comments + presentation-wide author list; XLSX also strips workbook-wide author list + per-worksheet threaded comments
  • HTML — <meta name="generator|author"> + <!-- comments --> + tracking scripts
  • EPUB — OCF zip-rewrite; strips OPF <dc:*> (except dc:identifier kept as a fresh UUID) and <meta> entries; preserves every other entry byte-for-byte (XHTML / CSS / images / NCX)
  • RTF — character-stream parser; strips \author / \company / \manager / \generator / \doccomm / \title / \subject / \keywords / \category / \comment / \hlinkbase / \operator / \version / \edmins / \nofpages / \nofwords / \nofchars / \nofcharsws / \id plus compound time-table entries \creatim / \revtbl / \printim / \buptim (each followed by sub-control words like \yr\mo\dy\hr\min\sec); preserves font table, colour table, stylesheet, headers / footers, and body text byte-for-byte
  • MP4 / MOV / M4V / M4A / 3GP — ISOBMFF box walker; strips moov.udta (the ©xyz GPS atom, ©mak / ©mod / ©swr device make / model / software, ©day / ©nam / ©ART / ©cmt authorship) and the QuickTime meta keys / ilst (title / author / album / genre); also strips EXIF / XMP / ICC from any meta; preserves mvhd / trak / edts / mvex and the mdat bitstream byte-for-byte (with largesize honour)

Image

load → mask → resize → infer → blend → save

  • Mask generation — alpha + colour-frequency heuristics with connected-component extraction (auto-detects logos, text overlays, corner watermarks)
  • Manual mask — supply your own mask image or coordinates
  • Inference — big-lama ONNX model; degrades gracefully if the model is missing (region detection still runs, the source image is copied unchanged)
  • Blending — alpha-blend the inpainted patch back to keep edges anti-aliased

🛠️ CLI reference

The produced executable is named watermarkremover. During development use dotnet run --project src/WatermarkRemover.CLI -- ….

Global options (available on every command)

FlagDescription
--jsonEmit structured JSON instead of pretty text
--verbose / -vVerbose logging (Debug+)
--dry-runShow what would happen without touching files
--output / -oWrite result to a path instead of stdout
--config / -cPath to a custom config.yaml
--version / -VPrint the assembly version and exit (short-circuits before config / logging are loaded). A bare -v is also accepted; note that Spectre intercepts -v even when paired with other args, so use --verbose (long form) for verbose logging.

Commands

CommandPurpose
clean-textClean plain text (Layers A / B / C).
clean-markdownClean markdown, preserving fenced code blocks.
clean-fileStrip metadata from files (single / directory / --recursive).
clean-imageRemove visual watermarks via mask + LaMa inpainting.
clean-allAuto-route a path — dispatches each file to text / markdown / metadata pipeline by extension (use --recursive, --dry-run).
detect-textDetect (do not remove) watermark signatures in text.
detect-markdownDetect AI artifacts in markdown.
detect-watermarkDetect visual watermark regions in an image.
inspect-fileReport all metadata found in a file.
download-modelDownload & extract the LaMa ONNX inpainting model.
serveHost the HTTP API (ASP.NET Core Minimal API).
serve-mcpHost the MCP server. --transport stdio (default, local agents) or --transport http (Streamable HTTP, remote).
completionsEmit a shell completion script (bash, zsh, powershell, fish).

Examples

# Clean text from an argument, a file, or stdin
dotnet run --project src/WatermarkRemover.CLI -- clean-text "Some AI text…"
cat article.txt | dotnet run --project src/WatermarkRemover.CLI -- clean-text --json

# Enable Layer B statistical (green-list) rewriting
dotnet run --project src/WatermarkRemover.CLI -- clean-text -i in.txt -o out.txt --statistical

# Clean markdown but keep everything except AI artifacts (default) or strip everything
dotnet run --project src/WatermarkRemover.CLI -- clean-markdown -i README.md --strip-all

# Strip metadata from a whole folder of files recursively
dotnet run --project src/WatermarkRemover.CLI -- clean-file ./docs --recursive

# Point one command at a mixed directory: .md → markdown, .jpg → metadata,
# .txt → text, .bin → skipped. Use --dry-run to preview.
dotnet run --project src/WatermarkRemover.CLI -- clean-all ./mixed-dir --recursive

# Inspect a file's metadata (JSON output)
dotnet run --project src/WatermarkRemover.CLI -- inspect-file photo.jpg --json

# Remove a visual watermark (auto-detected mask)
dotnet run --project src/WatermarkRemover.CLI -- clean-image logo.png -o clean.png

# Russian: synonym-aware rewriter is on by default with --statistical
dotnet run --project src/WatermarkRemover.CLI -- clean-text "Это значимый результат." --statistical
# → "Это существенный результат."

🌐 HTTP API (serve)

watermarkremover serve --port 5080 --api-key s3cret
  • Binds http://0.0.0.0:5080 by default (override with --host, --port).
  • Rate limiting: fixed window, 100 requests / minute / IP (HTTP 429 when exceeded).
  • Auth: when --api-key is supplied, every endpoint except /health requires the X-API-Key header. Omit --api-key to run open.

Endpoints

Method & pathBodyReturns
POST /clean/text{ "text": "…" }TextCleanResult JSON
POST /detect/text{ "text": "…" }WatermarkMatch[] JSON
POST /clean/markdown{ "markdown": "…", "stripAll": false }MarkdownCleanResult JSON
POST /detect/markdown{ "markdown": "…", "stripAll": false }AiArtifact[] JSON
POST /clean/filemultipart file uploadcleaned file (octet-stream)
POST /inspect/filemultipart file uploadMetadataEntry[] JSON
POST /clean/imagemultipart image uploadcleaned image
POST /detect/imagemultipart image uploadDetectedRegion[] JSON
GET /health—{ "status": "ok" }
GET /swagger—interactive Swagger UI (HTML)
GET /swagger/v1/swagger.json—OpenAPI 3.0 spec (JSON)

Example

curl -s -X POST http://localhost:5080/clean/text \
  -H "Content-Type: application/json" -H "X-API-Key: s3cret" \
  -d '{"text":"Пример текста"}'```

---

## 🤖 MCP server (`serve-mcp`)

`serve-mcp` exposes the full pipeline as
[Model Context Protocol](https://modelcontextprotocol.io/) tools so any
MCP-compatible agent (Claude Code, OpenCode, MiniMax Code, VS Code,
Cursor, Continue, …) can call `clean_text`, `clean_markdown`,
`clean_file`, `clean_image`, `detect_text`, `detect_markdown`,
`inspect_file`, and `detect_watermark` directly — no shell-out to the
CLI. Built on the official [`ModelContextProtocol` C#
SDK](https://github.com/modelcontextprotocol/csharp-sdk).

```bash
# stdio (default) — local agents (Claude Code, OpenCode, MiniMax Code, Cursor, Continue)
watermarkremover serve-mcp
# register with Claude Code
claude mcp add watermarkremover -- watermarkremover serve-mcp
# OpenCode — the project ships `.opencode/opencode.jsonc` pre-wired
# with a `watermarkremover` MCP entry (default `enabled: false`).
# Install the binary, flip the flag, restart OpenCode. Three slash
# commands (`/wr-clean-text`, `/wr-clean-file`, `/wr-detect`) appear
# automatically — no extra wiring. See docs/MCP.md → OpenCode.

# Streamable HTTP (stateless) — remote agents / Docker
watermarkremover serve-mcp --transport http --port 5090 --api-key s3cret

Flags: --transport <stdio\|http> (default stdio), -H|--host, -p|--port (default 5090), --api-key, --rate-limit, --rate-window. Configurable via the mcp: section in config.yaml — see docs/CONFIGURATION.md. The stdio transport routes all logging to stderr so the JSON-RPC stream on stdout stays clean, per the MCP stdio spec.

Cursor / Continue users get an npm wrapper. The project ships a zero-dependency npm package at @watermarkremover/mcp that downloads the platform-appropriate release binary on npm install and exposes npx -y @watermarkremover/mcp as a drop-in mcpServers entry. No manual dotnet install, no PATH tweaks — just paste the snippet from docs/MCP.md into ~/.cursor/mcp.json or ~/.continue/config.json.


> **Full reference:** [📘 docs/MCP.md](./docs/MCP.md) — architecture diagram, all 8
> tool schemas (request/response JSON), transport options, configuration keys, and
> install recipes for Claude Code, OpenCode, MiniMax Code, Cursor, Continue, and
> Docker. Claude Code-specific install paths and the optional
> `UserPromptSubmit` auto-clean hook live in
> [🪝 docs/CLAUDE-CODE.md](./docs/CLAUDE-CODE.md).

---

## 🧠 Agent skills

The [`skills/`](./skills/) directory ships **drop-in skill packages**
that teach AI coding assistants *when* and *how* to use
WatermarkRemover. Each skill is a self-contained folder with a
`SKILL.md` (the agent reads it) plus POSIX (`run.sh`) and Windows
(`run.ps1`) wrappers that pipe input through the CLI.

| Skill | CLI command | What it does |
|-------|-------------|--------------|
| `watermark-clean-text`     | `clean-text`     | Strip invisible characters and rewrite AI-sounding phrasing (EN + RU). |
| `watermark-clean-markdown` | `clean-markdown` | Strip AI signatures / frontmatter from Markdown, preserve fenced code. |
| `watermark-clean-file`     | `clean-file`     | Strip EXIF / XMP / IPTC / C2PA / XMP-pdf / DOCX core props / HTML meta. |
| `watermark-clean-image`    | `clean-image`    | Detect + inpaint a visual watermark with LaMa. |
| `watermark-detect`         | `detect-*`       | Read-only AI provenance check. |

**OpenCode users get the integration pre-wired.** The project ships
the master `watermark-remover` skill under
[`.opencode/skills/watermark-remover/SKILL.md`](./.opencode/skills/watermark-remover/SKILL.md)
plus three slash commands under
[`.opencode/commands/`](./.opencode/commands/) — `/wr-clean-text`,
`/wr-clean-file`, `/wr-detect` — so the agent learns the
integration as soon as you open the repo. The only step left is to
enable the MCP server entry in
[`.opencode/opencode.jsonc`](./.opencode/opencode.jsonc) after
installing the binary. See [`docs/MCP.md → OpenCode`](./docs/MCP.md#opencode).

**Claude Code users get a parallel pre-wired integration.** The
project ships the master `watermark-remover` skill under
[`.claude/skills/watermark-remover/SKILL.md`](./.claude/skills/watermark-remover/SKILL.md),
a drop-in `mcp-config.json`, and an optional `UserPromptSubmit`
hook at
[`.claude/skills/watermark-remover/hooks.json`](./.claude/skills/watermark-remover/hooks.json)
that auto-cleans text pasted into the prompt. The fastest install
is one line:

```bash
claude mcp add watermarkremover -- watermarkremover serve-mcp

Then restart Claude Code — the eight MCP tools appear and the project-local SKILL.md teaches the agent when to call them. See docs/CLAUDE-CODE.md for project-local mcp-config.json, global ~/.claude/settings.json merge, the auto-clean hook, and the troubleshooting table.

MiniMax Code users get a parallel pre-wired integration. The project ships a V1 local plugin package at minimax-code/watermark-remover/ — drop-in .minimax-plugin/plugin.json manifest, stdio MCP server entry in servers.mcp.json, and six skills under skills/ (master + five per-format). Copy the folder into MiniMax Code's plugin directory and toggle the plugin on:

# Linux / macOS
cp -R minimax-code/watermark-remover \
      ~/.local/share/MiniMax/plugins/watermark-remover
# Windows PowerShell
$dest = Join-Path $env:APPDATA 'MiniMax\plugins\watermark-remover'
Copy-Item -Recurse -Force minimax-code/watermark-remover $dest

The MCP server (watermarkremover serve-mcp) auto-starts as a stdio child process and the agent gets the same eight tools as the other agents — clean_text, clean_markdown, clean_file, clean_image, detect_text, detect_markdown, inspect_file, detect_watermark. See docs/MINIMAX-CODE.md for the full plugin reference, the HTTP-transport swap, the slash-command status, and the troubleshooting table.

VS Code users get a first-party extension. The project ships a dedicated VS Code extension at vscode/watermark-remover/ that adds three commands and two context-menu integrations: right-click a text selection → WatermarkRemover: Clean AI watermarks from selection; right-click a file in the Explorer → WatermarkRemover: Strip metadata from selected file(s); command palette → WatermarkRemover: Detect AI watermarks in selection. The extension is a thin client over the watermarkremover CLI — it does not re-implement any cleaning logic — and ships the same skills/ folder so AI agents running inside VS Code (Continue, Cline, …) learn the tool. Install from the Marketplace (search "WatermarkRemover", publisher techbuzzz) or via code --install-extension watermark-remover.vsix. See docs/VS-CODE.md for the full reference, configuration knobs, and the source-mode dotnet run recipe.

Install for your agent in one command:

./skills/install.sh --agent opencode          # ./.opencode/skills/watermarkremover/
./skills/install.sh --agent claude            # ~/.claude/skills/watermarkremover/
./skills/install.sh --agent minimax           # ~/.minimax/skills/watermarkremover/
./skills/install.sh --agent auto              # probe CWD for .opencode/.claude/.minimax
./skills/install.sh --dry-run                 # print what would happen

The installer scripts (install.sh + install.ps1) and the C# SkillsInstallerTargetResolver (in WatermarkRemover.CLI/Infrastructure/) share the same resolution rules — unit-tested for every agent name and every environment override.

Full reference: 📘 docs/SKILLS.md — resolution matrix, per-skill deep-dive, MCP-vs-skill integration, troubleshooting.


🐳 Docker

Pre-built images will be published to GHCR / Docker Hub after the first release; until then, build locally:

docker build -t watermarkremover .
docker run --rm -p 5080:5080 watermarkremover
# or with API-key auth + a model volume:
docker run --rm -p 5080:5080 \
  -e WATERMARKREMOVER_API_KEY=s3cret \
  -v $(pwd)/models:/app/models \
  watermarkremover

# MCP Streamable HTTP transport (stateless JSON-RPC for remote agents)
docker run --rm -p 5090:5090 \
  -e WATERMARKREMOVER_API_KEY=s3cret \
  watermarkremover serve-mcp --transport http --host 0.0.0.0 --port 5090 --api-key s3cret

docker-compose.yml ships two long-running services plus a profile-gated one-shot:

docker compose up                # HTTP API on :5080 (default)
docker compose up mcp            # MCP Streamable HTTP on :5090
docker compose up --detach mcp watermarkremover  # both side by side
docker compose run --rm clean clean-text \
  --input /data/in.txt --output /data/out.txt   # one-shot CLI

The Dockerfile is multi-stage, runs as a non-root user, and ships with a HEALTHCHECK that hits /health. Both 5080 and 5090 are EXPOSEd so either service can be published via the usual docker run -p syntax. The release workflow (.github/workflows/release.yml) smoke-tests the serve-mcp sub-command on every RID before publishing the binary, so a regression that accidentally trims the ModelContextProtocol SDK out of the single-file bundle fails the build. Full Docker / Compose reference: docs/MCP.md → Docker.


⚙️ Configuration

Copy src/config.yaml and edit it. Resolution order:

  1. --config <path> (CLI flag)
  2. ./config.yaml in the working directory
  3. config.yaml next to the executable
  4. Built-in defaults

CLI flags always override config values. See the Configuration reference for every key.


🧪 Build & test

dotnet build            # 0 warnings, 0 errors (warnings-as-errors on all src projects)
dotnet test             # 62 tests across Text (35) / Metadata (18) / Image (9)

Continuous integration runs on every push / PR to main across Ubuntu + Windows runners — see .github/workflows/build-and-test.yml. Coverage is collected in cobertura format and uploaded as a workflow artifact.


🗺️ Solution layout

WatermarkRemover.sln
├── src/
│   ├── WatermarkRemover.Core        # Models, interfaces, configuration, DI contracts
│   ├── WatermarkRemover.Text        # Layer A (Unicode) / B (statistical) / C (vendor) + Markdown
│   ├── WatermarkRemover.Metadata    # JPEG / PNG / WebP / TIFF / HEIF / AVIF / PDF / DOCX / PPTX / XLSX / HTML / EPUB / RTF metadata cleaners
│   ├── WatermarkRemover.Image       # Mask generation + LaMa ONNX inpainting pipeline
│   └── WatermarkRemover.CLI         # Spectre.Console CLI + ASP.NET Core HTTP API (serve)
└── src/tests/
    ├── WatermarkRemover.Text.Tests       (35 tests)
    ├── WatermarkRemover.Metadata.Tests   (18 tests)
    └── WatermarkRemover.Image.Tests       (9 tests)

🇷🇺 Поддержка русского языка / Russian language support

Приложение полностью поддерживает русский язык:

  • Слой A (Unicode) одинаково безопасно очищает латиницу и кириллицу. Нормализация омоглифов (похожих символов) срабатывает только когда подозрительный символ стоит между латинскими буквами, поэтому настоящие русские слова (например, «Привет мир») никогда не портятся.
  • Слой B (статистический рерайт) содержит встроенный русский словарь синонимов (SynonymDictionary) — например, «значимый» → «существенный», «использовать» → «применять». Включается флагом --statistical.
  • Слой C (детекторы вендоров) и очистка метаданных не зависят от языка.
  • CLI и HTTP API принимают и корректно обрабатывают текст в кодировке UTF-8 на русском языке.

Пример:

dotnet run --project src/WatermarkRemover.CLI -- clean-text "Это значимый результат." --statistical
# → "Это существенный результат."

Соответствующие юнит-тесты (StatisticalWatermarkRewriterTests, UnicodeHygieneCleanerTests, MarkdownCleanerTests, VendorDetectorTests) проверяют сохранность и корректную обработку русского текста.


📚 Documentation

  • 📘 docs/FAQ.md — frequently asked questions
  • ⚖️ docs/COMPARISON.md — how this tool differs from exiftool, mat2, exiv2, sd-webui-watermark, etc.
  • 🏛️ docs/ARCHITECTURE.md — module boundaries, data flow, extension points
  • 🌐 docs/WEB-UI.md — the plug-and-play Astro web UI
  • ⚙️ docs/CONFIGURATION.md — every config.yaml key explained
  • 🚀 docs/ci-release.md — how the release pipeline works
  • 🤖 docs/MCP.md — serve-mcp for Claude Code, OpenCode, MiniMax Code, Cursor, Continue (MCP server architecture, tool schemas, install recipes)
  • 📦 npm/watermarkremover-mcp/ — @watermarkremover/mcp zero-dependency npm wrapper for Cursor, Continue, and any other MCP host that prefers npm-based registration (downloads the platform-appropriate release binary on npm install)
  • 🪝 docs/CLAUDE-CODE.md — Claude Code-specific install (project-local mcp-config.json, ~/.claude/settings.json merge, claude mcp add one-liner) and the optional UserPromptSubmit hook for auto-cleaning pasted text
  • 🧩 docs/MINIMAX-CODE.md — MiniMax Code V1 local plugin (minimax-code/watermark-remover/), install paths per OS, MCP transport swap, slash-command status, troubleshooting
  • 🆚 docs/VS-CODE.md — VS Code first-party extension (vscode/watermark-remover/), three commands (cleanText, cleanFile, detectText), right-click context menus, bundled skills, dotnet run source-mode recipe
  • 🧠 docs/SKILLS.md — drop-in agent skills (skills/) for OpenCode, Claude Code, MiniMax Code, Cursor, Continue (install, resolution rules, per-skill reference)
  • 🧭 BACKLOG.md — prioritised roadmap
  • 📝 TODO.md — current sprint

🗓️ Roadmap

The active roadmap lives in BACKLOG.md. Highlights:

P0 — Release readiness

  • CI matrix on Windows + Linux
  • Release workflow (self-contained binaries for 4 RIDs)
  • Multi-stage Docker image
  • Issue / PR templates, CODEOWNERS, CODE_OF_CONDUCT, SECURITY, CHANGELOG

P1 — Core features (v1.0)

  • More metadata formats (MP4) — WebP, HEIF, AVIF, EPUB, RTF landed (TIFF retired with the SkiaSharp migration)
  • DeepSeek / Grok / Mistral vendor detectors
  • Synonym dictionary: EN → 400+, RU → 200+
  • Image batch processing + GPU inference
  • OpenAPI / Swagger UI for the HTTP API

P2 — Platform & UX

  • clean-all auto-routing command
  • Shell completion (PowerShell / bash / zsh / fish)
  • Web UI (vanilla JS drag-and-drop)
  • Configurable rate-limit + CORS + /metrics

P3 — Quality & reliability

  • CLI integration test project (WebApplicationFactory)
  • Property-based tests (FsCheck) for Unicode hygiene
  • BenchmarkDotNet regression suite in CI
  • Polly resilience for LLM back-translation

P4 — Ecosystem

  • Homebrew / Scoop / Winget / APT packages
  • Python + Node.js bindings
  • Browser + VS Code extensions
  • NuGet packages

See BACKLOG.md for the full list with status indicators.


🤝 Contributing

We love PRs. Start with CONTRIBUTING.md for setup, code style, commit conventions, and the review process. The short version:

  1. Fork & branch from main (feat/<short-topic> or fix/<short-topic>).
  2. dotnet build and dotnet test must pass — warnings are errors.
  3. Add a unit test for any new behavior; keep the test pyramid balanced.
  4. Run dotnet format before pushing.
  5. Open a PR using the template; reference any related issue with Closes #N.

First-time contributors: look for issues tagged good first issue.

Please read our Code of Conduct — be kind, be constructive.


🔐 Security

For vulnerabilities, please do not file a public issue — see SECURITY.md for the responsible-disclosure channel and our responsible-use policy.


📜 License

MIT — Copyright (c) 2026 Victor Buzin.


💖 Acknowledgements


If WatermarkRemover saved you time, a ⭐ is the easiest way to say thanks.
agent-skills
ai
ai-agents
ai-watermark-remover
c2pa
cli-tool
csharp
dotnet
dotnet-10
self-hosted
watermark
watermark-remover

techbuzzz/ai-watermark-remover

A local-first .NET 10 CLI and HTTP API for removing AI watermarks and metadata from text, images, and documents. Detects AI-specific text watermarks (DeepSeek, Grok, Mistral), strips EXIF/XMP/C2PA from images, and cleans document metadata (PDF, DOCX, PPTX). 100% offline, no telemetry.

C#

3

102 commits

updated Sep 14, 2026

See the code

README

🧹 WatermarkRemover

A cross-platform, single-binary .NET 10 toolkit for stripping AI-provenance watermarks from text, markdown, files (metadata), and images — with first-class Russian support.

GitHub stars Forks Build & Test Release

Latest release MIT License .NET 10 Commit activity Open issues Contributors

🐛 Report a bug · ✨ Request a feature · ❓ FAQ · 💬 Discussions


✨ Why WatermarkRemover?

AI providers increasingly embed invisible provenance signals into the content they generate — invisible Unicode characters, statistical token biases, hidden C2PA manifests, EXIF/XMP metadata, visual logos, and per-vendor "SynthID"-style fingerprints. Sometimes you want to clean your own output, normalize a corpus, or run forensic analysis.

WatermarkRemover is a single .NET 10 binary (no Node, no Python, no Electron) that:

  • 🧽 Cleans text through three complementary layers (Unicode hygiene → statistical rewrite → vendor heuristics)
  • 📝 Cleans markdown while preserving fenced code blocks and configurable structure
  • 🗂️ Strips metadata from JPEG / PNG / WebP / HEIF / AVIF / MP4 / MOV / 3GP / PDF / DOCX / PPTX / XLSX / HTML / EPUB / RTF — pixel-preserving, byte-level. (TIFF support was retired in the SkiaSharp migration — SkiaSharp has no TIFF codec.)
  • 🖼️ Inpaints visual watermarks with the LaMa ONNX model
  • 🌐 Speaks Russian natively — synonym dictionary, homoglyph-safe Unicode normalisation
  • 🚀 Runs everywhere — Windows / Linux / macOS / x64 / ARM64
  • 🐳 Hosts as a microservice — built-in HTTP API with auth, rate limiting, multipart uploads

⚠️ Responsible use. This tool is for cleaning content you own or have permission to modify, for security research, and for forensic analysis. Removing watermarks from third-party content to evade attribution may violate terms of service or law. See SECURITY.md → Responsible use.


📑 Table of contents


🚀 Quick start

The promise: clone, build, run — and the API + web UI are both live on http://localhost:5080/. Or docker run and the UI is immediately available from the container. No extra steps, no separate ports.

# 1. Clone & build (one-time, needs .NET 10 SDK + Node 20+)
git clone https://github.com/techbuzzz/ai-watermark-remover.git
cd ai-watermark-remover
dotnet build                           # .NET pipeline
(cd web && npm install && npm run build)   # web UI (bundled into the .NET publish)

# 2. Strip invisible characters from a text blob
dotnet run --project src/WatermarkRemover.CLI -- clean-text "Hello‍‎world"   # ZWSP, ZWJ, LRM
# → "Helloworld"

# 3. Strip metadata from a folder of photos (recursive)
dotnet run --project src/WatermarkRemover.CLI -- clean-file ./photos --recursive

# 4. Remove a visual watermark from an image (auto-detected mask + LaMa inpainting)
dotnet run --project src/WatermarkRemover.CLI -- download-model
dotnet run --project src/WatermarkRemover.CLI -- clean-image photo.png -o clean.png

# 5. Spin up the HTTP API + web UI on :5080 (UI at http://localhost:5080/)
dotnet run --project src/WatermarkRemover.CLI -- serve --port 5080 --api-key s3cret

Or the one-command variants:

# Linux / macOS (uses Make)
make build && make serve

# Windows (PowerShell)
powershell -ExecutionPolicy Bypass -File scripts\build.ps1 -Serve

Or just Docker — single command, UI included:

docker run --rm -p 5080:5080 techbuzzz/watermarkremover:latest
# → open http://localhost:5080/

🌐 Web UI

watermarkremover serve ships a plug-and-play Astro web UI on the same port — a single-page "box" with four tabs (Text, Markdown, File, Image) that wrap every endpoint in the API. The UI is a pure-static bundle (no Node server, no framework runtime), so it deploys anywhere a folder of files can be served.

After building once (see Quick start), the UI is automatically mounted on the same port as the API:

dotnet run --project src/WatermarkRemover.CLI -- serve --port 5080
# → open http://localhost:5080/

To re-point the UI at a different API (e.g. a remote server you already have running), rebuild with a different URL:

cd web
PUBLIC_API_URL=https://api.other-host.example.com npm run build

For full configuration, dev loop, deployment recipes (Vercel / Netlify / GH Pages / nginx), and security notes, see 📘 docs/WEB-UI.md.


📦 Installation

Every install path produces a working API and web UI on the same port. Pick the path that fits your environment.

Download the latest self-contained single-file executable for your platform from GitHub Releases. No .NET runtime required. Web UI is embedded in the binary.

PlatformArchitectureAsset
🐧 Linuxx64watermarkremover-linux-x64.zip
🐧 LinuxARM64watermarkremover-linux-arm64.zip
🪟 Windowsx64watermarkremover-win-x64.zip
🍎 macOSx64watermarkremover-osx-x64.zip
# Linux / macOS — one binary, UI included
unzip watermarkremover-linux-x64.zip
sudo mv watermarkremover /usr/local/bin/
watermarkremover serve --port 5080
# → open http://localhost:5080/  (UI + API on the same port)
# Windows (PowerShell) — one .exe, UI included
Expand-Archive .\watermarkremover-win-x64.zip
& .\watermarkremover\watermarkremover.exe serve --port 5080
# → open http://localhost:5080/  (UI + API on the same port)

2. Docker (one command, UI included)

# Build the image (multi-stage; the Astro web UI is built into the same image)
docker build -t watermarkremover .
docker run --rm -p 5080:5080 -e WATERMARKREMOVER_API_KEY=s3cret watermarkremover
# → open http://localhost:5080/

Or with docker compose (single-service dev loop with the ./models volume mounted):

docker compose up
# → open http://localhost:5080/

See 🐳 Docker and docker-compose.yml.

3. From source

Requires the .NET 10 SDK and Node.js 20+ (for the web UI only).

git clone https://github.com/techbuzzz/ai-watermark-remover.git
cd ai-watermark-remover

# One command — builds the web UI + .NET, then runs the server with the UI on :5080
# Linux / macOS
make serve

# Windows PowerShell
powershell -ExecutionPolicy Bypass -File scripts\build.ps1 -Serve

Or the manual three-liner if you prefer:

dotnet build
(cd web && npm install && npm run build)
dotnet run --project src/WatermarkRemover.CLI -- serve --port 5080
# → open http://localhost:5080/

4. As a library (planned)

WatermarkRemover.Core, .Text, .Metadata, .Image will be published to NuGet once the package surface stabilises — see BACKLOG.md → Distribution. Track progress on the P1 board.

5. As a dotnet tool (WR-P011)

If you have the .NET 10 SDK (or the ASP.NET Core runtime) installed and want the smallest, most idiomatic .NET install path, the watermarkremover CLI is also published as a global tool:

dotnet tool install -g watermarkremover
watermarkremover --version
watermarkremover clean-text "Hello‍‎world"   # → Helloworld

The tool ships the same serve + serve-mcp + clean-* + detect-* commands as the pre-built binary. The Astro web UI is bundled so watermarkremover serve works out of the box (pass --no-ui to skip it). Update with dotnet tool update -g watermarkremover; uninstall with dotnet tool uninstall -g watermarkremover.


🧠 How it works

Text — three layers

The text pipeline runs three independent layers in order. Each can be toggled via config.yaml or the CLI.

  • Layer A · Unicode hygiene — strips zero-width spaces / joiners, BOM, soft hyphens, bidi controls, variation selectors; applies NFKC normalisation and homoglyph folding only between Latin letters (so genuine Cyrillic text is never mangled).
  • Layer B · Statistical rewrite — swaps "green-list" tokens for synonyms using built-in English and Russian dictionaries, or back-translates through an Ollama-compatible LLM endpoint for higher-quality rewrites.
  • Layer C · Vendor detectors — best-effort heuristics for Claude, Gemini/SynthID, OpenAI, DeepSeek ( tag leak + fullwidth-ASCII CJK fingerprint), Grok (emoji burst + em-dash cluster), and Mistral (chat-template marker leak) invisible-carrier patterns. These are heuristic because the underlying schemes are key-based and not publicly verifiable.

Markdown

20+ toggleable transforms; preserves fenced code blocks (only forced invisible-character cleanup runs inside them); strips AI-specific artifacts (frontmatter, "As an AI" lines, emoji-driven sign-offs) when enabled.

Metadata

Byte-level, pixel-preserving cleaners:

  • JPEG — segment parser (APP0–APP15, COM); strips EXIF / XMP / IPTC / ICC / C2PA
  • PNG — chunk filter (tEXt, zTXt, iTXt, eXIf); preserves IHDR / IDAT / IEND
  • PDF — PdfPig rebuild without document info / XMP metadata
  • DOCX / PPTX / XLSX — OpenXML core/extended/custom properties; DOCX also strips revision history; PPTX also strips per-slide comments + presentation-wide author list; XLSX also strips workbook-wide author list + per-worksheet threaded comments
  • HTML — <meta name="generator|author"> + <!-- comments --> + tracking scripts
  • EPUB — OCF zip-rewrite; strips OPF <dc:*> (except dc:identifier kept as a fresh UUID) and <meta> entries; preserves every other entry byte-for-byte (XHTML / CSS / images / NCX)
  • RTF — character-stream parser; strips \author / \company / \manager / \generator / \doccomm / \title / \subject / \keywords / \category / \comment / \hlinkbase / \operator / \version / \edmins / \nofpages / \nofwords / \nofchars / \nofcharsws / \id plus compound time-table entries \creatim / \revtbl / \printim / \buptim (each followed by sub-control words like \yr\mo\dy\hr\min\sec); preserves font table, colour table, stylesheet, headers / footers, and body text byte-for-byte
  • MP4 / MOV / M4V / M4A / 3GP — ISOBMFF box walker; strips moov.udta (the ©xyz GPS atom, ©mak / ©mod / ©swr device make / model / software, ©day / ©nam / ©ART / ©cmt authorship) and the QuickTime meta keys / ilst (title / author / album / genre); also strips EXIF / XMP / ICC from any meta; preserves mvhd / trak / edts / mvex and the mdat bitstream byte-for-byte (with largesize honour)

Image

load → mask → resize → infer → blend → save

  • Mask generation — alpha + colour-frequency heuristics with connected-component extraction (auto-detects logos, text overlays, corner watermarks)
  • Manual mask — supply your own mask image or coordinates
  • Inference — big-lama ONNX model; degrades gracefully if the model is missing (region detection still runs, the source image is copied unchanged)
  • Blending — alpha-blend the inpainted patch back to keep edges anti-aliased

🛠️ CLI reference

The produced executable is named watermarkremover. During development use dotnet run --project src/WatermarkRemover.CLI -- ….

Global options (available on every command)

FlagDescription
--jsonEmit structured JSON instead of pretty text
--verbose / -vVerbose logging (Debug+)
--dry-runShow what would happen without touching files
--output / -oWrite result to a path instead of stdout
--config / -cPath to a custom config.yaml
--version / -VPrint the assembly version and exit (short-circuits before config / logging are loaded). A bare -v is also accepted; note that Spectre intercepts -v even when paired with other args, so use --verbose (long form) for verbose logging.

Commands

CommandPurpose
clean-textClean plain text (Layers A / B / C).
clean-markdownClean markdown, preserving fenced code blocks.
clean-fileStrip metadata from files (single / directory / --recursive).
clean-imageRemove visual watermarks via mask + LaMa inpainting.
clean-allAuto-route a path — dispatches each file to text / markdown / metadata pipeline by extension (use --recursive, --dry-run).
detect-textDetect (do not remove) watermark signatures in text.
detect-markdownDetect AI artifacts in markdown.
detect-watermarkDetect visual watermark regions in an image.
inspect-fileReport all metadata found in a file.
download-modelDownload & extract the LaMa ONNX inpainting model.
serveHost the HTTP API (ASP.NET Core Minimal API).
serve-mcpHost the MCP server. --transport stdio (default, local agents) or --transport http (Streamable HTTP, remote).
completionsEmit a shell completion script (bash, zsh, powershell, fish).

Examples

# Clean text from an argument, a file, or stdin
dotnet run --project src/WatermarkRemover.CLI -- clean-text "Some AI text…"
cat article.txt | dotnet run --project src/WatermarkRemover.CLI -- clean-text --json

# Enable Layer B statistical (green-list) rewriting
dotnet run --project src/WatermarkRemover.CLI -- clean-text -i in.txt -o out.txt --statistical

# Clean markdown but keep everything except AI artifacts (default) or strip everything
dotnet run --project src/WatermarkRemover.CLI -- clean-markdown -i README.md --strip-all

# Strip metadata from a whole folder of files recursively
dotnet run --project src/WatermarkRemover.CLI -- clean-file ./docs --recursive

# Point one command at a mixed directory: .md → markdown, .jpg → metadata,
# .txt → text, .bin → skipped. Use --dry-run to preview.
dotnet run --project src/WatermarkRemover.CLI -- clean-all ./mixed-dir --recursive

# Inspect a file's metadata (JSON output)
dotnet run --project src/WatermarkRemover.CLI -- inspect-file photo.jpg --json

# Remove a visual watermark (auto-detected mask)
dotnet run --project src/WatermarkRemover.CLI -- clean-image logo.png -o clean.png

# Russian: synonym-aware rewriter is on by default with --statistical
dotnet run --project src/WatermarkRemover.CLI -- clean-text "Это значимый результат." --statistical
# → "Это существенный результат."

🌐 HTTP API (serve)

watermarkremover serve --port 5080 --api-key s3cret
  • Binds http://0.0.0.0:5080 by default (override with --host, --port).
  • Rate limiting: fixed window, 100 requests / minute / IP (HTTP 429 when exceeded).
  • Auth: when --api-key is supplied, every endpoint except /health requires the X-API-Key header. Omit --api-key to run open.

Endpoints

Method & pathBodyReturns
POST /clean/text{ "text": "…" }TextCleanResult JSON
POST /detect/text{ "text": "…" }WatermarkMatch[] JSON
POST /clean/markdown{ "markdown": "…", "stripAll": false }MarkdownCleanResult JSON
POST /detect/markdown{ "markdown": "…", "stripAll": false }AiArtifact[] JSON
POST /clean/filemultipart file uploadcleaned file (octet-stream)
POST /inspect/filemultipart file uploadMetadataEntry[] JSON
POST /clean/imagemultipart image uploadcleaned image
POST /detect/imagemultipart image uploadDetectedRegion[] JSON
GET /health—{ "status": "ok" }
GET /swagger—interactive Swagger UI (HTML)
GET /swagger/v1/swagger.json—OpenAPI 3.0 spec (JSON)

Example

curl -s -X POST http://localhost:5080/clean/text \
  -H "Content-Type: application/json" -H "X-API-Key: s3cret" \
  -d '{"text":"Пример текста"}'```

---

## 🤖 MCP server (`serve-mcp`)

`serve-mcp` exposes the full pipeline as
[Model Context Protocol](https://modelcontextprotocol.io/) tools so any
MCP-compatible agent (Claude Code, OpenCode, MiniMax Code, VS Code,
Cursor, Continue, …) can call `clean_text`, `clean_markdown`,
`clean_file`, `clean_image`, `detect_text`, `detect_markdown`,
`inspect_file`, and `detect_watermark` directly — no shell-out to the
CLI. Built on the official [`ModelContextProtocol` C#
SDK](https://github.com/modelcontextprotocol/csharp-sdk).

```bash
# stdio (default) — local agents (Claude Code, OpenCode, MiniMax Code, Cursor, Continue)
watermarkremover serve-mcp
# register with Claude Code
claude mcp add watermarkremover -- watermarkremover serve-mcp
# OpenCode — the project ships `.opencode/opencode.jsonc` pre-wired
# with a `watermarkremover` MCP entry (default `enabled: false`).
# Install the binary, flip the flag, restart OpenCode. Three slash
# commands (`/wr-clean-text`, `/wr-clean-file`, `/wr-detect`) appear
# automatically — no extra wiring. See docs/MCP.md → OpenCode.

# Streamable HTTP (stateless) — remote agents / Docker
watermarkremover serve-mcp --transport http --port 5090 --api-key s3cret

Flags: --transport <stdio\|http> (default stdio), -H|--host, -p|--port (default 5090), --api-key, --rate-limit, --rate-window. Configurable via the mcp: section in config.yaml — see docs/CONFIGURATION.md. The stdio transport routes all logging to stderr so the JSON-RPC stream on stdout stays clean, per the MCP stdio spec.

Cursor / Continue users get an npm wrapper. The project ships a zero-dependency npm package at @watermarkremover/mcp that downloads the platform-appropriate release binary on npm install and exposes npx -y @watermarkremover/mcp as a drop-in mcpServers entry. No manual dotnet install, no PATH tweaks — just paste the snippet from docs/MCP.md into ~/.cursor/mcp.json or ~/.continue/config.json.


> **Full reference:** [📘 docs/MCP.md](./docs/MCP.md) — architecture diagram, all 8
> tool schemas (request/response JSON), transport options, configuration keys, and
> install recipes for Claude Code, OpenCode, MiniMax Code, Cursor, Continue, and
> Docker. Claude Code-specific install paths and the optional
> `UserPromptSubmit` auto-clean hook live in
> [🪝 docs/CLAUDE-CODE.md](./docs/CLAUDE-CODE.md).

---

## 🧠 Agent skills

The [`skills/`](./skills/) directory ships **drop-in skill packages**
that teach AI coding assistants *when* and *how* to use
WatermarkRemover. Each skill is a self-contained folder with a
`SKILL.md` (the agent reads it) plus POSIX (`run.sh`) and Windows
(`run.ps1`) wrappers that pipe input through the CLI.

| Skill | CLI command | What it does |
|-------|-------------|--------------|
| `watermark-clean-text`     | `clean-text`     | Strip invisible characters and rewrite AI-sounding phrasing (EN + RU). |
| `watermark-clean-markdown` | `clean-markdown` | Strip AI signatures / frontmatter from Markdown, preserve fenced code. |
| `watermark-clean-file`     | `clean-file`     | Strip EXIF / XMP / IPTC / C2PA / XMP-pdf / DOCX core props / HTML meta. |
| `watermark-clean-image`    | `clean-image`    | Detect + inpaint a visual watermark with LaMa. |
| `watermark-detect`         | `detect-*`       | Read-only AI provenance check. |

**OpenCode users get the integration pre-wired.** The project ships
the master `watermark-remover` skill under
[`.opencode/skills/watermark-remover/SKILL.md`](./.opencode/skills/watermark-remover/SKILL.md)
plus three slash commands under
[`.opencode/commands/`](./.opencode/commands/) — `/wr-clean-text`,
`/wr-clean-file`, `/wr-detect` — so the agent learns the
integration as soon as you open the repo. The only step left is to
enable the MCP server entry in
[`.opencode/opencode.jsonc`](./.opencode/opencode.jsonc) after
installing the binary. See [`docs/MCP.md → OpenCode`](./docs/MCP.md#opencode).

**Claude Code users get a parallel pre-wired integration.** The
project ships the master `watermark-remover` skill under
[`.claude/skills/watermark-remover/SKILL.md`](./.claude/skills/watermark-remover/SKILL.md),
a drop-in `mcp-config.json`, and an optional `UserPromptSubmit`
hook at
[`.claude/skills/watermark-remover/hooks.json`](./.claude/skills/watermark-remover/hooks.json)
that auto-cleans text pasted into the prompt. The fastest install
is one line:

```bash
claude mcp add watermarkremover -- watermarkremover serve-mcp

Then restart Claude Code — the eight MCP tools appear and the project-local SKILL.md teaches the agent when to call them. See docs/CLAUDE-CODE.md for project-local mcp-config.json, global ~/.claude/settings.json merge, the auto-clean hook, and the troubleshooting table.

MiniMax Code users get a parallel pre-wired integration. The project ships a V1 local plugin package at minimax-code/watermark-remover/ — drop-in .minimax-plugin/plugin.json manifest, stdio MCP server entry in servers.mcp.json, and six skills under skills/ (master + five per-format). Copy the folder into MiniMax Code's plugin directory and toggle the plugin on:

# Linux / macOS
cp -R minimax-code/watermark-remover \
      ~/.local/share/MiniMax/plugins/watermark-remover
# Windows PowerShell
$dest = Join-Path $env:APPDATA 'MiniMax\plugins\watermark-remover'
Copy-Item -Recurse -Force minimax-code/watermark-remover $dest

The MCP server (watermarkremover serve-mcp) auto-starts as a stdio child process and the agent gets the same eight tools as the other agents — clean_text, clean_markdown, clean_file, clean_image, detect_text, detect_markdown, inspect_file, detect_watermark. See docs/MINIMAX-CODE.md for the full plugin reference, the HTTP-transport swap, the slash-command status, and the troubleshooting table.

VS Code users get a first-party extension. The project ships a dedicated VS Code extension at vscode/watermark-remover/ that adds three commands and two context-menu integrations: right-click a text selection → WatermarkRemover: Clean AI watermarks from selection; right-click a file in the Explorer → WatermarkRemover: Strip metadata from selected file(s); command palette → WatermarkRemover: Detect AI watermarks in selection. The extension is a thin client over the watermarkremover CLI — it does not re-implement any cleaning logic — and ships the same skills/ folder so AI agents running inside VS Code (Continue, Cline, …) learn the tool. Install from the Marketplace (search "WatermarkRemover", publisher techbuzzz) or via code --install-extension watermark-remover.vsix. See docs/VS-CODE.md for the full reference, configuration knobs, and the source-mode dotnet run recipe.

Install for your agent in one command:

./skills/install.sh --agent opencode          # ./.opencode/skills/watermarkremover/
./skills/install.sh --agent claude            # ~/.claude/skills/watermarkremover/
./skills/install.sh --agent minimax           # ~/.minimax/skills/watermarkremover/
./skills/install.sh --agent auto              # probe CWD for .opencode/.claude/.minimax
./skills/install.sh --dry-run                 # print what would happen

The installer scripts (install.sh + install.ps1) and the C# SkillsInstallerTargetResolver (in WatermarkRemover.CLI/Infrastructure/) share the same resolution rules — unit-tested for every agent name and every environment override.

Full reference: 📘 docs/SKILLS.md — resolution matrix, per-skill deep-dive, MCP-vs-skill integration, troubleshooting.


🐳 Docker

Pre-built images will be published to GHCR / Docker Hub after the first release; until then, build locally:

docker build -t watermarkremover .
docker run --rm -p 5080:5080 watermarkremover
# or with API-key auth + a model volume:
docker run --rm -p 5080:5080 \
  -e WATERMARKREMOVER_API_KEY=s3cret \
  -v $(pwd)/models:/app/models \
  watermarkremover

# MCP Streamable HTTP transport (stateless JSON-RPC for remote agents)
docker run --rm -p 5090:5090 \
  -e WATERMARKREMOVER_API_KEY=s3cret \
  watermarkremover serve-mcp --transport http --host 0.0.0.0 --port 5090 --api-key s3cret

docker-compose.yml ships two long-running services plus a profile-gated one-shot:

docker compose up                # HTTP API on :5080 (default)
docker compose up mcp            # MCP Streamable HTTP on :5090
docker compose up --detach mcp watermarkremover  # both side by side
docker compose run --rm clean clean-text \
  --input /data/in.txt --output /data/out.txt   # one-shot CLI

The Dockerfile is multi-stage, runs as a non-root user, and ships with a HEALTHCHECK that hits /health. Both 5080 and 5090 are EXPOSEd so either service can be published via the usual docker run -p syntax. The release workflow (.github/workflows/release.yml) smoke-tests the serve-mcp sub-command on every RID before publishing the binary, so a regression that accidentally trims the ModelContextProtocol SDK out of the single-file bundle fails the build. Full Docker / Compose reference: docs/MCP.md → Docker.


⚙️ Configuration

Copy src/config.yaml and edit it. Resolution order:

  1. --config <path> (CLI flag)
  2. ./config.yaml in the working directory
  3. config.yaml next to the executable
  4. Built-in defaults

CLI flags always override config values. See the Configuration reference for every key.


🧪 Build & test

dotnet build            # 0 warnings, 0 errors (warnings-as-errors on all src projects)
dotnet test             # 62 tests across Text (35) / Metadata (18) / Image (9)

Continuous integration runs on every push / PR to main across Ubuntu + Windows runners — see .github/workflows/build-and-test.yml. Coverage is collected in cobertura format and uploaded as a workflow artifact.


🗺️ Solution layout

WatermarkRemover.sln
├── src/
│   ├── WatermarkRemover.Core        # Models, interfaces, configuration, DI contracts
│   ├── WatermarkRemover.Text        # Layer A (Unicode) / B (statistical) / C (vendor) + Markdown
│   ├── WatermarkRemover.Metadata    # JPEG / PNG / WebP / TIFF / HEIF / AVIF / PDF / DOCX / PPTX / XLSX / HTML / EPUB / RTF metadata cleaners
│   ├── WatermarkRemover.Image       # Mask generation + LaMa ONNX inpainting pipeline
│   └── WatermarkRemover.CLI         # Spectre.Console CLI + ASP.NET Core HTTP API (serve)
└── src/tests/
    ├── WatermarkRemover.Text.Tests       (35 tests)
    ├── WatermarkRemover.Metadata.Tests   (18 tests)
    └── WatermarkRemover.Image.Tests       (9 tests)

🇷🇺 Поддержка русского языка / Russian language support

Приложение полностью поддерживает русский язык:

  • Слой A (Unicode) одинаково безопасно очищает латиницу и кириллицу. Нормализация омоглифов (похожих символов) срабатывает только когда подозрительный символ стоит между латинскими буквами, поэтому настоящие русские слова (например, «Привет мир») никогда не портятся.
  • Слой B (статистический рерайт) содержит встроенный русский словарь синонимов (SynonymDictionary) — например, «значимый» → «существенный», «использовать» → «применять». Включается флагом --statistical.
  • Слой C (детекторы вендоров) и очистка метаданных не зависят от языка.
  • CLI и HTTP API принимают и корректно обрабатывают текст в кодировке UTF-8 на русском языке.

Пример:

dotnet run --project src/WatermarkRemover.CLI -- clean-text "Это значимый результат." --statistical
# → "Это существенный результат."

Соответствующие юнит-тесты (StatisticalWatermarkRewriterTests, UnicodeHygieneCleanerTests, MarkdownCleanerTests, VendorDetectorTests) проверяют сохранность и корректную обработку русского текста.


📚 Documentation

  • 📘 docs/FAQ.md — frequently asked questions
  • ⚖️ docs/COMPARISON.md — how this tool differs from exiftool, mat2, exiv2, sd-webui-watermark, etc.
  • 🏛️ docs/ARCHITECTURE.md — module boundaries, data flow, extension points
  • 🌐 docs/WEB-UI.md — the plug-and-play Astro web UI
  • ⚙️ docs/CONFIGURATION.md — every config.yaml key explained
  • 🚀 docs/ci-release.md — how the release pipeline works
  • 🤖 docs/MCP.md — serve-mcp for Claude Code, OpenCode, MiniMax Code, Cursor, Continue (MCP server architecture, tool schemas, install recipes)
  • 📦 npm/watermarkremover-mcp/ — @watermarkremover/mcp zero-dependency npm wrapper for Cursor, Continue, and any other MCP host that prefers npm-based registration (downloads the platform-appropriate release binary on npm install)
  • 🪝 docs/CLAUDE-CODE.md — Claude Code-specific install (project-local mcp-config.json, ~/.claude/settings.json merge, claude mcp add one-liner) and the optional UserPromptSubmit hook for auto-cleaning pasted text
  • 🧩 docs/MINIMAX-CODE.md — MiniMax Code V1 local plugin (minimax-code/watermark-remover/), install paths per OS, MCP transport swap, slash-command status, troubleshooting
  • 🆚 docs/VS-CODE.md — VS Code first-party extension (vscode/watermark-remover/), three commands (cleanText, cleanFile, detectText), right-click context menus, bundled skills, dotnet run source-mode recipe
  • 🧠 docs/SKILLS.md — drop-in agent skills (skills/) for OpenCode, Claude Code, MiniMax Code, Cursor, Continue (install, resolution rules, per-skill reference)
  • 🧭 BACKLOG.md — prioritised roadmap
  • 📝 TODO.md — current sprint

🗓️ Roadmap

The active roadmap lives in BACKLOG.md. Highlights:

P0 — Release readiness

  • CI matrix on Windows + Linux
  • Release workflow (self-contained binaries for 4 RIDs)
  • Multi-stage Docker image
  • Issue / PR templates, CODEOWNERS, CODE_OF_CONDUCT, SECURITY, CHANGELOG

P1 — Core features (v1.0)

  • More metadata formats (MP4) — WebP, HEIF, AVIF, EPUB, RTF landed (TIFF retired with the SkiaSharp migration)
  • DeepSeek / Grok / Mistral vendor detectors
  • Synonym dictionary: EN → 400+, RU → 200+
  • Image batch processing + GPU inference
  • OpenAPI / Swagger UI for the HTTP API

P2 — Platform & UX

  • clean-all auto-routing command
  • Shell completion (PowerShell / bash / zsh / fish)
  • Web UI (vanilla JS drag-and-drop)
  • Configurable rate-limit + CORS + /metrics

P3 — Quality & reliability

  • CLI integration test project (WebApplicationFactory)
  • Property-based tests (FsCheck) for Unicode hygiene
  • BenchmarkDotNet regression suite in CI
  • Polly resilience for LLM back-translation

P4 — Ecosystem

  • Homebrew / Scoop / Winget / APT packages
  • Python + Node.js bindings
  • Browser + VS Code extensions
  • NuGet packages

See BACKLOG.md for the full list with status indicators.


🤝 Contributing

We love PRs. Start with CONTRIBUTING.md for setup, code style, commit conventions, and the review process. The short version:

  1. Fork & branch from main (feat/<short-topic> or fix/<short-topic>).
  2. dotnet build and dotnet test must pass — warnings are errors.
  3. Add a unit test for any new behavior; keep the test pyramid balanced.
  4. Run dotnet format before pushing.
  5. Open a PR using the template; reference any related issue with Closes #N.

First-time contributors: look for issues tagged good first issue.

Please read our Code of Conduct — be kind, be constructive.


🔐 Security

For vulnerabilities, please do not file a public issue — see SECURITY.md for the responsible-disclosure channel and our responsible-use policy.


📜 License

MIT — Copyright (c) 2026 Victor Buzin.


💖 Acknowledgements


If WatermarkRemover saved you time, a ⭐ is the easiest way to say thanks.
agent-skills
ai
ai-agents
ai-watermark-remover
c2pa
cli-tool
csharp
dotnet
dotnet-10
self-hosted
watermark
watermark-remover