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.
See the codeA cross-platform, single-binary .NET 10 toolkit for stripping AI-provenance watermarks from text, markdown, files (metadata), and images — with first-class Russian support.
🐛 Report a bug · ✨ Request a feature · ❓ FAQ · 💬 Discussions
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:
⚠️ 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.
serve)serve-mcp)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 "Helloworld" # 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/
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.
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.
| Platform | Architecture | Asset |
|---|---|---|
| 🐧 Linux | x64 | watermarkremover-linux-x64.zip |
| 🐧 Linux | ARM64 | watermarkremover-linux-arm64.zip |
| 🪟 Windows | x64 | watermarkremover-win-x64.zip |
| 🍎 macOS | x64 | watermarkremover-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)
# 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.
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/
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.
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 "Helloworld" # → 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.
The text pipeline runs three independent layers in order. Each can be toggled via
config.yaml or the CLI.
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.
Byte-level, pixel-preserving cleaners:
PdfPig rebuild without document info / XMP metadata<meta name="generator|author"> + <!-- comments --> + tracking scripts<dc:*> (except dc:identifier kept as a fresh UUID) and <meta> entries; preserves every other entry byte-for-byte (XHTML / CSS / images / NCX)\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-bytemoov.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)load → mask → resize → infer → blend → save
big-lama ONNX model; degrades gracefully if the model is missing
(region detection still runs, the source image is copied unchanged)The produced executable is named watermarkremover. During development use
dotnet run --project src/WatermarkRemover.CLI -- ….
| Flag | Description |
|---|---|
--json | Emit structured JSON instead of pretty text |
--verbose / -v | Verbose logging (Debug+) |
--dry-run | Show what would happen without touching files |
--output / -o | Write result to a path instead of stdout |
--config / -c | Path to a custom config.yaml |
--version / -V | Print 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. |
| Command | Purpose |
|---|---|
clean-text | Clean plain text (Layers A / B / C). |
clean-markdown | Clean markdown, preserving fenced code blocks. |
clean-file | Strip metadata from files (single / directory / --recursive). |
clean-image | Remove visual watermarks via mask + LaMa inpainting. |
clean-all | Auto-route a path — dispatches each file to text / markdown / metadata pipeline by extension (use --recursive, --dry-run). |
detect-text | Detect (do not remove) watermark signatures in text. |
detect-markdown | Detect AI artifacts in markdown. |
detect-watermark | Detect visual watermark regions in an image. |
inspect-file | Report all metadata found in a file. |
download-model | Download & extract the LaMa ONNX inpainting model. |
serve | Host the HTTP API (ASP.NET Core Minimal API). |
serve-mcp | Host the MCP server. --transport stdio (default, local agents) or --transport http (Streamable HTTP, remote). |
completions | Emit a shell completion script (bash, zsh, powershell, fish). |
# 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
# → "Это существенный результат."
serve)watermarkremover serve --port 5080 --api-key s3cret
http://0.0.0.0:5080 by default (override with --host, --port).--api-key is supplied, every endpoint except /health requires the
X-API-Key header. Omit --api-key to run open.| Method & path | Body | Returns |
|---|---|---|
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/file | multipart file upload | cleaned file (octet-stream) |
POST /inspect/file | multipart file upload | MetadataEntry[] JSON |
POST /clean/image | multipart image upload | cleaned image |
POST /detect/image | multipart image upload | DetectedRegion[] JSON |
GET /health | — | { "status": "ok" } |
GET /swagger | — | interactive Swagger UI (HTML) |
GET /swagger/v1/swagger.json | — | OpenAPI 3.0 spec (JSON) |
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.
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.
Copy src/config.yaml and edit it. Resolution order:
--config <path> (CLI flag)./config.yaml in the working directoryconfig.yaml next to the executableCLI flags always override config values. See the Configuration reference for every key.
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.
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)
Приложение полностью поддерживает русский язык:
SynonymDictionary) — например, «значимый» → «существенный»,
«использовать» → «применять». Включается флагом --statistical.Пример:
dotnet run --project src/WatermarkRemover.CLI -- clean-text "Это значимый результат." --statistical
# → "Это существенный результат."
Соответствующие юнит-тесты (StatisticalWatermarkRewriterTests, UnicodeHygieneCleanerTests,
MarkdownCleanerTests, VendorDetectorTests) проверяют сохранность и корректную
обработку русского текста.
exiftool, mat2, exiv2, sd-webui-watermark, etc.config.yaml key explainedserve-mcp for Claude Code, OpenCode, MiniMax Code, Cursor, Continue (MCP server architecture, tool schemas, install recipes)@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)mcp-config.json, ~/.claude/settings.json merge, claude mcp add one-liner) and the optional UserPromptSubmit hook for auto-cleaning pasted textminimax-code/watermark-remover/), install paths per OS, MCP transport swap, slash-command status, troubleshootingvscode/watermark-remover/), three commands (cleanText, cleanFile, detectText), right-click context menus, bundled skills, dotnet run source-mode recipeskills/) for OpenCode, Claude Code, MiniMax Code, Cursor, Continue (install, resolution rules, per-skill reference)The active roadmap lives in BACKLOG.md. Highlights:
P0 — Release readiness
P1 — Core features (v1.0)
P2 — Platform & UX
clean-all auto-routing command/metricsP3 — Quality & reliability
WebApplicationFactory)P4 — Ecosystem
See BACKLOG.md for the full list with status indicators.
We love PRs. Start with CONTRIBUTING.md for setup, code style, commit conventions, and the review process. The short version:
main (feat/<short-topic> or fix/<short-topic>).dotnet build and dotnet test must pass — warnings are errors.dotnet format before pushing.Closes #N.First-time contributors: look for issues tagged
good first issue.
Please read our Code of Conduct — be kind, be constructive.
For vulnerabilities, please do not file a public issue — see SECURITY.md for the responsible-disclosure channel and our responsible-use policy.
MIT — Copyright (c) 2026 Victor Buzin.
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.
See the codeA cross-platform, single-binary .NET 10 toolkit for stripping AI-provenance watermarks from text, markdown, files (metadata), and images — with first-class Russian support.
🐛 Report a bug · ✨ Request a feature · ❓ FAQ · 💬 Discussions
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:
⚠️ 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.
serve)serve-mcp)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 "Helloworld" # 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/
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.
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.
| Platform | Architecture | Asset |
|---|---|---|
| 🐧 Linux | x64 | watermarkremover-linux-x64.zip |
| 🐧 Linux | ARM64 | watermarkremover-linux-arm64.zip |
| 🪟 Windows | x64 | watermarkremover-win-x64.zip |
| 🍎 macOS | x64 | watermarkremover-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)
# 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.
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/
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.
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 "Helloworld" # → 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.
The text pipeline runs three independent layers in order. Each can be toggled via
config.yaml or the CLI.
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.
Byte-level, pixel-preserving cleaners:
PdfPig rebuild without document info / XMP metadata<meta name="generator|author"> + <!-- comments --> + tracking scripts<dc:*> (except dc:identifier kept as a fresh UUID) and <meta> entries; preserves every other entry byte-for-byte (XHTML / CSS / images / NCX)\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-bytemoov.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)load → mask → resize → infer → blend → save
big-lama ONNX model; degrades gracefully if the model is missing
(region detection still runs, the source image is copied unchanged)The produced executable is named watermarkremover. During development use
dotnet run --project src/WatermarkRemover.CLI -- ….
| Flag | Description |
|---|---|
--json | Emit structured JSON instead of pretty text |
--verbose / -v | Verbose logging (Debug+) |
--dry-run | Show what would happen without touching files |
--output / -o | Write result to a path instead of stdout |
--config / -c | Path to a custom config.yaml |
--version / -V | Print 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. |
| Command | Purpose |
|---|---|
clean-text | Clean plain text (Layers A / B / C). |
clean-markdown | Clean markdown, preserving fenced code blocks. |
clean-file | Strip metadata from files (single / directory / --recursive). |
clean-image | Remove visual watermarks via mask + LaMa inpainting. |
clean-all | Auto-route a path — dispatches each file to text / markdown / metadata pipeline by extension (use --recursive, --dry-run). |
detect-text | Detect (do not remove) watermark signatures in text. |
detect-markdown | Detect AI artifacts in markdown. |
detect-watermark | Detect visual watermark regions in an image. |
inspect-file | Report all metadata found in a file. |
download-model | Download & extract the LaMa ONNX inpainting model. |
serve | Host the HTTP API (ASP.NET Core Minimal API). |
serve-mcp | Host the MCP server. --transport stdio (default, local agents) or --transport http (Streamable HTTP, remote). |
completions | Emit a shell completion script (bash, zsh, powershell, fish). |
# 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
# → "Это существенный результат."
serve)watermarkremover serve --port 5080 --api-key s3cret
http://0.0.0.0:5080 by default (override with --host, --port).--api-key is supplied, every endpoint except /health requires the
X-API-Key header. Omit --api-key to run open.| Method & path | Body | Returns |
|---|---|---|
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/file | multipart file upload | cleaned file (octet-stream) |
POST /inspect/file | multipart file upload | MetadataEntry[] JSON |
POST /clean/image | multipart image upload | cleaned image |
POST /detect/image | multipart image upload | DetectedRegion[] JSON |
GET /health | — | { "status": "ok" } |
GET /swagger | — | interactive Swagger UI (HTML) |
GET /swagger/v1/swagger.json | — | OpenAPI 3.0 spec (JSON) |
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.
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.
Copy src/config.yaml and edit it. Resolution order:
--config <path> (CLI flag)./config.yaml in the working directoryconfig.yaml next to the executableCLI flags always override config values. See the Configuration reference for every key.
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.
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)
Приложение полностью поддерживает русский язык:
SynonymDictionary) — например, «значимый» → «существенный»,
«использовать» → «применять». Включается флагом --statistical.Пример:
dotnet run --project src/WatermarkRemover.CLI -- clean-text "Это значимый результат." --statistical
# → "Это существенный результат."
Соответствующие юнит-тесты (StatisticalWatermarkRewriterTests, UnicodeHygieneCleanerTests,
MarkdownCleanerTests, VendorDetectorTests) проверяют сохранность и корректную
обработку русского текста.
exiftool, mat2, exiv2, sd-webui-watermark, etc.config.yaml key explainedserve-mcp for Claude Code, OpenCode, MiniMax Code, Cursor, Continue (MCP server architecture, tool schemas, install recipes)@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)mcp-config.json, ~/.claude/settings.json merge, claude mcp add one-liner) and the optional UserPromptSubmit hook for auto-cleaning pasted textminimax-code/watermark-remover/), install paths per OS, MCP transport swap, slash-command status, troubleshootingvscode/watermark-remover/), three commands (cleanText, cleanFile, detectText), right-click context menus, bundled skills, dotnet run source-mode recipeskills/) for OpenCode, Claude Code, MiniMax Code, Cursor, Continue (install, resolution rules, per-skill reference)The active roadmap lives in BACKLOG.md. Highlights:
P0 — Release readiness
P1 — Core features (v1.0)
P2 — Platform & UX
clean-all auto-routing command/metricsP3 — Quality & reliability
WebApplicationFactory)P4 — Ecosystem
See BACKLOG.md for the full list with status indicators.
We love PRs. Start with CONTRIBUTING.md for setup, code style, commit conventions, and the review process. The short version:
main (feat/<short-topic> or fix/<short-topic>).dotnet build and dotnet test must pass — warnings are errors.dotnet format before pushing.Closes #N.First-time contributors: look for issues tagged
good first issue.
Please read our Code of Conduct — be kind, be constructive.
For vulnerabilities, please do not file a public issue — see SECURITY.md for the responsible-disclosure channel and our responsible-use policy.
MIT — Copyright (c) 2026 Victor Buzin.