
watermarks-remover v0.5.0
एक गोपनीयता-प्रथम ऐप जो आपकी स्वयं की सामग्री से AI वॉटरमार्क हटाता है।
_ _ _ ____ ___ ____ ____ _ _ ____ ____ _ _ ____ ____ ____ _ _ ____ _ _ ____ ____
| | | |__| | |___ |__/ |\/| |__| |__/ |_/ [__ __ |__/ |___ |\/| | | | | |___ |__/
|_|_| | | | |___ | \ | | | | | \ | \_ ___] | \ |___ | | |__| \/ |___ | \
watermarks-remover
एजेंट स्किल + stdlib Python सेवा, जो टेक्स्ट और फ़ाइलों से बहु-विक्रेता AI प्रोवेनेंस मार्क्स हटाती है — उस कंटेंट पर गोपनीयता और स्वच्छता के लिए जिसका आप स्वामी हैं। यह स्किल एक पतला क्लाइंट है: यह मशीनरी को HTTP के ज़रिए चलाता है, इसलिए एजेंट होस्ट को Python की आवश्यकता नहीं है।
| परत | लक्ष्य | कैसे |
|---|---|---|
| A | अदृश्य Unicode, विचित्र स्पेस, bidi, टैग अक्षर | नियतात्मक Python स्क्रिप्ट्स |
| B | सांख्यिकीय (टोकन-सैंपलिंग) टेक्स्ट वॉटरमार्क्स | एजेंट रीराइट + वैकल्पिक rewrite_text.py हुक |
| फ़ाइलें | C2PA / EXIF / XMP / doc props | PNG, JPEG, WebP, AVIF, HEIC, BMP, GIF, TIFF, SVG, PDF, DOCX, XLSX, PPTX, EPUB, ODT, HTML, Markdown, MP4/MOV/M4A/M4V, WAV, MP3, FLAC |
विक्रेता / इकोसिस्टम (क्लास-स्तर): Claude, Gemini / SynthID-Text, OpenAI प्रोवेनेंस सतहें, open-LLM Kirchenbauer-शैली (ग्रीन-लिस्ट) और keyed-Gumbel / EXP (Aaronson) मार्क्स।
नवीनतम रिलीज़: v0.7.0
स्किल पथ: skills/remove-ai-marks/
सेवा पथ: service/
(माइग्रेशन: पहले remove-claude-marks; स्लैश उपनाम /remove-claude-marks अभी भी प्रलेखित है)
इंस्टॉल (एजेंट स्किल)
यह स्किल कोई कोड नहीं भेजती — यह सेवा को HTTP के ज़रिए कॉल करती है। स्किल (केवल markdown) इंस्टॉल करें और सेवा शुरू करें, फिर यदि http://127.0.0.1:8765 न हो तो WATERMARKS_SERVICE_URL सेट करें।
Claude Code में, सबसे तेज़ रास्ता बंडल किया गया plugin marketplace है — कोई क्लोन नहीं, और यह जगह पर ही अपडेट हो जाता है। बाकी हर जगह, एक इंस्टॉलर हर समर्थित होस्ट को कवर करता है (Python 3.10+ stdlib, कोई निर्भरता नहीं):```bash python3 install_skill.py --skill remove-ai-marks --target claude-code
| Host | Target | Lands in |
| --- | --- | --- |
| Claude Code (personal) | `--target claude-code` | `~/.claude/skills/<skill>` (honors `CLAUDE_CONFIG_DIR`) |
| Claude Code (project) | `--target claude-project --project-dir PATH` | `PATH/.claude/skills/<skill>` |
| Cowork, claude.ai, cloud sessions, routines | `--target cowork` | `dist/<skill>.zip` to upload under **Customize → Skills** |
| Cursor | `--target cursor` (default) | `~/.cursor/skills/<skill>` |
Shipped skills: `remove-ai-marks` (full, service-backed) and
`clean-user-facing-text` (text only, self-contained). `--list` prints them.
Existing installations are preserved unless you pass `--force`; replacement is
staged first and the previous install is kept as a uniquely named backup.
`--link` symlinks this checkout instead of copying, so edits are picked up
live. On Windows, use `py install_skill.py ...`; the `install-skill.sh` wrapper
is provided for macOS/Linux shells.
Before writing anything, the installer validates the skill against the
[Agent Skills](https://agentskills.io) packaging rules that claude.ai uploads
and the Skills API enforce: spec-only frontmatter (`name`, `description`,
`license`, `compatibility`, `metadata`, `allowed-tools`), a lowercase hyphenated
`name` of at most 64 characters matching the directory, a non-empty
`description` of at most 1024 characters. The Cowork bundle additionally has
to fit the 30 MB upload limit, which the packager enforces.
### Automatic cleaning via hook (deterministic)
A skill is an instruction: the model decides whether to invoke it, and the
model is the thing producing the marks. A **hook** is executed by the harness
on every matching tool call, cooperation not required. That makes the hook the
deterministic half of this workflow.
The plugin registers a `PostToolUse` hook on `Write|Edit|MultiEdit|NotebookEdit`
that runs [`service/scripts/hook_written_file.py`](https://github.com/guillaumemeyer/watermarks-remover/blob/main/service/scripts/hook_written_file.py)
against the file the agent just wrote. Two modes, matching the pre-commit
convention of check-by-default:
| Mode | Behaviour |
| --- | --- |
| `check` (default) | Reports provenance marks, leaves the file alone. Findings go to the model (exit 2), so it can offer to clean them. |
| `clean` | Strips the marks in place, then tells the model the file on disk changed. |
Set the mode from the plugin's settings (**Hook mode** in `/plugin manage`,
read by the hook as `CLAUDE_PLUGIN_OPTION_HOOK_MODE`), or with
`WATERMARKS_HOOK_MODE=clean` in the environment. The hook command deliberately
does **not** interpolate `${user_config.hook_mode}`: Claude Code refuses to run
a hook that references an option the user has never opened `/plugin manage` to
set — a declared `default` does not satisfy it — so interpolating it would mean
the hook silently never runs on a fresh install. Detection reuses `audit_lib`'s
`scan_file` / `is_actionable`, so the hook, the pre-commit gate, and the CI
SARIF export agree on what counts as actionable; cleaning shells out to
`clean_file.py`, so no cleaning logic is duplicated. `clean` mode writes to a
sibling temp file and swaps only on a real difference, so files that were
already clean keep their mtime and don't retrigger file watchers.
Without the plugin, wire it in `~/.claude/settings.json` (or a project
`.claude/settings.json`) yourself:```json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit|MultiEdit|NotebookEdit",
"hooks": [
{
"type": "command",
"command": "python3",
"args": ["/path/to/watermarks-remover/service/scripts/hook_written_file.py",
"--mode", "check"],
"timeout": 30
}
]
}
]
}
}
Windows पर, python3 को py से बदलें।
एक हुक क्या नहीं कर सकता। कोई भी हुक आपके पढ़ने से पहले सहायक के चैट संदेश को फिर से नहीं लिख सकता। Claude Code का Stop हुक last_assistant_message को केवल-पढ़ने के लिए प्राप्त करता है, और अंतिम प्रतिक्रियाओं के लिए कोई प्री-सेंड फ़िल्टर नहीं है — वही सीमा जो यह प्रोजेक्ट पहले से ही Cursor नियमों के लिए प्रलेखित करता है। इसलिए नियतात्मक गारंटी एजेंट द्वारा लिखी गई फ़ाइलों को कवर करती है, साथ ही git में जाने वाली किसी भी चीज़ के लिए pre-commit गेट। केवल चैट ट्रांसक्रिप्ट में मौजूद टेक्स्ट अभी भी स्किल वर्कफ़्लो पर निर्भर करता है, जो मॉडल-निर्देश-आधारित है और इसलिए best-effort है।
Claude Code प्लगइन (मार्केटप्लेस)
रिपॉज़िटरी एक Claude Code प्लगइन और एकल-प्लगइन मार्केटप्लेस (.claude-plugin/) भी है, इसलिए दोनों स्किल दो कमांड में इंस्टॉल और अपडेट हो जाते हैं, किसी क्लोन या स्क्रिप्ट की आवश्यकता नहीं:```
/plugin marketplace add guillaumemeyer/watermarks-remover
/plugin install watermarks-remover@watermarks-remover
कौशल फिर नेमस्पेस्ड लोड होते हैं: `/watermarks-remover:remove-ai-marks` और
`/watermarks-remover:clean-user-facing-text` (बिना नेमस्पेस वाला `/remove-ai-marks` भी
काम करता है जब कोई और उस नाम पर दावा नहीं करता)। `/plugin marketplace update
watermarks-remover` बाद के संस्करणों को खींच लाता है। यही CLI से भी काम करता है
`claude plugin marketplace add …` / `claude plugin install …` के साथ, और लोकल
चेकआउट से `owner/repo` के बजाय एक पाथ पास करके।
मेंटेनर्स: `make plugin-validate` दोनों मैनिफ़ेस्ट के विरुद्ध `claude plugin validate . --strict`
चलाता है; `tests/test_plugin_manifest.py` CLI की आवश्यकता के बिना उन्हीं फ़ाइलों को कवर करता है।
### Claude Code```bash
# Personal — available in all your projects
python3 install_skill.py --skill remove-ai-marks --target claude-code
# or: make install-claude-code-skill
# Project — commit .claude/skills/ to share it with the repo
python3 install_skill.py --skill remove-ai-marks --target claude-project \
--project-dir /path/to/project
# or: make install-claude-project-skill PROJECT=/path/to/project
Claude Code बिना रीस्टार्ट के व्यक्तिगत और प्रोजेक्ट skills उठा लेता है; /skills
दिखाता है कि क्या लोड हुआ। /remove-ai-marks से इनवोक करें या कहें "strip AI
watermarks / C2PA / Claude marks / SynthID-class text।" एक प्रोजेक्ट इंस्टॉल वही है
जिसे cloud sessions
पढ़ते हैं, क्योंकि वे रिपॉज़िटरी को क्लोन करते हैं और उसका .claude/skills/ लोड करते हैं।
Cowork (और claude.ai, cloud sessions, routines)
Cowork सत्र आपकी मशीन पर ~/.claude/skills नहीं पढ़ते — वे आपके claude.ai
खाते के लिए सक्षम किए गए skills लोड करते हैं, जो सत्र शुरू होने पर सिंक होते हैं।
इसलिए वहाँ बंडल अपलोड करके इंस्टॉल करें:```bash
python3 install_skill.py --skill remove-ai-marks --target cowork
writes dist/remove-ai-marks.zip (make package-cowork-skill)
फिर, Claude Desktop ऐप में, **Customize → Skills → Add** खोलें और
zip अपलोड करें (claude.ai पर वही skill settings भी काम करती हैं)। यह
bundle reproducible है और इसमें एक ही top-level `remove-ai-marks/` directory होती है जिसके
root में `SKILL.md` होता है, जो वही layout है जिसकी upload अपेक्षा करता है।
Service reachability यहाँ local install की तुलना में अधिक मायने रखती है: यह skill एक
thin HTTP client है, इसलिए session को `WATERMARKS_SERVICE_URL` तक पहुँचने में सक्षम होना चाहिए।
Cowork sessions जो आपकी मशीन पर locally चलते हैं, एक local `make serve` तक पहुँचते हैं;
cloud sessions और routines remotely चलते हैं और उन्हें वहाँ से reachable एक service URL चाहिए
(और उस पर `WATERMARKS_SERVER_API_KEY` set होना चाहिए)। यदि आप बिना किसी
service के skill चाहते हैं, तो इसके बजाय `clean-user-facing-text` अपलोड करें — यह text-only है और
अपनी scripts के साथ आता है:```bash
python3 install_skill.py --skill clean-user-facing-text --target cowork
Grok```bash
Grok Build / project-local
mkdir -p .grok/skills ln -sfn "$(pwd)/skills/remove-ai-marks" .grok/skills/remove-ai-marks
User-global Grok
mkdir -p ~/.grok/skills ln -sfn "$(pwd)/skills/remove-ai-marks" ~/.grok/skills/remove-ai-marks
### वैकल्पिक टेक्स्ट-केवल स्किल
[`skills/clean-user-facing-text/`](https://github.com/guillaumemeyer/watermarks-remover/blob/main/skills/clean-user-facing-text) अधिकृत पांडुलिपियों, दस्तावेज़ीकरण और वेब कॉपी के लिए एक स्व-निहित स्किल है। यह इमेज, C2PA, सेवा और बाहरी-मॉडल टूलिंग को बाहर रखता है, और सेवा को कॉल करने के बजाय अपनी स्वयं की वेंडर की गई Layer A स्क्रिप्ट चलाता है।```bash
python3 install_skill.py --skill clean-user-facing-text --target claude-code
python3 install_skill.py --skill clean-user-facing-text --target cursor
कौशल आह्वान मॉडल-चयनित है। जो प्रोजेक्ट्स Cursor में इस वर्कफ़्लो को स्पष्ट रूप से अपनाते हैं, वे वैकल्पिक नियम भी कॉपी कर सकते हैं:```bash
mkdir -p /path/to/project/.cursor/rules
cp integrations/cursor/clean-user-facing-text.mdc
/path/to/project/.cursor/rules/clean-user-facing-text.mdc
सभी प्रोजेक्ट्स के लिए, उसी निर्देश को Cursor **User Rules** में डालें।
नियम निरंतरता में सुधार करते हैं लेकिन मॉडल निर्देश बने रहते हैं; Cursor अंतिम चैट प्रतिक्रियाओं के लिए
एक नियतात्मक प्री-सेंड फ़िल्टर उजागर नहीं करता है।
### सेवा प्रारंभ करें
सबसे तेज़ तरीका एक स्थानीय HTTP सर्वर है (केवल Python 3.10+ stdlib — कोई deps नहीं, कोई Docker नहीं):```bash
make serve # http://127.0.0.1:8765
# or directly:
python3 service/scripts/server.py --host 127.0.0.1 --port 8765
Windows (Docker के बिना)
Windows लॉगिन पर Docker के बिना सेवा को ऑटो-स्टार्ट करने के लिए docs/windows-autostart.md देखें।
पूरे इन्फ्रा (core + वैकल्पिक harness/heavy backends) के लिए, नीचे Docker / compose देखें।
वैकल्पिक सिस्टम टूल्स (मौजूद होने पर स्वतः उपयोग किए जाते हैं — core Docker इमेज में पहले से इंस्टॉल):
| Tool | Role |
|---|---|
c2patool | C2PA मैनिफेस्ट का निरीक्षण करें |
exiftool | अवशिष्ट मेटाडेटा स्ट्रिप (विशेष रूप से PDF) |
qpdf | संरचनात्मक PDF पुनर्निर्माण — वास्तविक PDF स्ट्रिप के लिए आवश्यक (नीचे देखें) |
Core स्क्रिप्ट्स को केवल Python 3.10+ stdlib की आवश्यकता है। Layer B मॉडल कॉल वैकल्पिक हैं।
त्वरित उपयोग (स्क्रिप्ट्स)```bash
SCRIPTS=service/scripts
Unified inspect / clean
python3 "$SCRIPTS/inspect_file.py" draft.md python3 "$SCRIPTS/clean_file.py" draft.md -o draft.cleaned.md python3 "$SCRIPTS/clean_file.py" photo.png -o photo.cleaned.png python3 "$SCRIPTS/clean_file.py" notes.docx -o notes.cleaned.docx
Text Layer A
python3 "$SCRIPTS/inspect_text.py" draft.md python3 "$SCRIPTS/clean_text.py" draft.md -o draft.cleaned.md --stats
Layer B rewrite hook (default: print prompt only — no model required)
python3 "$SCRIPTS/rewrite_text.py" draft.md --backend print-prompt --tactic paraphrase
Optional local Ollama (loopback only by default — remote endpoints require
WATERMARKS_REWRITE_ALLOW_REMOTE=1 or --allow-remote):
WATERMARKS_REWRITE_BACKEND=ollama WATERMARKS_REWRITE_MODEL=llama3.2 \
python3 "$SCRIPTS/rewrite_text.py" draft.md -o draft.rewritten.md
API keys are read from WATERMARKS_REWRITE_API_KEY only (never argv).
Images
python3 "$SCRIPTS/inspect_image.py" shot.png python3 "$SCRIPTS/clean_image.py" shot.png -o shot.cleaned.png
### टेक्स्ट टूल्स बाइनरी इनपुट अस्वीकार करते हैं
`inspect_text.py`, `clean_text.py` और `rewrite_text.py` टेक्स्ट पर काम करते हैं। किसी `.docx`, `.pdf` या इमेज पर इंगित किए जाने पर ये पहले संपीड़ित बाइट्स को डिकोड करके जो भी कोडपॉइंट्स निकलते थे, उन्हें रिपोर्ट करते थे — शोर जो सामग्री नहीं, बल्कि संपीड़न को ट्रैक करता है — और फिर `clean_text.py` उन विकृत बाइट्स को वापस लिख देता था, जिससे फ़ाइल नष्ट हो जाती थी। अब ये बाइनरी इनपुट अस्वीकार करते हैं और उस टूल का नाम बताते हैं जो इसे संभालता है:```bash
python3 "$SCRIPTS/inspect_text.py" report.docx
# refusing to treat report.docx as text: it looks like a ZIP container (DOCX, ODT, …).
# Use inspect_file.py / clean_file.py, which route by format,
# or pass --force-text to scan the raw bytes anyway.
पता लगाना magic number के साथ-साथ control-byte अनुपात से होता है, इसलिए UTF-8 के अलावा अन्य encodings में मौजूद text भी काम करता रहता है। --force-text इसे हर जगह override कर देता है।
अपरिचित formats कभी भी स्वतः साफ़ नहीं किए जाते
classify() उन bytes को unknown लेबल करता है जो किसी भी समर्थित text, image या container format से मेल नहीं खाते — यह अब "text" पर वापस नहीं गिरता। auto mode में clean_file.py ऐसी फ़ाइलों को अस्वीकार कर देता है (exit 2, कोई output नहीं लिखा जाता) बजाय उन्हें UTF-8 के रूप में decode करके विकृत bytes वापस लिखने के; --as text या --force-text स्पष्ट opt-ins हैं। inspect_file.py फ़ाइल को unknown के रूप में रिपोर्ट करता है (exit 0), और HTTP service /inspect का उत्तर kind: "unknown" के साथ देती है लेकिन अपरिचित formats के /clean को अस्वीकार करती है (400 — ज्ञात extension वाला filename भेजें, जैसे notes.txt)।
HTTP service
वही machinery एक stdlib HTTP service (service/scripts/server.py) के रूप में चलती है — यही interface है जिसका उपयोग skill करता है और जिस तरह कोई भी web app बिना vendoring के integrate कर सकता है:
| Method | Path | Body | Returns |
|---|---|---|---|
| GET | /health | — | {"ok": true, "version": ...} |
| GET | /capabilities | — | optional tools / backends usable (प्रत्येक tool version-probed है, केवल PATH पर मिला हुआ नहीं) |
| GET | /openapi.json | — | dynamically generated OpenAPI 3.0.3 spec |
| POST | /inspect | {"file": "<base64>", "name": "notes.md"} | {"ok", "kind", "suspicious", "report"} |
| POST | /detect | {"file": "<base64>", "name": "notes.txt"} | {"ok", "kind", "detections": [...]} |
| POST | /clean | {"file": "<base64>", "name": "notes.md", "options": {...}} | {"ok", "kind", "cleaned": "<base64>", "report"} |
| POST | /watermark | {"text": "...", "keys": [118, 504, ...], "options": {...}} or {"file": "<base64>", ...} | {"ok", "kind", "watermarked_text", "report": {"scheme_used", ...}} |
| POST | /inspect/batch | {"files": [{"file": "<base64>", "name": "notes.md"}, ...]} | {"ok", "results": [{"name", "ok", "kind", "suspicious", "report"}, ...]} |
| POST | /detect/batch | {"files": [{"file": "<base64>", "name": "notes.txt"}, ...]} | {"ok", "results": [{"name", "ok", "kind", "detections", "report"}, ...]} |
| POST | /clean/batch | {"files": [{"file": "<base64>", "name": "notes.md", "options": {...}}, ...]} | {"ok", "results": [{"name", "ok", "kind", "cleaned", "report"}, ...]} |
| POST | /watermark/batch | {"files": [{"text": "...", "keys": [...]}, {"file": "<base64>"}, ...]} | {"ok", "results": [{"name", "ok", "kind", "watermarked_text", "report": {"scheme_used", ...}}, ...]} |
Batch endpoints /inspect, /detect, /clean, और /watermark जैसी ही per-file pipeline को loop करते हैं, प्रति request WATERMARKS_MAX_BATCH_FILES फ़ाइलों तक सीमित (default 50)। एक malformed entry (खराब base64, अपरिचित option, अपरिचित format) उस entry के "ok": false के रूप में "error" string के साथ सामने आती है — यह कभी भी बाकी batch को abort नहीं करती।```bash
WM="http://127.0.0.1:8765"
curl -s "$WM/health" # {"ok": true, "version": "..."}
curl -s "$WM/openapi.json" # machine-readable OpenAPI 3.0.3 contract
curl -s -X POST "$WM/clean" -H 'Content-Type: application/json'
-d "{"file": "$(base64 < notes.md | tr -d '\n')", "name": "notes.md"}"
सेवा फ़ाइल नाम एक्सटेंशन और फिर मैजिक बाइट्स के आधार पर रूट करती है, इसलिए टेक्स्ट / इमेज / कंटेनर स्वतः पहचाने जाते हैं। हर अनुरोध पर `Authorization: Bearer <key>` की आवश्यकता के लिए `WATERMARKS_SERVER_API_KEY` सेट करें। डिफ़ॉल्ट रूप से केवल लूपबैक पर बाइंड (`--host` से ओवरराइड करें); विश्वसनीय नेटवर्क के लिए अभिप्रेत।
### वॉटरमार्क डिटेक्शन (`/detect` और `detect_before` / `detect_after`)
डिटेक्शन क्लीनिंग से एक अलग चरण है — सेवा कभी भी विक्रेता
API को कॉल नहीं करती जब तक आप इसे कहने के लिए न कहें:
- **`POST /detect`** किसी फ़ाइल पर कॉन्फ़िगर किए गए वॉटरमार्क डिटेक्टर चलाता है।
टेक्स्ट → विक्रेता डिटेक्टर + स्टाइलोमेट्री; इमेज → SynthID पिक्सेल स्कोर।
- **`/inspect`** एक ऑप्ट-इन `"detect": true` फ़्लैग स्वीकार करता है जो
टेक्स्ट रिपोर्ट में डिटेक्टर परिणाम जोड़ता है (और `suspicious` को फ़्लिप कर सकता है)।
- **`/clean`** `"detect_before"` / `"detect_after"` विकल्प स्वीकार करता है ताकि
इनपुट और क्लीन किए गए आउटपुट को स्कोर किया जा सके, जिससे आप माप सकें कि क्लीन ने
वास्तव में क्या बदला।
- **`/clean`** डिफ़ॉल्ट रूप से Layer A के बाद Layer B टेक्स्ट रीराइट चलाता है (यह
टेक्स्ट के लिए एक आवश्यक चरण है)। एक **`"strategy"`** विकल्प (एक क्रमबद्ध
`tactic@intensity` सूची, जैसे `"[email protected],[email protected]"`) स्ट्रैटेजी कॉन्फ़िग
फ़ाइल से डिफ़ॉल्ट को ओवरराइड करता है (नीचे देखें)। जब किसी चरण के लिए रीराइट
बैकएंड/मॉडल कॉन्फ़िगर नहीं होता, तो `/clean` 400 लौटाता है।
टेक्स्ट डिटेक्टर (देखें `/capabilities` → `text_detectors`):
टेक्स्ट डिटेक्टर (देखें `/capabilities` → `text_detectors`):
| डिटेक्टर | सक्रियकर्ता | नोट्स |
| --- | --- | --- |
| `markllm` | `MARKLLM_DIR` (होस्ट चेकआउट) | रिसर्च हार्नेस (KGW / SynthID स्कीम), केवल-समान-कॉन्फ़िग — विक्रेता ओरेकल नहीं। |
| `gumbel` | `WATERMARKS_GUMBEL_KEY` | कीड-गुम्बेल (Aaronson EXP) स्कीम का मॉडल-मुक्त समान-की रीप्ले (देखें `detect_gumbel.py`), केवल-stdlib — arbi-serve जैसे स्व-होस्टेड इंजन; केवल-समान-की, विक्रेता ओरेकल नहीं। |
| `claude-text` | — (प्लेसहोल्डर) | Anthropic ने वॉटरमार्क डिटेक्शन API की घोषणा की है; यह सीम तब सक्रिय होती है जब यह शिप होती है। |
इमेज स्कोरिंग: जब `WATERMARKS_SYNTHID_SCORER_URL` सेट होता है, तो सेवा
`wr-synthid-score` साइडकार (हेवी प्रोफ़ाइल) के माध्यम से इमेज स्कोर करती है; स्थानीय
`REVERSE_SYNTHID_DIR` के साथ यह सीधे चेकआउट का उपयोग करती है। डिटेक्शन
फेल-सॉफ्ट है: अनकॉन्फ़िगर, टाइम-आउट, या त्रुटिग्रस्त डिटेक्टर
`{"available": false, "error": ...}` रिपोर्ट करते हैं और क्लीनिंग को कभी ब्लॉक नहीं करते।
### वॉटरमार्क जनरेशन (`/watermark` और `/watermark/batch`)
बेंचमार्क मूल्यांकन और राउंड-ट्रिप परीक्षण के लिए वॉटरमार्क्ड टेक्स्ट जनरेट करता है।
जब `WATERMARKS_SYNTHID_TEXT_URL` सेट होता है, तो सेवा जनरेशन को
`wr-synthid-text` साइडकार (हार्नेस प्रोफ़ाइल) को सौंपती है; स्थानीय `MARKLLM_DIR` के साथ यह
सीधे चेकआउट का उपयोग करती है। डिटेक्शन की तरह, जनरेशन भी फेल-सॉफ्ट है: एक अनकॉन्फ़िगर जनरेटर
`{"ok": false, "error": ...}` रिपोर्ट करता है।
## Docker / compose
प्रकाशित इमेज (GHCR):
| इमेज टैग | सामग्री | प्रकाशित? |
| --- | --- | --- |
| `ghcr.io/guillaumemeyer/watermarks-remover:<tag>` / `:latest` | कोर HTTP सेवा + सभी क्लीनर + exiftool / qpdf / c2patool | हाँ |
| `…:markllm-<tag>` / `:markllm-latest` | MarkLLM टेक्स्ट-वॉटरमार्क हार्नेस (Apache-2.0 अपस्ट्रीम) | हाँ |
| `…:markdiffusion-<tag>` / `:markdiffusion-latest` | MarkDiffusion इमेज हार्नेस (Apache-2.0 अपस्ट्रीम) | हाँ |
| `watermarks-remover-ctrlregen:local` | CtrlRegen पिक्सेल रिमूवल — **कभी प्रकाशित नहीं** (`noai-watermark` कोई LICENSE शिप नहीं करता) | केवल स्थानीय बिल्ड |
| `watermarks-remover-synthid-scorer:local` | reverse-SynthID स्कोरर — **कभी प्रकाशित नहीं** (गैर-व्यावसायिक Research License) | केवल स्थानीय बिल्ड (CLI स्कोरर + वैकल्पिक `wr-synthid-score` HTTP साइडकार `heavy` प्रोफ़ाइल के अंतर्गत) |
कोर सेवा बिल्ड और रन करें:```bash
make docker-core-build
docker run --rm -p 127.0.0.1:8765:8765 --read-only --tmpfs /tmp watermarks-remover
# any CLI stays runnable by overriding the command:
docker run --rm -v "$(pwd):/data" watermarks-remover \
/app/scripts/clean_file.py /data/notes.md -o /data/notes.cleaned.md
पूरे इन्फ्रा का ब्रिंग-अप:```bash docker compose up -d # core HTTP service only docker compose --profile harness up -d # + markllm / markdiffusion / wr-synthid-text sidecar docker compose --profile heavy up -d # + ctrlregen / synthid (local builds) docker compose --profile harness --profile heavy up -d # all services
The compose stack core service को `127.0.0.1:8765` पर map करता है। Persistent services background daemons के रूप में चलते हैं (`wr-core` और harness profile के अंतर्गत `wr-synthid-text` sidecar)। शेष harness/heavy services one-shot CLIs हैं — जब आपको verification या pixel work की आवश्यकता हो तो `docker compose run --rm <service> …` के साथ invoke करें।
चल रहे stack को validate करें (केवल exit code, सफलता पर कोई output नहीं):```bash
make compose-check # or: ./compose-check.sh
wr-core को GET /health के माध्यम से जाँचता है और प्रत्येक harness/heavy service को --help के साथ चलाता है, जिसमें exit 0 आवश्यक है।
कॉन्फ़िगरेशन (docker compose के लिए env vars)
टेक्स्ट क्लीनिंग के लिए Layer B कॉन्फ़िगरेशन आवश्यक है — टेक्स्ट पर POST /clean के लिए Layer B rewrite एक अनिवार्य चरण है, इसलिए core service को rewrite backend सेटअप करने की आवश्यकता है, अन्यथा टेक्स्ट क्लीनिंग HTTP 400 लौटाती है। Image/container metadata क्लीनिंग बिना किसी अतिरिक्त सेटअप के काम करती है। टेक्स्ट के लिए आपको Layer B strategy निर्भरताएँ कॉन्फ़िगर करनी होंगी: transformers + roberta-large (डिफ़ॉल्ट mlm चरण के लिए) और WATERMARKS_REWRITE_* LLM कॉन्फ़िग (paraphrase चरण के लिए):```bash
echo "Hello\u200bWorld\u00ad!" > /tmp/sample.txt
curl -s -X POST http://127.0.0.1:8765/clean -H 'Content-Type: application/json'
-d "{"file": "$(base64 < /tmp/sample.txt | tr -d '\n')", "name": "sample.txt"}"
जिन भाषाओं की टाइपोग्राफी नॉन-ब्रेकिंग स्पेस पर निर्भर करती है (फ़्रेंच `« … »`, `; : ! ?` से पहले का स्पेस), उन्हें `"options": {"normalize_spaces": false}` पास करना चाहिए, जो `clean_text.py --no-normalize-spaces` का HTTP समकक्ष है। अदृश्य कैरियर अभी भी हटा दिए जाते हैं; केवल स्पेस रीराइट छोड़ दिया जाता है।
बाकी सब कुछ वैकल्पिक है और रेपो रूट में एक `.env` फ़ाइल में रहता है। `docker compose` **`.env` को स्वतः लोड करता है** और उससे `compose.yaml` में `${VAR}` संदर्भों को इंटरपोलेट करता है (यदि दोनों सेट हों तो शेल एक्सपोर्ट `.env` पर भारी पड़ते हैं)।```bash
cp .env.example .env # then edit
docker compose up -d # picks up .env automatically
.env gitignored (deny-by-default) है — इसे कभी commit न करें। host-side CLI runs (rewrite_text.py, the skill) के लिए, उसी फ़ाइल को environment में export करें:```bash
set -a; . ./.env; set +a; python3 service/scripts/rewrite_text.py /tmp/x.txt -o /tmp/x.rewritten.txt
| Var | Reaches | Purpose |
| --- | --- | --- |
| `WATERMARKS_SERVER_API_KEY` | `wr-core` (compose `environment` के माध्यम से) | HTTP API पर `Authorization: Bearer <key>` आवश्यक करें |
| `WATERMARKS_GEMINI_*` | — | अगस्त 2026 में हटाया गया: Google ने API पर SynthID टेक्स्ट वॉटरमार्किंग बंद कर दी (देखें `vendor-notes.md`) |
| `WATERMARKS_SYNTHID_SCORER_URL` | `wr-core` | SynthID इमेज स्कोरिंग के लिए core को `wr-synthid-score` साइडकार की ओर इंगित करें (जैसे heavy प्रोफ़ाइल के अंतर्गत `http://wr-synthid-score:8766`) |
| `WATERMARKS_SYNTHID_SCORER_API_KEY` | `wr-core` + `wr-synthid-score` | स्कोरर साइडकार के लिए साझा bearer key (खाली = कोई प्रमाणीकरण नहीं) |
| `WATERMARKS_SYNTHID_TEXT_URL` | `wr-core` | SynthID टेक्स्ट वॉटरमार्किंग के लिए core को `wr-synthid-text` साइडकार की ओर इंगित करें (जैसे harness प्रोफ़ाइल के अंतर्गत `http://wr-synthid-text:8767`) |
| `WATERMARKS_SYNTHID_TEXT_API_KEY` | `wr-core` + `wr-synthid-text` | टेक्स्ट वॉटरमार्क साइडकार के लिए साझा bearer key (खाली = कोई प्रमाणीकरण नहीं) |
| `WATERMARKS_SYNTHID_TEXT_TIMEOUT` | `wr-core` | `wr-synthid-text` साइडकार की प्रतीक्षा करने के लिए सेकंड (डिफ़ॉल्ट 120) |
| `WATERMARKS_MARKLLM_SCHEME` | `text_detectors.py` (होस्ट) | `/detect` के लिए MarkLLM स्कीम: `kgw` (डिफ़ॉल्ट) / `synthid` |
| `HF_TOKEN` | harness/heavy सेवाएँ | gated मॉडल के लिए Hugging Face टोकन |
| `WATERMARKS_SERVICE_URL` | केवल क्लाइंट (skill / curl) | सेवा तक कहाँ पहुँचना है; डिफ़ॉल्ट `http://127.0.0.1:8765` |
| `WATERMARKS_REWRITE_BACKEND` | `rewrite_text.py` हुक | `print-prompt` (डिफ़ॉल्ट) / `ollama` / `openai-compatible` |
| `WATERMARKS_REWRITE_MODEL` | `rewrite_text.py` हुक | मॉडल नाम (जैसे `deepseek-v4-flash`) |
| `WATERMARKS_REWRITE_BASE_URL` | `rewrite_text.py` हुक | API बेस (जैसे `https://api.deepseek.com`) |
| `WATERMARKS_REWRITE_API_KEY` | `rewrite_text.py` हुक | API key — केवल env में, कभी argv पर नहीं |
| `WATERMARKS_REWRITE_ALLOW_REMOTE` | `rewrite_text.py` हुक | गैर-loopback एंडपॉइंट की अनुमति देने के लिए `1` |
| `WATERMARKS_REWRITE_REASONING_EFFORT` | `rewrite_text.py` हुक | `none` (डिफ़ॉल्ट) / `low` / `medium` / `high` / `off` |
| `WATERMARKS_CLEAN_STRATEGY_FILE` | `server.py` `/clean` | Layer B रणनीति कॉन्फ़िग JSON का पथ (डिफ़ॉल्ट `config/clean_strategy.json`) |
| `WATERMARKS_GUMBEL_KEY` | `detect_gumbel.py` / `text_detectors.py` | keyed-Gumbel (EXP) same-key replay के लिए गुप्त key (जैसे `0x…`); argv पर वरीयता — कभी लॉग नहीं किया जाता |
**टेक्स्ट सफ़ाई के लिए Layer B आवश्यक है।** `/clean` हमेशा Layer A के बाद एक टेक्स्ट फ़ाइल पर डिफ़ॉल्ट रणनीति (from `config/clean_strategy.json`, `{"default_strategy": "[email protected],[email protected]"}`) लागू करता है, जब तक कि अनुरोध अपना स्वयं का `"strategy"` विकल्प (एक क्रमबद्ध `tactic@intensity` सूची) पास न करे। एक रणनीति चरण `tactic@intensity` होता है; `mlm` चरण को `transformers` + `roberta-large` चाहिए, और किसी भी LLM चरण (`paraphrase`, `humanize`, …) को `WATERMARKS_REWRITE_*` कॉन्फ़िग चाहिए। यदि आवश्यक बैकएंड/मॉडल कॉन्फ़िगर नहीं है — या कोई रणनीति उपलब्ध नहीं है — तो `/clean` **अनुरोध को 400 के साथ अस्वीकार कर देता है**। कॉन्फ़िग पथ के लिए प्राथमिकता: `--strategy-config` CLI फ़्लैग > `WATERMARKS_CLEAN_STRATEGY_FILE` env var > डिफ़ॉल्ट `config/clean_strategy.json`।
इमेज [`.github/workflows/release-images.yml`](https://github.com/guillaumemeyer/watermarks-remover/blob/main/.github/workflows/release-images.yml) के माध्यम से `v*` टैग पर स्वचालित रूप से प्रकाशित होती हैं।
## वैकल्पिक SynthID पिक्सेल स्कोरिंग
`inspect_image.py` और `clean_image.py` एक पिक्सेल-डोमेन SynthID
विश्वास स्कोर रिपोर्ट कर सकते हैं जब
[`aloshdenny/reverse-SynthID`](https://github.com/aloshdenny/reverse-SynthID)
का बाहरी चेकआउट उपलब्ध हो। स्कोरर **बंडल नहीं किया गया है**: यह रनटाइम पर आपके
चेकआउट से लोड होता है, और इसका कोड अपस्ट्रीम प्रोजेक्ट के गैर-व्यावसायिक
Research License के अंतर्गत रहता है।
### विकल्प 1: एक-कमांड बूटस्ट्रैप (कोई Docker नहीं)```bash
SCRIPTS=service/scripts
# Clones upstream, creates a venv, and installs scorer-only dependencies.
"$SCRIPTS/setup_synthid.sh"
# Score an image (default checkout: ~/reverse-SynthID).
REVERSE_SYNTHID_DIR=~/reverse-SynthID \
~/reverse-SynthID/.venv/bin/python "$SCRIPTS/score_synthid.py" shot.png
# Or surface the score from inspect / clean (same venv Python).
REVERSE_SYNTHID_DIR=~/reverse-SynthID \
~/reverse-SynthID/.venv/bin/python "$SCRIPTS/inspect_image.py" shot.png
setup_synthid.sh --dir PATH, --ref REF, और --full स्वीकार करता है (पूर्ण अपस्ट्रीम requirements.txt इंस्टॉल करता है, जो इस प्रोजेक्ट द्वारा उपयोग न किए जाने वाले अपस्ट्रीम VAE बायपास के लिए torch/diffusers जोड़ता है)।
Windows पर setup_synthid.ps1 (-Dir, -Ref, -Full) का उपयोग करें, जो venv को .venv\Scripts\ पर बनाता है — वही लेआउट जिसे image_meta.py पहले से ही os.name == "nt" पर खोजता है।
विकल्प 2: स्थानीय Docker बिल्ड```bash
make docker-synthid-build
Run unprivileged and with a read-only rootfs; the scorer only needs to read
/data and write to stdout/tmp.
docker run --rm
--user "$(id -u):$(id -g)"
--read-only --tmpfs /tmp
-v "$(pwd):/data"
watermarks-remover-synthid-scorer /data/shot.png
इमेज बिल्ड समय पर अपस्ट्रीम स्रोत से स्थानीय रूप से बनाई जाती है। यह प्रकाशित नहीं की जाती, इसलिए यह अपस्ट्रीम कोड का पुनर्वितरण नहीं करती।
### विकल्प 3: HTTP स्कोरर साइडकार (docker compose)
`heavy` प्रोफ़ाइल के अंतर्गत compose स्टैक स्कोरर को एक HTTP साइडकार (`wr-synthid-score`) के रूप में भी चलाता है ताकि **प्रकाशित कोर सेवा** गैर-व्यावसायिक अपस्ट्रीम कोड को बंडल किए बिना क्लीनिंग से पहले/बाद में इमेज को स्कोर कर सके। `wr-core` को इसकी ओर इंगित करें और एक bearer key साझा करें (देखें `.env.example`):```bash
# .env
WATERMARKS_SYNTHID_SCORER_URL=http://wr-synthid-score:8766
WATERMARKS_SYNTHID_SCORER_API_KEY=change-me
docker compose --profile heavy up -d
फिर POST /clean को {"options": {"detect_before": true, "detect_after": true}} के साथ भेजने पर रिपोर्ट में synthid_before / synthid_after लौटाता है, और किसी इमेज पर POST /detect भेजने पर SynthID स्कोर लौटाता है। Fail-soft:
यदि sidecar डाउन है या कॉन्फ़िगर नहीं है, तो रिपोर्ट में
{"available": false, "error": ...} होता है और cleaning फिर भी सफल होती है।
V4 स्कोरिंग upstream checkout से artifacts/spectral_codebook_v4.npz का उपयोग करती है
(`220 MB)। यह केवल detection/scoring है — यह पिक्सेल
वॉटरमार्क्स को हटाता नहीं है।
वैकल्पिक CtrlRegen पिक्सेल हटाना
पिक्सेल-डोमेन इमेज वॉटरमार्क्स (SynthID-श्रेणी, StegaStamp, Tree-Ring,
StableSignature) के लिए, एक वैकल्पिक बाहरी बैकएंड CtrlRegen पाइपलाइन
(ControlNet + DINOv2 IP-Adapter controllable regeneration) चलाता है। बैकएंड
mertizci/noai-watermark है, जो ICLR 2025
CtrlRegen विधि का एक maintained पुनःकार्यान्वयन है
जिसमें स्वचालित tiling है।
बैकएंड बंडल नहीं है और इसमें कोई LICENSE फ़ाइल नहीं है, इसलिए इसे
all-rights-reserved माना जाता है: इसे एक pinned commit पर क्लोन किया जाता है और रनटाइम पर लोड किया जाता है।
इसकी research-era dependency pins (requirements-ctrlregen.txt — जैसे
transformers==4.37.2, diffusers==0.27.2) में प्रकाशित advisories हैं और
जानबूझकर वर्तमान नहीं हैं, इसलिए इन्हें केवल उसी समर्पित venv के अंदर इंस्टॉल किया जाता है जो यह स्क्रिप्ट बनाती है और कभी भी मुख्य
सेवा इमेज में नहीं;
setup_ctrlregen.sh मौजूदा
checkouts पर भी pinned commit को फिर से सत्यापित करता है, केवल नए clones पर ही नहीं।
Bootstrap```bash
SCRIPTS=service/scripts
Clones upstream (pinned commit), creates a venv, installs torch + deps.
"$SCRIPTS/setup_ctrlregen.sh"
Standalone removal (default checkout: ~/noai-watermark).
NOAI_WATERMARK_DIR=~/noai-watermark
~/noai-watermark/.venv/bin/python "$SCRIPTS/clean_ctrlregen.py" shot.png -o shot.ctrlregen.png
Windows पर `setup_ctrlregen.ps1` का उपयोग करें (`-Dir`, `-Ref`, `-Python` के समान फ़्लैग्स);
venv `.venv\Scripts\` में बनता है, जिसे `clean_image.py` पहले से ही हल कर लेता है।
यह प्रकाशित PyTorch wheel indices की जाँच करता है और `nvidia-smi` द्वारा दिखाए गए CUDA संस्करण के बराबर या उससे नीचे का उच्चतम वह index चुनता है जो वास्तव में मौजूद है — वह संख्या वह अधिकतम है जिसे *driver* समर्थन करता है, और drivers पीछे की ओर संगत होते हैं, इसलिए 13.1 रिपोर्ट करने वाला driver (कोई प्रकाशित `cu131` नहीं) `cu130` इंस्टॉल करता है। compute capability 7.5 से नीचे यह `cu126` को बाध्य करता है, जो अंतिम index है जिसके wheels में अभी भी Maxwell/Pascal/Volta kernels होते हैं। यह उस index से `torch` **और** `torchvision` दोनों को एक साथ इंस्टॉल करता है ताकि dependency install उन्हें PyPI से CPU builds से न बदल सके, फिर इंस्टॉल के बाद सत्यापित करता है कि `torch.cuda.is_available()` true है — यदि GPU का पता चला था लेकिन torch CPU-only निकलता है, तो script ज़ोर से चेतावनी देता है और सेटअप सफल होने का दिखावा करने के बजाय non-zero के साथ बाहर निकल जाता है।
### `clean_image.py` से```bash
NOAI_WATERMARK_DIR=~/noai-watermark \
~/noai-watermark/.venv/bin/python "$SCRIPTS/clean_image.py" shot.png \
-o shot.cleaned.png --remove-pixel ctrlregen
संचालन का क्रम: पहले मेटाडेटा स्ट्रिप, फिर CtrlRegen पिक्सेल हटाना, फिर
एक वैकल्पिक रिवर्स-SynthID पहले/बाद स्कोर (जब REVERSE_SYNTHID_DIR भी सेट हो)।
तीव्रता डिफ़ॉल्ट रूप से रूढ़िवादी है (--ctrlregen-intensity 0.25), क्योंकि
अधिक तीव्रता अधिक वॉटरमार्क हटाती है लेकिन छवि का अधिक पुनर्निर्माण करती है।
प्रलेखित प्रीसेट: 0.15 न्यूनतम / 0.25 डिफ़ॉल्ट / 0.35 संतुलित /
0.5 आक्रामक / 0.7 अधिकतम (बैकएंड डिफ़ॉल्ट 0.5 है)। --ctrlregen-steps
डिफ़ॉल्ट 50 है (प्रभावी डीनॉइज़िंग चरण ≈ चरण × तीव्रता)।
छवि आकार (512×512 मूल सीमा)
CtrlRegen एक 512×512 Stable Diffusion 1.5 ControlNet है। बैकएंड इसे मनमाने इनपुट के लिए हल करता है, इसलिए यहाँ कोई अतिरिक्त टाइलिंग उजागर नहीं की गई है:
- ≤512 px: एकल पास — 512 पर केंद्र-क्रॉप/रीसाइज़, पुनर्निर्माण, वापस रीसाइज़।
- >512 px: स्वचालित ओवरलैपिंग टाइलिंग (512 px टाइल्स, 192 px ओवरलैप), चौड़ाई/ऊँचाई 8 के गुणजों में संरेखित, फिर कोसाइन-ब्लेंडेड सीम।
- दोनों पथ: आउटपुट को मूल आकार में रीसाइज़ किया जाता है और मूल छवि से रंग-मिलान किया जाता है।
बहुत बड़ी छवियाँ (जैसे 4K) कई टाइल्स उत्पन्न करती हैं, इसलिए रन टाइल संख्या के साथ बढ़ते हैं (धीमा और अधिक VRAM)। व्यावहारिक होने पर बड़े इनपुट को पहले छोटा करें; टाइल आकार और ओवरलैप अपस्ट्रीम में हार्डकोडेड हैं और फ्लैग के रूप में उजागर नहीं हैं।
कंप्यूट, गेटेड मॉडल, और सत्यापन
लगभग ~10 GB मॉडल डाउनलोड की अपेक्षा करें; GPU की दृढ़ता से सिफारिश की जाती है और CPU रन
धीमे होते हैं। कुछ अपस्ट्रीम मॉडल गेटेड हैं, इसलिए HF_TOKEN निर्यात करें (केवल env —
कभी argv नहीं)। clean_ctrlregen.py निर्भरताओं को स्वतः-इंस्टॉल करने से इंकार करता है; पहले
setup_ctrlregen.sh चलाएँ।
StegaStamp/Tree-Ring/StableSignature के लिए कोई स्थानीय डिटेक्टर नहीं है, इसलिए
एकमात्र स्थानीय संकेत रिवर्स-SynthID स्कोर (एक सरोगेट) है। जब उपलब्ध हो,
clean_image.py --remove-pixel ctrlregen उस स्कोर को पहले/बाद में रिपोर्ट करता है; आधिकारिक
Google SynthID जाँच अंतिम प्राधिकारी बनी रहती है।
Docker```bash
make docker-ctrlregen-build
docker run --rm -e HF_TOKEN="$HF_TOKEN"
--user "$(id -u):$(id -g)"
-v "$(pwd):/data"
watermarks-remover-ctrlregen /data/shot.png -o /data/shot.ctrlregen.png
## वैकल्पिक MarkLLM टेक्स्ट-वॉटरमार्क सत्यापन
**नियंत्रित प्रयोगों** के लिए, एक वैकल्पिक बाहरी हार्नेस
[`THU-BPM/MarkLLM`](https://github.com/THU-BPM/MarkLLM) (Apache-2.0) को रैप करता है ताकि
परीक्षण टेक्स्ट पर वॉटरमार्क लगाया जा सके और Layer B रीराइट के बाद उसे फिर से डिटेक्ट किया जा सके — उदाहरण के लिए, यह साबित करना कि
एक KGW (Kirchenbauer, आपकी "open-LLM" पंक्ति) या SynthID-Text (Gemini पंक्ति) मार्क
आपके रीराइट के अंतर्गत गायब हो जाता है। यह एक **सत्यापन हार्नेस है, ऑरेकल नहीं**:
MarkLLM डिटेक्शन केवल उसी स्कीम कॉन्फ़िग + keys के विरुद्ध मान्य है जो जनरेशन के समय उपयोग किए गए थे, और यह प्रमाणित नहीं कर सकता कि किसी विक्रेता का डिटेक्टर विफल होगा।
बैकएंड **बंडल नहीं किया गया है**। `setup_markllm.sh` अपस्ट्रीम को एक पिन किए गए
कमिट पर क्लोन करता है, एक venv बनाता है, और पिन किए गए डिप्स (torch + transformers) इंस्टॉल करता है; स्कोरिंग मॉडल (डिफ़ॉल्ट `facebook/opt-1.3b`, Apache-2.0) पहले रन पर Hugging
Face से डाउनलोड होता है।```bash
SCRIPTS=service/scripts
# Bootstrap (clones upstream, creates ~/MarkLLM/.venv, installs deps).
"$SCRIPTS/setup_markllm.sh"
# Generate watermarked + unwatermarked sample text under the KGW scheme.
MARKLLM_DIR=~/MarkLLM \
~/MarkLLM/.venv/bin/python "$SCRIPTS/detect_text_watermark.py" watermark prompt.txt \
--scheme kgw -o wm.txt -o2 plain.txt
# Detect the scheme mark in a text file.
MARKLLM_DIR=~/MarkLLM \
~/MarkLLM/.venv/bin/python "$SCRIPTS/detect_text_watermark.py" detect wm.txt --scheme kgw --json
Layer B रीराइट के आसपास सत्यापन: rewrite_text.py को --markllm-scheme पास करें (--markllm-dir के साथ), और यह MarkLLM डिटेक्शन को पहले/बाद में रिकॉर्ड करता है साथ ही एक cleared फ़्लैग भी:```bash
export WATERMARKS_REWRITE_BACKEND=ollama WATERMARKS_REWRITE_MODEL=llama3.2
MARKLLM_DIR=~/MarkLLM
python3 "$SCRIPTS/rewrite_text.py" wm.txt -o wm.rewritten.txt
--markllm-scheme kgw --markllm-dir "$HOME/MarkLLM" --json-stats
**डिटेक्शन-निर्देशित पुनरावृत्तीय पुनर्लेखन:** लेयर B अब पुनरावृत्तीय रूप से पुनर्लेखन करता है और जैसे ही कोई प्रयास मूल्यांकन पास करता है, रुक जाता है। प्रत्येक मूल्यांकन दौर `--candidates` वेरिएंट उत्पन्न करता है (डिफ़ॉल्ट **1**, `WATERMARKS_REWRITE_CANDIDATES`) और `--max-loops` यह सीमित करता है कि सर्वोत्तम-प्रयास वेरिएंट लौटाए जाने से पहले कितने दौर चलें (डिफ़ॉल्ट **1**, `WATERMARKS_REWRITE_LOOPS`)। प्रत्येक वेरिएंट एक पुनर्लेखन कॉल और एक मूल्यांकन है, और एक दौर पहले प्रयास पर ही जल्दी समाप्त हो जाता है जिसे मूल्यांकनकर्ता गैर-वॉटरमार्क्ड बताता है — इसलिए `--max-loops` बढ़ाने से नए वेरिएंट तब तक पुनः प्रयास करते हैं जब तक कोई मूल्यांकन पास न हो जाए (एक सामान्य स्वच्छ पुनर्लेखन में एक प्रयास लगता है)। मूल्यांकनकर्ता प्राथमिकता के आधार पर चुना जाता है:
1. **MarkLLM** — समान-कॉन्फ़िग शोध डिटेक्शन, जब `--markllm-scheme` पास किया जाता है (साथ में `--markllm-dir`)। MarkLLM के ऊपर Google के SynthID-text डिटेक्टर के लिए एक विक्रेता-डिटेक्टर स्लॉट आरक्षित है, जिसे Google ने अगस्त 2026 में अपने API पर बंद कर दिया — भविष्य में कोई विक्रेता एंडपॉइंट वहाँ जुड़ सकता है।
2. **bigram-Jaccard शाब्दिक विचलन** — जब कोई डिटेक्टर कॉन्फ़िगर नहीं होता; कोई पास/फेल निर्णय नहीं, इसलिए प्रत्येक प्रयास उत्पन्न होता है और सबसे अधिक शाब्दिक रूप से विचलित चुना जाता है (मूल व्यवहार)।
`--json-stats` मूल्यांकनकर्ता, किए गए प्रयास, पास/फेल, और प्रति-प्रयास रिकॉर्ड रिपोर्ट करता है:```json
{
"evaluator": "markllm",
"candidates": 1,
"max_loops": 2,
"attempts_made": 2,
"passed": true,
"candidate_scores": [
{
"lexical_divergence": 0.91,
"selection_score": 0.91,
"selected": false,
"passed": false,
"evaluation": {"detector": "markllm", "available": true, "scheme": "kgw",
"is_watermarked": true, "score": 4.3, "threshold": 3.0}
},
{
"lexical_divergence": 0.84,
"selection_score": 0.84,
"selected": true,
"passed": true,
"evaluation": {"detector": "markllm", "available": true, "scheme": "kgw",
"is_watermarked": false, "score": 1.7, "threshold": 3.0}
}
],
"markllm": {"scheme": "kgw", "before": {"...": "..."}, "after": {"...": "..."},
"cleared": true, "note": "same-config only"}
}
एक डिटेक्टर जो अनकॉन्फ़िगर है, टाइम आउट होता है, या त्रुटि देता है, एक
"available": false प्रविष्टि देता है जिसमें एक error कारण होता है और यह कभी भी
रीराइट को विफल नहीं करता — वह प्रयास बस पास नहीं हो सकता, और लूप लेक्सिकल-डाइवर्जेंस
चयन पर वापस चला जाता है। जब बिना किसी पास के अधिकतम सीमा समाप्त हो जाती है, तो
सबसे कम-वॉटरमार्क वाला (न्यूनतम स्कोर) प्रयास एक नोट के साथ बेस्ट-एफर्ट के रूप में
लौटाया जाता है।
यदि बैकएंड अनकॉन्फ़िगर है या उसकी डिपेंडेंसीज़ गायब हैं, तो रीराइट आगे बढ़ता है और रिपोर्ट नोट करती है कि सत्यापन अनुपलब्ध था। एक GPU की सिफारिश की जाती है; CPU रन काम करते हैं लेकिन धीमे होते हैं, और मॉडल डाउनलोड कुछ GB का होता है।
हार्डनिंग नॉब्स:
- एडाप्टर पर
--offline(या कोई भी MarkLLM रन) स्कोरिंग मॉडल को केवल Hugging Face कैश से लोड करता है — शून्य नेटवर्क एग्रेस; यदि कैश में नहीं है तो तेज़ी से विफल हो जाता है। कस्टम रिमोट कोड कभी निष्पादित नहीं किया जाता (transformerstrust_remote_codeकभी सक्षम नहीं किया जाता)। WATERMARKS_MARKLLM_RLIMIT_AS=<bytes>(env, POSIX) MarkLLM डिटेक्टर सबप्रोसेस पर एक एड्रेस-स्पेस सीमा लागू करता है। डिफ़ॉल्ट रूप से बंद क्योंकि torch/CUDA को आमतौर पर बड़े एड्रेस स्पेस की आवश्यकता होती है।- कॉन्फ़िग फ़ाइलें 1 MiB तक सीमित हैं; अपस्ट्रीम चेकआउट और बेस इमेज SHA/digest द्वारा पिन किए गए हैं।
Docker```bash
make docker-markllm-build
docker run --rm --user "$(id -u):$(id -g)" -v "$(pwd):/data"
watermarks-remover-markllm detect /data/wm.txt --scheme kgw --json
### Keyed-Gumbel (Aaronson EXP) समान-कुंजी सत्यापन
[ARBI की तकनीकी रिपोर्ट](https://arbicity.com/news/ai-text-watermarking-for-self-hosted-ai/) keyed-Gumbel ("exponential") टेक्स्ट वॉटरमार्क का वर्णन करती है — जो अब ओपन-सोर्स arbi-serve इंजन (`ARBI_WATERMARK_KEY`) में शिप हो रहा है — जहाँ सैंपलर का शोर अंतिम 4-टोकन कॉन्टेक्स्ट विंडो के keyed hash से प्राप्त होता है। डिटेक्शन एक **मॉडल-मुक्त रीप्ले** है: केवल टेक्स्ट से `u = PRF(Hash(key, window), token)` की पुनर्गणना करें और Gamma tail का परीक्षण करें, इसलिए इसे किसी GPU, मॉडल, या logits की आवश्यकता नहीं होती। यह रेपो उस डिटेक्टर को `detect_gumbel.py` के रूप में शिप करता है (केवल-stdlib; p-value एक पूर्णांक Gamma shape के लिए सटीक Poisson-sum identity है):```bash
# Text mode (deterministic word/run tokenizer) — quick checks and rewrite-loop
# evaluation; exact replay against a real engine needs its tokenizer:
python3 service/scripts/detect_gumbel.py draft.txt --key 0x... --json
# Exact replay: pass the engine's token ids (JSON array or one per line).
python3 service/scripts/detect_gumbel.py ids.json --tokens --key 0x... --json
MarkLLM की तरह ही ईमानदारी की चेतावनी: यह एक same-key replay है — केवल उसी key, tokenizer, और PRF layout के विरुद्ध मान्य है जो generation के समय उपयोग किए गए थे, और एक negative परिणाम कुछ भी सिद्ध नहीं करता। यहाँ HMAC-SHA256 layout एक auditable instantiation है, किसी विशिष्ट engine kernel के साथ bit-compatible नहीं (exact replay के लिए क्या adapt करना है, इसके लिए module docstring देखें)।
Detection-guided rewriting: rewrite_text.py को --gumbel-key पास करें (env: WATERMARKS_GUMBEL_KEY, preferred) और iterative rewrite loop उसी same-key Gumbel replay द्वारा संचालित होता है — evaluator priority gumbel > MarkLLM > lexical divergence हो जाती है — साथ में एक gumbel.before/after/cleared रिपोर्ट के साथ:```bash
export WATERMARKS_REWRITE_BACKEND=ollama WATERMARKS_REWRITE_MODEL=llama3.2
export WATERMARKS_GUMBEL_KEY=0x...
python3 "$SCRIPTS/rewrite_text.py" wm.txt -o wm.rewritten.txt --json-stats
कुंजी कभी भी stats या logs में नहीं दिखाई देती। जो self-hosted operators अपने
engine की कुंजी रखते हैं, वे सत्यापित कर सकते हैं कि किसी rewrite ने Gumbel mark को साफ़ कर दिया; बाकी सभी Layer B को केवल best-effort मानते हैं।
## वैकल्पिक SynthID-text हटाने का बेंचमार्क
[`bench_synthid_text.py`](https://github.com/guillaumemeyer/watermarks-remover/blob/main/service/scripts/bench_synthid_text.py) मापता है कि
एक Layer B rewrite SynthID-text-श्रेणी के watermarks को कितने प्रभावी ढंग से साफ़ करता है और
किस लागत पर। यह MarkLLM
SynthID scheme (same-config detection, sanity-gated) के साथ watermarked + unwatermarked samples उत्पन्न करता है, आपके rewrite
variants (tactic × max rewrite attempts; pass पर loop जल्दी रुक जाता है) के साथ-साथ
controls (no-removal, Layer-A-only, वैकल्पिक re-stamp check) चलाता है, और एक साझा करने योग्य `report.md` /
`results.json` / `results.csv` लिखता है। पूरी मार्गदर्शिका:
[`docs/synthid-text-benchmark.md`](https://github.com/guillaumemeyer/watermarks-remover/blob/main/docs/synthid-text-benchmark.md)।
इसके लिए एक MarkLLM checkout (`setup_markllm.sh` / `MARKLLM_DIR`) और एक
rewrite backend आवश्यक है। **rewriting model एक LLM है जिसे आप configure करते हैं** — वही
`rewrite_text.py` backend जो skill उपयोग करता है। MarkLLM का डिफ़ॉल्ट
`facebook/opt-1.3b` (`--markllm-model`) केवल watermark
generator/detector है; यह कभी rewrite नहीं करता। rewrite model को env
vars या benchmark flags के माध्यम से configure करें (वे ऊपर दी गई
[config table](#configuration-env-vars-for-docker-compose) को दर्शाते हैं):
| Env var | Benchmark flag | Default | Meaning |
| --- | --- | --- | --- |
| `WATERMARKS_REWRITE_BACKEND` | `--rewrite-backend` | `ollama` | `ollama` या `openai-compatible` |
| `WATERMARKS_REWRITE_MODEL` | `--rewrite-model` | *(आवश्यक)* | वह LLM जो rewrite करता है (जैसे `llama3.2`, `deepseek-v4-flash`) |
| `WATERMARKS_REWRITE_BASE_URL` | `--rewrite-base-url` | `http://127.0.0.1:11434` | Endpoint; Ollama का डिफ़ॉल्ट loopback है |
| `WATERMARKS_REWRITE_API_KEY` | `--rewrite-api-key` | — | API key (child process में केवल env, कभी argv नहीं) |
| `WATERMARKS_REWRITE_ALLOW_REMOTE=1` | `--rewrite-allow-remote` | off | non-loopback endpoints पर content भेजने के लिए आवश्यक |```bash
# Ollama (loopback):
python3 service/scripts/bench_synthid_text.py --markllm-dir ~/MarkLLM \
--rewrite-backend ollama --rewrite-model llama3.2
# OpenAI-compatible API (remote):
WATERMARKS_REWRITE_API_KEY=... python3 service/scripts/bench_synthid_text.py \
--markllm-dir ~/MarkLLM --rewrite-backend openai-compatible \
--rewrite-model deepseek-v4-flash --rewrite-base-url https://api.deepseek.com \
--rewrite-allow-remote
रीराइटिंग के लिए गैर-मूल मॉडल का उपयोग करें (उसी वॉटरमार्क वाले मॉडल से दोबारा न लिखें जिसने टेक्स्ट जनरेट किया था) अन्यथा रीराइट आउटपुट पर दोबारा मार्क लगा सकता है; --restamp-control इसे मापता है।
वैकल्पिक MarkDiffusion इमेज-वॉटरमार्क हार्नेस
इमेज पर नियंत्रित प्रयोगों के लिए, एक वैकल्पिक बाहरी हार्नेस
THU-BPM/MarkDiffusion (Apache-2.0) को रैप करता है,
जो लेटेंट डिफ्यूज़न मॉडल के लिए एक जनरेटिव वॉटरमार्किंग टूलकिट है (यह मार्क एम्बेड करता है
— हटाता नहीं है)। हम इसका उपयोग तीन कामों के लिए करते हैं:
- सत्यापन हार्नेस (MarkLLM की तरह, लेकिन इमेज के लिए): एक टेस्ट इमेज पर किसी स्कीम से वॉटरमार्क लगाएँ, रिमूवल चलाएँ, और उसी स्कीम कॉन्फ़िग से दोबारा डिटेक्ट करें — जैसे यह साबित करना कि Tree-Ring-श्रेणी का मार्क आपकी पाइपलाइन के अंतर्गत साफ़ हो जाता है। यह एक सत्यापन हार्नेस है, ऑरेकल नहीं: डिटेक्शन के लिए जनरेटिंग मॉडल (और key-आधारित स्कीमों के लिए keys) चाहिए, इसलिए यह प्रमाणित नहीं कर सकता कि किसी वेंडर डिटेक्टर किसी मनमानी इमेज पर विफल होगा।
- वैकल्पिक पिक्सेल-रिमूवल इंजन: इसका
DiffusionPurificationरीजनरेशन अटैकclean_image.py --remove-pixel diffusionके रूप में उजागर है, जो CtrlRegen का एक विकल्प है। यह ब्लाइंड रीजनरेशन है (कोई ControlNet कंडीशनिंग नहीं), इसलिए यह CtrlRegen की तुलना में इमेज कंटेंट को अधिक विचलित करता है — रूढ़िवादी इंटेंसिटी डिफ़ॉल्ट (0.3), इसे फ़ॉलबैक/तुलना के रूप में माना जाता है, कभी गारंटी नहीं। - Tree-Ring-श्रेणी के मार्कों के लिए लोकल समान-स्कीम डिटेक्टर, जो आंशिक रूप से "StegaStamp/Tree-Ring/StableSignature के लिए कोई लोकल डिटेक्टर नहीं" की कमी को भरता है (यह Tree-Ring/Ring-ID/Gaussian-Shading आदि को कवर करता है, StegaStamp / StableSignature / SynthID-media को नहीं)।
बैकएंड बंडल नहीं है। setup_markdiffusion.sh एक venv बनाता है और
PyPI से markdiffusion==1.0.2 (पिन किया हुआ) इंस्टॉल करता है, torch को सही
प्लेटफ़ॉर्म इंडेक्स से इंस्टॉल करता है; --checkout इसके बजाय एक पिन किए गए
कमिट पर एक एडिटेबल क्लोन इंस्टॉल करता है। Stable Diffusion मॉडल (डिफ़ॉल्ट
huanzi05/stable-diffusion-2-1-base) पहली बार चलाने पर Hugging Face से डाउनलोड होता है।```bash
SCRIPTS=service/scripts
Bootstrap (PyPI pin default; creates ~/markdiffusion/.venv, installs deps).
"$SCRIPTS/setup_markdiffusion.sh"
1. Generate a Tree-Ring watermarked image (+ unwatermarked control).
echo "a red fox in snow" > /tmp/prompt.txt
MARKDIFFUSION_DIR=~/markdiffusion
~/markdiffusion/.venv/bin/python "$SCRIPTS/markdiffusion_harness.py" watermark
/tmp/prompt.txt -o wm.png -o2 plain.png --scheme tr --json
2. Remove with the DiffusionPurification regeneration attack.
MARKDIFFUSION_DIR=~/markdiffusion
~/markdiffusion/.venv/bin/python "$SCRIPTS/markdiffusion_harness.py" purify
wm.png -o wm.purified.png --purification-intensity 0.3 --json
3. Re-detect with the SAME scheme config.
MARKDIFFUSION_DIR=~/markdiffusion
~/markdiffusion/.venv/bin/python "$SCRIPTS/markdiffusion_harness.py" detect
wm.purified.png --scheme tr --detector-type l1_distance --json
या सामान्य इमेज पाइपलाइन के हिस्से के रूप में शुद्धिकरण चलाएँ:```bash
MARKDIFFUSION_DIR=~/markdiffusion \
~/markdiffusion/.venv/bin/python "$SCRIPTS/clean_image.py" shot.png \
-o shot.cleaned.png --remove-pixel diffusion
Hardening knobs MarkLLM harness को mirror करते हैं: --offline model को केवल
Hugging Face cache से load करता है (zero network egress, no remote code), HF_TOKEN
env-only है (कभी argv नहीं), algorithm configs 1 MiB पर capped हैं, और
subprocess को CtrlRegen के समान higher resource caps मिलते हैं।
Docker```bash
make docker-markdiffusion-build
docker run --rm --user "$(id -u):$(id -g)" -v "$(pwd):/data"
watermarks-remover-markdiffusion detect /data/wm.png --scheme tr --json
यह इमेज एक CPU torch इंस्टॉल करती है; CUDA उपयोगकर्ताओं को होस्ट पर इसके बजाय `setup_markdiffusion.sh` चलाना चाहिए। मॉडल डाउनलोड पहली बार चलाने पर अभी भी HF hub पर हिट करते हैं।
## कवरेज मैट्रिक्स
| चैनल | Claude | Gemini/SynthID | OpenAI | Open-LLM |
| --- | --- | --- | --- | --- |
| Unicode / edit-आधारित टेक्स्ट | Layer A | Layer A | Layer A | Layer A |
| **सांख्यिकीय सैंपलिंग टेक्स्ट** | Layer B best-effort (Claude seam जब Anthropic का detection API शिप होता है) | Layer B best-effort (+ MarkLLM same-config harness; Google ने vendor detector को Aug 2026 में रिटायर कर दिया) | Layer B यदि मौजूद हो | Layer B best-effort + वैकल्पिक MarkLLM harness |
| C2PA / फ़ाइल मेटाडेटा | हाँ (सूचीबद्ध फ़ॉर्मेट) | हाँ जब मौजूद हो | हाँ जब मौजूद हो | हाँ जब मौजूद हो |
| पिक्सेल इमेज मार्क्स | दायरे से बाहर | वैकल्पिक SynthID स्कोर + CtrlRegen हटाना (बाहरी); वैकल्पिक MarkDiffusion same-scheme detect + DiffusionPurification हटाना (बाहरी) | दायरे से बाहर | वैकल्पिक CtrlRegen / MarkDiffusion हटाना (बाहरी) |
| ट्रेनिंग बैकडोर | दायरे से बाहर | दायरे से बाहर | दायरे से बाहर | दायरे से बाहर |
विवरण: [`skills/remove-ai-marks/references/vendor-notes.md`](https://github.com/guillaumemeyer/watermarks-remover/blob/main/skills/remove-ai-marks/references/vendor-notes.md), [`mark-classes.md`](https://github.com/guillaumemeyer/watermarks-remover/blob/main/skills/remove-ai-marks/references/mark-classes.md)।
---
## टेक्स्ट मार्किंग कैसे काम करती है (संक्षेप)
आधुनिक LLM वॉटरमार्क अक्सर एक सिग्नल को **कौन से टोकन चुने जाते हैं** (generative / sampling bias) में छिपाते हैं, न कि केवल अदृश्य वर्णों में। Edit-आधारित स्कीमें Unicode या synonym नियम इंजेक्ट करती हैं। फ़ाइल स्कीमें **C2PA** या generator मेटाडेटा संलग्न करती हैं।
- **Layer A** edit-आधारित Unicode वाहक हटाता है (परीक्षण योग्य)।
- **Layer B** भारी पुनर्लेखन के माध्यम से sampling वॉटरमार्क पर हमला करता है (best-effort; साहित्य-मानक हमले जैसे paraphrase / back-translation)।
- **फ़ाइल क्लीनर** समर्थित कंटेनरों से C2PA/XMP/props हटाते हैं।
जब तक विक्रेता सार्वजनिक डिटेक्टर और कुंजियाँ शिप नहीं करते, **कोई भी टूल ईमानदारी से प्रमाणित नहीं कर सकता** कि "यह आधिकारिक जाँच में विफल होता है।" रिपोर्टों को सत्यापन योग्य बनाम best-effort कार्य को अलग करना चाहिए।
Layer B के लिए **non-origin** मॉडल को प्राथमिकता दें (यदि आप पुनः-स्टैम्पिंग से बचना चाहते हैं तो Claude टेक्स्ट को Claude से पुनर्लेखित न करें)।
---
## अस्वीकरण: टेक्स्ट वॉटरमार्क हटाने की लागत
टेक्स्ट वॉटरमार्क **शब्दावली में ही** रहते हैं: सिग्नल टोकन विकल्पों में फैला होता है, इसलिए लगभग हर वाक्य उसका थोड़ा हिस्सा वहन करता है। इसके दो परिणाम होते हैं, और यही कारण है कि Layer B को ईमानदारी से *best-effort* बताया जाता है, न कि जादुई इरेज़र।
1. **हटाने का अर्थ पुनर्शब्दीकरण है, पुनर्संरचना नहीं।** पैराग्राफ़ों को फेरबदल करना, शीर्षक बदलना, या हल्के टच-अप सिग्नल को मुश्किल से हिलाते हैं। सांख्यिकीय मार्क को हटाने के लिए टेक्स्ट के एक बड़े हिस्से को फिर से लिखना आवश्यक है — वाक्य-दर-वाक्य, अनुभाग-दर-अनुभाग नहीं।
2. **पुनर्शब्दीकरण कॉपी को ख़राब करता है।** कोई भी पुनर्लेखन मूल शब्द विकल्पों को पुनर्लेखन मॉडल के साथ बदल देता है, जो टोन, आवाज़ और सटीकता को सपाट कर देता है। प्रोडक्शन कॉपी (SEO, मार्केटिंग, क्लाइंट कार्य) पर यह गिरावट वास्तविक होती है और अक्सर उन लोगों को दिखाई देती है जो लेखन की सबसे अधिक परवाह करते हैं। यह ऐसा है जैसे किसी शीर्ष-स्तरीय मॉडल से टेक्स्ट लेकर किसी कम सक्षम मॉडल से इसे शुरू से फिर से लिखवाना: परिणाम पुनर्लेखन मॉडल की सीमा से अधिक नहीं हो सकता।
जो ईमानदार पूर्ण-वृत्त प्रश्न की ओर ले जाता है:
> यदि योजना वैसे भी सस्ते मॉडल से टेक्स्ट को फिर से लिखने की है, तो पहले स्थान पर प्रीमियम मॉडल के लिए भुगतान क्यों करें? सीधे सस्ते मॉडल से जनरेट करना सरल, सस्ता है, और वही — या बेहतर — अंतिम परिणाम देता है।
Layer B तब समझ में आता है जब आप विशेष रूप से प्रीमियम मॉडल की **सोच और ड्राफ्टिंग** चाहते हैं और स्वच्छता या गोपनीयता आवश्यकता को पूरा करने के लिए एक पुनर्लेखन पास स्वीकार करते हैं — न कि मार्क-मुक्त टेक्स्ट के सस्ते मार्ग के रूप में।
**Layer B को कब छोड़ें:**
- **गुणवत्ता स्वच्छता से अधिक मायने रखती है:** lossless पथ का उपयोग करें — Layer A Unicode स्क्रब प्लस फ़ाइल मेटाडेटा क्लीनर — और मूल गद्य रखें।
- **वैसे भी पुनर्लेखन कर रहे हैं:** एक **non-origin** मॉडल का उपयोग करें (origin मॉडल से पुनर्लेखन टेक्स्ट को फिर से स्टैम्प कर सकता है), और याद रखें कि अवशिष्ट जोखिम बना रहता है — कोई भी टूल प्रमाणित नहीं कर सकता कि विक्रेता डिटेक्टर विफल होगा।
---
## फ़ाइल फ़ॉर्मेट
| फ़ॉर्मेट | निरीक्षण | सफ़ाई |
| --- | --- | --- |
| PNG / JPEG / WebP | C2PA chunks / APP11 / RIFF `C2PA`, AI XMP संकेत | मेटाडेटा सेगमेंट हटाएँ |
| AVIF / HEIC | ISOBMFF `jumb` / XMP `uuid` बॉक्स | बॉक्स हटाएँ |
| BMP | अनुगामी गैर-इमेज बाइट्स (कोई मानकीकृत चैनल नहीं) | अनुगामी मेटाडेटा ट्रंकेट करें, फ़ाइल-आकार फ़ील्ड ठीक करें |
| GIF | Comment / XMP एप्लिकेशन एक्सटेंशन | Comment और XMP हटाएँ, `NETSCAPE2.0` लूप रखें |
| TIFF (classic + BigTIFF) | IFD टैग: XMP, EXIF, GPS, IPTC, MakerNote | टैग हटाएँ, पेलोड शून्य करें, strips रखें |
| SVG | `<metadata>`, XMP | ब्लॉक हटाएँ |
| PDF | Byte/XMP + वैकल्पिक टूल | **exiftool** फिर **qpdf**, फिर एम्बेडेड इमेज के अंदर मेटाडेटा के लिए **ghostscript**; प्रत्येक अनुपस्थित टूल एक अलग परत को कमज़ोर करता है (दस्तावेज़ स्ट्रिप, संरचनात्मक पुनर्लेखन, एम्बेडेड इमेज) |
| DOCX | docProps / customXml | props स्क्रब करें, customXml हटाएँ |
| EPUB | OPF मेटाडेटा, XHTML meta/JSON-LD, एम्बेडेड मीडिया | OPF स्क्रब करें, XHTML meta हटाएँ, मीडिया साफ़ करें + Layer A (एन्क्रिप्टेड भागों को छोड़ता है) |
| ODT | meta.xml | generator / AI-जैसा meta हटाएँ |
| HTML | meta, JSON-LD, data-ai* | टैग/attrs हटाएँ |
| Markdown | YAML frontmatter AI कुंजियाँ | कुंजियाँ हटाएँ + Layer A बॉडी |
| MP4 / MOV / M4A / M4V | ISOBMFF `jumb`/`uuid` बॉक्स (AVIF/HEIC के समान तंत्र) + `moov/udta` generator टैग | बॉक्स हटाएँ |
| WAV | RIFF `C2PA` / `LIST INFO` chunks, एम्बेडेड `id3\x20` chunk | chunks हटाएँ |
| MP3 | ID3v2 फ़्रेम (v2.3/v2.4 per-frame; v2.2 whole-tag) | मिलान किए गए फ़्रेम या पूरा टैग हटाएँ |
| FLAC | ID3v2 `GEOB` फ़्रेम में C2PA मैनिफ़ेस्ट | मिलान किया गया फ़्रेम या पूरा ID3v2 टैग हटाएँ |
FLAC समर्थन C2PA के मानकीकृत ID3v2 वाहक को कवर करता है। मूल FLAC मेटाडेटा ब्लॉक, Vorbis Comments, और waveform-डोमेन वॉटरमार्क अछूते छोड़े जाते हैं।
#### PDF को qpdf की आवश्यकता क्यों है, केवल exiftool की नहीं
ExifTool PDF को **इंक्रीमेंटली** लिखता है। `exiftool -all=` एक `%BeginExifToolUpdate` ब्लॉक जोड़ता है जो Info ऑब्जेक्ट को मुक्त करता है और trailer से `/Info` हटा देता है — लेकिन मूल मेटाडेटा बाइट्स फ़ाइल में शब्दशः बने रहते हैं, और exiftool स्वयं `-PDF-update:all=` के साथ संपादन को पूर्ववत कर सकता है। कमांड `0` पर निकलता है, व्यूअर कोई मेटाडेटा नहीं दिखाते, और फ़ाइल *बड़ी* हो जाती है, जो कि संकेत है।
एक provenance-हटाने वाले टूल के लिए यह एक मूक लीक है, इसलिए `clean_pdf` exiftool पास के बाद `qpdf --linearize` चलाता है, जो दस्तावेज़ को उसके ऑब्जेक्ट ग्राफ़ से पुनः-सीरियलाइज़ करता है और अब-असंदर्भित ऑब्जेक्ट्स को हटा देता है। `qpdf` इंस्टॉल न होने पर सफ़ाई फिर भी चलती है, लेकिन यह बताती है:```
warning: exiftool PDF edits are incremental — the original metadata bytes
remain recoverable; install qpdf for a structural rewrite
PDF के अंदर छवियों के लिए qpdf पर्याप्त क्यों नहीं है
ऊपर के दोनों पास दस्तावेज़ पर काम करते हैं: Info dictionary, XMP packet,
object graph। कोई भी किसी image XObject में नहीं उतरता, इसलिए एक स्कैन या
Photoshop export — एक पेज जो है ही एक बड़ा JPEG — जो कुछ भी छवि में ले जाती है, वह बना रहता है। एक वास्तविक Photoshop-exported PDF पर, एक
"सफल" सफाई के बाद 27 टैग बचे रह जाते हैं, जिनमें IFD0:Software, कैप्चर टाइमस्टैम्प और एक preview
thumbnail शामिल हैं; उसी छवि से जुड़ा एक C2PA manifest भी उससे बच निकलता है।
इसलिए clean_pdf एक तीसरा पास जोड़ता है, deep_images, जो Ghostscript के
pdfwrite द्वारा संचालित होता है। यह दो स्तरों में चलता है और फ़ाइल साफ़ होते ही रुक जाता है:
- Lossless। pass-through के साथ
pdfwriteobject graph से दस्तावेज़ को फिर से बनाता है जबकि संपीड़ित छवि डेटा को byte-for-byte कॉपी करता है — जिसे पहले और बाद में streams को hash करके सत्यापित किया जाता है। यह उस सब कुछ को साफ़ कर देता है जो PDF ने छवि के चारों ओर लपेटा था। Pass-through उन codecs को कवर करता है जिन्हें Ghostscript इसके लिए समर्थन देता है, JPEG (DCTDecode) और JPEG2000 (JPXDecode); Flate, CCITT और LZW छवियाँ decode और re-encode की जाती हैं, जो उन codecs के लिए व्यवहार में lossless है लेकिन byte-identical नहीं।neverउस दस्तावेज़ के लिए विकल्प है जिसकी streams को बिना छेड़े बचे रहना चाहिए। - Re-encode, केवल प्रमाण पर। JPEG के अपने APPn
segments में जो कुछ भी रहता है — APP1 में EXIF, APP11 में एक C2PA manifest, APP13 में Photoshop resources — वह उन bytes के साथ चलता है जिनसे वह जुड़ा है, इसलिए pass-through उसे संरक्षित रखता है। Rung 2 वही पास pass-through बंद करके चलाता है, और केवल तब जब rung 1 ने प्रमाणित रूप से कुछ पीछे छोड़ा हो: किसी भी mode में एक AI/C2PA marker, या,
alwaysके अंतर्गत, कोई भी बचा हुआ APPn metadata। APP0 (JFIF) और APP2 (ICC) को छोड़ दिया जाता है — पहला संरचनात्मक है और दूसरा तय करता है कि रंग कैसे पढ़े जाते हैं। Pixels प्रमाण पर खर्च किए जाते हैं, संदेह पर कभी नहीं।
deep_images लेता है auto (डिफ़ॉल्ट: rung 1 केवल तब जब markers document strip से बच गए हों, फिर rung 2 यदि वे उससे भी बच जाएँ), always (हर
PDF के लिए rung 1, camera और editor EXIF के लिए भी rung 2 तक बढ़ता हुआ), lossless (केवल rung 1 — कभी recompress नहीं, और जो भी बचे उसे सामान्य
still_has_c2pa / post_findings fields के माध्यम से रिपोर्ट करता है) और never। एक अपरिचित मान को चुपचाप auto मानने के बजाय अस्वीकार कर दिया जाता है। रिपोर्ट बताती है कि कौन से rungs चले
meta.deep_image_pass और meta.images_reencoded के माध्यम से, और जब पास छोड़ दिया जाता है तो यह उस विकल्प का नाम बताती है जो आगे जाएगा:```text
deep image pass not needed for AI/C2PA markers; pass deep_images="always"
to also clear non-AI EXIF inside images
Ghostscript इंस्टॉल किए बिना भी क्लीन अभी भी चलता है और बताता है कि वह किस तक नहीं पहुँच सका:```text
warning: metadata inside embedded images left in place; install ghostscript
for the deep image pass
पिक्सेल-डोमेन वॉटरमार्क हटाना अब एक वैकल्पिक बाहरी CtrlRegen बैकएंड के रूप में उपलब्ध है (ऊपर देखें); यह एक पुनर्जनन करने वाला रिमूवर है, कोई गारंटी नहीं। C2PA सॉफ्ट बाइंडिंग (इन-कंटेंट वॉटरमार्क जो मेटाडेटा हटाए जाने के बाद रिमोट Content Credentials मैनिफेस्ट को फिर से लिंक कर सकता है) दायरे से बाहर बना हुआ है। हार्ड-बाउंड C2PA को हटाना उन चैनलों को साफ़ नहीं करता।
क्लीन के बाद अवशिष्ट जोखिम
यह टूल सत्यापन योग्य हटाने (Unicode गणनाएँ, मेटाडेटा क्रियाएँ) और बेस्ट-एफर्ट Layer B रीराइट की रिपोर्ट करता है। यह प्रमाणित नहीं कर सकता कि विक्रेता डिटेक्टर विफल होंगे।
अवशिष्ट संकेतों को स्वयं जाँचने के लिए (वैकल्पिक, बाहरी):
| चैनल | हम क्या हटाते हैं | क्या शेष रह सकता है | बाहरी जाँच (उदाहरण) |
|---|---|---|---|
| हार्ड-बाउंड C2PA / EXIF / XMP | हाँ | सॉफ्ट-बाउंड / पिक्सेल मार्क | c2patool, Content Credentials verify |
| SynthID-श्रेणी मीडिया | वैकल्पिक पिक्सेल हटाना (बाहरी CtrlRegen); अन्यथा स्थानीय स्कोर | ऑडियो/वीडियो वॉटरमार्क; हटाने के बाद अवशिष्ट पिक्सेल वॉटरमार्क | प्रदाता टूल (जैसे Google SynthID / Vertex डिटेक्टर जहाँ उपलब्ध हो); वैकल्पिक स्थानीय reverse-SynthID स्कोरर |
| सांख्यिकीय टेक्स्ट | बेस्ट-एफर्ट रीराइट | हल्के संपादन के बाद मजबूत मार्क | कोई सार्वजनिक सार्वभौमिक डिटेक्टर नहीं; विक्रेता टूल जब उपलब्ध हों |
उद्योग दो-परत संदर्भ (C2PA + अगोचर वॉटरमार्क): Institute of AI PM guide.
वॉटरमार्क डिटेक्टर
यह सत्यापित करने के लिए विक्रेता-प्रदत्त चेकर कि कंटेंट में AI प्रोवेनेंस मार्क हैं या नहीं:
- Claude: जाँचें कि कोई फ़ाइल Claude से बनी है या नहीं — यह रिपोर्ट करने के लिए कि फ़ाइल बनाने में Claude शामिल था या नहीं, छवियों, वीडियो और ऑडियो में C2PA कंटेंट क्रेडेंशियल पढ़ता है; ब्राउज़र में चलता है। Claude का टेक्स्ट-वॉटरमार्क डिटेक्शन API वर्तमान में निजी पूर्वावलोकन में है।
- OpenAI: OpenAI-जनित कंटेंट सत्यापित करें — एक छवि या ऑडियो फ़ाइल अपलोड करें और OpenAI प्रोवेनेंस संकेतों (C2PA मेटाडेटा और SynthID वॉटरमार्क) की जाँच करें। एक प्रोग्रामेटिक API भी उपलब्ध है।
- Google DeepMind: SynthID — AI-जनित छवियों, ऑडियो, टेक्स्ट और वीडियो के लिए Google की वॉटरमार्किंग तकनीक, जिसमें अगोचर मार्क कैसे एम्बेड और डिटेक्ट किए जाते हैं इसका अवलोकन शामिल है।
- Gemini: AI-जनित छवियों, वीडियो और ऑडियो को सत्यापित करें — SynthID वॉटरमार्क और Content Credentials का उपयोग करके Gemini ऐप में फ़ाइलों को सत्यापित करने के लिए Google की मार्गदर्शिका, जिसमें अपलोड सीमाएँ और परिणाम कैसे पढ़ें शामिल है।
हटाने के विकल्प (सारांश)
| विकल्प | क्या हटाता है | नोट्स |
|---|---|---|
| Unicode स्क्रब (Layer A) | ZWSP, bidi, टैग, विदेशी स्पेस, … | टेक्स्ट के लिए सुरक्षित डिफ़ॉल्ट |
| रीराइट (Layer B) | सांख्यिकीय टोकन मार्क (बेस्ट-एफर्ट) | हमेशा स्किल द्वारा प्रस्तावित; शैली की कीमत पर — देखें Disclaimer |
| कंटेनर/मेटाडेटा स्ट्रिप | फ़ाइल प्रोवेनेंस | फ़ॉर्मेट तालिका देखें |
| CtrlRegen पिक्सेल हटाना (वैकल्पिक) | पिक्सेल-डोमेन छवि मार्क (SynthID-श्रेणी, StegaStamp, Tree-Ring, StableSignature) | बाहरी बैकएंड; भारी कंप्यूट; रूढ़िवादी तीव्रता डिफ़ॉल्ट |
| DiffusionPurification पिक्सेल हटाना (वैकल्पिक) | पिक्सेल-डोमेन छवि मार्क (Tree-Ring-श्रेणी) | MarkDiffusion बैकएंड; ब्लाइंड पुनर्जनन (CtrlRegen से अधिक ड्रिफ्ट); रूढ़िवादी तीव्रता डिफ़ॉल्ट |
| Open-weight स्थानीय मॉडल | मूल मॉडल से पुनः-स्टैम्पिंग से बचें | परिचालन विकल्प |
मैट्रिक्स: skills/remove-ai-marks/references/removal-matrix.md.
नैतिकता और अस्वीकरण
देखें skills/remove-ai-marks/references/ethics.md. आपके कंटेंट पर गोपनीयता और शोध के लिए — शैक्षणिक धोखाधड़ी या झूठे "मानव-लिखित" दावों के लिए नहीं।
जिम्मेदार उपयोग: यह प्रोजेक्ट उस कंटेंट के लिए है जिसका आप स्वामी हैं या जिसे संसाधित करने के लिए आप अधिकृत हैं। उपयोगकर्ताओं को स्थानीय नियमों का पालन करना चाहिए और इसका जिम्मेदारी से उपयोग करना चाहिए। डेवलपर्स उपयोगकर्ताओं द्वारा संभावित दुरुपयोग के लिए किसी भी देयता से इनकार करते हैं।
इकोसिस्टम
तृतीय-पक्ष प्रोजेक्ट जो इस रिपॉज़िटरी को रैप या पूरक करते हैं, केवल खोज-योग्यता के लिए सूचीबद्ध। वे इस प्रोजेक्ट द्वारा अनुरक्षित, समर्थित या समर्थन प्राप्त नहीं हैं। यह प्रोजेक्ट उनके कोड की समीक्षा नहीं करता, उनके व्यवहार या गारंटी की पुष्टि नहीं करता, या इस सूची से आपके द्वारा इंस्टॉल या चलाई गई किसी भी चीज़ की जिम्मेदारी नहीं लेता। प्रत्येक प्रोजेक्ट अपने स्वयं के लाइसेंस, अनुरक्षकों और दस्तावेज़ीकरण द्वारा शासित है — उपयोग से पहले उन्हें पढ़ें।
MetaClean — डेस्कटॉप GUI
MetaClean एक स्वतंत्र MIT-लाइसेंस प्राप्त Rust/Tauri डेस्कटॉप एप्लिकेशन (Windows, macOS, Linux) है जो ड्रैग-एंड-ड्रॉप मेटाडेटा सफाई के लिए एक पैकेज्ड नेटिव GUI प्रदान करता है, जिसमें सिस्टम ट्रे और Explorer एकीकरण शामिल है। यह एक अलग कोडबेस है: यह इस रिपॉज़िटरी की Python सेवा को कॉल नहीं करता, और इसके समर्थित फ़ॉर्मेट और सफाई गारंटी इस प्रोजेक्ट से भिन्न हैं। विवरण के लिए इसका README देखें।
unmark-web — ब्राउज़र वेब UI
unmark-web एक स्वतंत्र, MIT-लाइसेंस प्राप्त स्टैटिक वेब क्लाइंट है। यह टेक्स्ट से अदृश्य Unicode मार्क हटाता है और छवियों से प्रोवेनेंस मेटाडेटा को पूरी तरह ब्राउज़र में स्ट्रिप करता है, और उन फ़ॉर्मेट के लिए वैकल्पिक रूप से इस रिपॉज़िटरी की HTTP सेवा को कॉल कर सकता है जिन्हें यह स्थानीय रूप से संभाल नहीं सकता। यह एक अलग कोडबेस है और इस प्रोजेक्ट से संबद्ध नहीं है; दायरे और सीमाओं के लिए इसका README देखें।
DropMarks — macOS GUI
DropMarks एक स्वतंत्र MIT-लाइसेंस प्राप्त macOS SwiftUI एप्लिकेशन है। यह उन stdlib स्क्रिप्ट्स के वेंडर किए गए स्नैपशॉट के माध्यम से इस रिपॉज़िटरी के inspect_file.py / clean_file.py (और वैकल्पिक रूप से rewrite_text.py) को कॉल करता है। यह एक अलग कोडबेस है और इस प्रोजेक्ट से संबद्ध नहीं है; दायरे और सीमाओं के लिए इसका README देखें।
एक प्रोजेक्ट जोड़ना
यहाँ एक प्रोजेक्ट पंजीकृत करने के लिए, एक संक्षिप्त प्रविष्टि जोड़ते हुए PR खोलें — प्रोजेक्ट का नाम, यह क्या रैप या जोड़ता है, और इसकी अपनी रिपॉज़िटरी का लिंक। प्रविष्टियाँ संक्षिप्त और तथ्यात्मक रखें; इस प्रोजेक्ट के साथ संगतता या इसके द्वारा समर्थन का दावा न करें। सूचीबद्ध प्रोजेक्ट को इस रिपॉज़िटरी पर निर्मित या एकीकृत होना चाहिए — उदाहरण के लिए, इसकी सेवा को कॉल करके या इसके डिटेक्शन इंजन का पुनः उपयोग करके — न कि केवल स्वतंत्र रूप से उसी समस्या का समाधान करके। कृपया ऐसे नामों से बचें जो watermarks-remover से शुरू होते हैं या उससे निकटता से मिलते-जुलते हैं — मिलते-जुलते नामों से यह बताना कठिन हो जाता है कि कौन सा प्रोजेक्ट कौन सा है।
Pre-commit हुक
CI गेटिंग पहले से मौजूद है (audit_dir.py का SARIF निर्यात, देखें Coverage matrix संदर्भ) — नीचे दिए गए pre-commit हुक उसी श्रेणी की समस्या को पहले पकड़ लेते हैं, इससे पहले कि कोई मार्क्ड फ़ाइल कमिट भी हो। दोनों मौजूदा CLIs (audit_dir.py / clean_file.py) को रैप करते हैं — कोई अलग डिटेक्शन लॉजिक नहीं।```yaml
.pre-commit-config.yaml
repos:
- repo: https://github.com/guillaumemeyer/watermarks-remover
rev: v0.5.0 # pin to a tag/commit
hooks:
- id: watermarks-remover-check # fails the commit if marks are found
- id: watermarks-remover-clean # opt-in: cleans staged files in place instead
`watermarks-remover-check` कमिट को विफल करता है और निष्कर्षों की सूची देता है; `watermarks-remover-clean` ऑप्ट-इन है और स्टेज किए गए फ़ाइलों को यथास्थान फिर से लिखता है (एक्ज़िट 1 ताकि आप डिफ़ की समीक्षा करें और फिर से स्टेज करें — यह उसी परंपरा का पालन करता है जैसे `ruff --fix` जैसे ऑटो-फिक्सिंग हुक)। जब क्लीनर किसी फ़ाइल को बिल्कुल भी प्रोसेस नहीं कर पाता — वह क्रैश हो गया, किल कर दिया गया, या कोई रिपोर्ट उत्पन्न नहीं हुई — तो `watermarks-remover-clean` उस फ़ाइल का नाम लेता है और इसके बजाय एक्ज़िट 3 करता है, ताकि विफल हुए क्लीनर को कभी भी पहले से साफ़ फ़ाइल न समझा जाए। इनमें से किसी को भी मैन्युअल रूप से `python3 service/scripts/check_staged.py <files...>` / `clean_staged.py <files...>` के साथ चलाएँ।
## परीक्षण```bash
python3 -m venv .venv && .venv/bin/pip install pytest
.venv/bin/python -m pytest # or: make test
make smoke # quick CLI smoke on fixtures
Changelog
v0.7.0 — /clean Layer B पुनर्लेखन, वॉटरमार्क-चोरी मॉड्यूल, ऑडियो/वीडियो वॉटरमार्क हटाना, और बेंचमार्क/टूलिंग विस्तार
v0.7.0, Layer B सांख्यिकीय-चिह्न पुनर्लेखन को /clean सेवा में ही लाता है, जो एक कॉन्फ़िगर करने योग्य, बेंचमार्क-ट्यून्ड रणनीति ([email protected],[email protected]) द्वारा संचालित है। इसके साथ: एक ब्लैक-बॉक्स वॉटरमार्क-चोरी मॉड्यूल, विनाशकारी ऑडियो और प्रति-फ़्रेम वीडियो वॉटरमार्क हटाना, एक काफ़ी समृद्ध पुनर्लेखन बेंचमार्क, और ढेर सारे हार्डनिंग, सुरक्षा, और टूलिंग सुधार।
सेवा में Layer B पुनर्लेखन
/cleanLayer A के बाद टेक्स्ट के लिए Layer B पुनर्लेखन चलाता है। डिफ़ॉल्टconfig/clean_strategy.jsonसे आता है; प्रति-अनुरोधoptions.strategyइसे ओवरराइड करता है, और/clean400 के साथ अस्वीकार करता है जब आवश्यक बैकएंड कॉन्फ़िगर नहीं होता (#315)। कॉन्फ़िग प्राथमिकता:--strategy-config>WATERMARKS_CLEAN_STRATEGY_FILE>config/clean_strategy.json।- नई
mlmपुनर्लेखन रणनीति: सामग्री शब्दों के एक अंश को मास्क करें औरroberta-largeसे इनफ़िल करें — एक गैर-ऑटोरेग्रेसिव स्थानीय संपादन, इसलिए आउटपुट मूल टोकन स्ट्रीम को मास्क्ड-LM भविष्यवाणियों के साथ मिलाता है (#311)। humanizeरणनीति अब humanizer-skill पास को नियतात्मक रूप से लागू करती है (सीधे उद्धरण, कोई em/en डैश नहीं, फ़िलर संकुचन,utilize→use) और प्रॉम्प्ट में human-writer नियमों का नाम देती है (#311)।rewrite_text.pyको एक--strategyCLI पथ मिला।- पुनर्लेखन शुद्धता: लेक्सिकल डाइवर्जेंस में यूनिकोड शब्द टोकनाइज़ेशन (#305); राउंडिंग से पहले कच्चे मार्जिन की तुलना करें और चयन मेटाडेटा / रैंक किए गए p-मान रिकॉर्ड करें (#249)।
बेंचमार्क
- SynthID रेसिपी खोज + मज़बूत माप (#280); पुनर्लेखन शब्दावली का नाम बदला, क्रॉस-इनपुट खोज, और humanize-last क्रम (#302); केवल उन रणनीतियों की सिफ़ारिश करें जो humanize पॉलिश के बाद भी पास होती हैं (#307)।
- मानव-समानता बैकएंड के रूप में Pangram बल्क API (#296); 30-दस्तावेज़ कॉर्पस के साथ हार्डन्ड न्यूनतम-पुनर्लेखन-स्तर बेंचमार्क (#257); मान्य वेट ग्रिड + विस्तारित रेसिपी खोज (#294); पोलिश बेंचमार्क कॉर्पस (#295)।
वॉटरमार्क चोरी
- नया ब्लैक-बॉक्स वॉटरमार्क-चोरी मॉड्यूल और प्रॉम्प्ट-कॉर्पस डाउनलोडर (#303); start-over प्रोब विफलता पर पुरानी स्थिति साफ़ करें (#310)।
ऑडियो / वीडियो / छवि
- silentcipher/AudioSeal/WavMark के लिए विनाशकारी ऑडियो वॉटरमार्क हटाने की श्रृंखला (tempo + pitch + EQ + low-bitrate re-encode → M4A) (#266)।
- प्रति-फ़्रेम TrustMark वीडियो शुद्धिकरण जो टेम्पोरल वोट को ध्वस्त कर देता है (#265)।
- MP4/MOV/AVIF/HEIC पर C2PA सामग्री-उद्गम
uuidबॉक्स पहचाना गया (#264)। - स्ट्रिपिंग के दौरान काटे गए MP4 टेल्स को संरक्षित करें (#242); ऑडियो re-encode गंतव्य को कंटेनर-क्लीन गंतव्य से अलग रखें (#278)।
- पोस्ट-क्लीन स्कैन में छोड़े गए exiftool आउटपुट और अनावश्यक SynthID को छोड़ें (#261); जब exiftool किसी PDF को प्रोसेस नहीं कर सकता तो साफ़-सुथरे ढंग से डिग्रेड करें (#281)।
- डीकंप्रेस्ड PNG
zTXt/iTXtको 1 MiB पर सीमित करें (#308); SVG XML DOCTYPE/ENTITY घोषणाएँ हटाएँ (#288); DOCX बाइनरी सदस्यों को बाइट-सुरक्षित रखें (#314); OOXMLAppVersionसंरक्षित करें (#289)।
HTTP सेवा और CLI
- विदेशी स्पेस रखने का
/cleanविकल्प, CLI की तरह (#274);/inspectसंदिग्ध पेलोड में स्पष्ट साक्ष्य वर्गों को उजागर करता है (#277); HTTP अनुरोध लॉग में टाइमस्टैम्प (#256); HTTP SynthID स्कोरिंग औरinspect_*में थ्रेड पेलोड बाइट्स ताकि अनावश्यक रीड-बैक से बचा जाए। clean_file.pyको-q/--quiet/--only-changedमिला (#254)।
स्किल्स, प्लगइन और हुक्स
clean-user-facing-textके लिए स्टाइलोमेट्री स्कोरिंग और डिटेक्टर लीवर (#258); PostToolUse हुक लॉन्चर क्रॉस-प्लेटफ़ॉर्म बनाया गया (#255); pre-commit हुक बाइट-समान क्लीन गैर-टेक्स्ट फ़ाइलों को बदला हुआ मानता है (#238)।
ऑडिट
audit_dir.pyस्रोत, दस्तावेज़, और i18n फ़ाइलों को स्कैन करता है जिन्हें राउटर छोड़ गया था (#284);.ts/.tsx/.jsx/.gdस्कैन करता है और प्रारूपों में स्पेस कॉन्फ़िडेंस को संरेखित करता है (#273);audit_website.py --sarifसमर्थन (#194); इन-प्लेस बैकअप, क्लीन-फ़ाइल स्थिति, SynthID निर्णय, काटे गए ID3v2, और zip रूटिंग को हार्डन करें (#201)।
सुरक्षा
- data-URI और JSON-LD स्कैन में बहुपद ReDoS हटाएँ (#306); SSRF को रोकने के लिए SynthID स्कोरर में HTTP रीडायरेक्ट ब्लॉक करें (#252)।
CI, टूलिंग और दस्तावेज़
- CI विफल होता है जब वैकल्पिक बैकएंड आवश्यकताएँ हल नहीं हो सकतीं (#301); Docker इमेज ffmpeg को उपयोगी बताती है और Ghostscript इंस्टॉल करती है (#272); निर्भरता बम्प्स (cython #299, scipy #298, ruff #297, docker/setup-buildx-action #237)।
- दस्तावेज़: Watermark Detectors अनुभाग, ETH SRI "Probing SynthID" ब्लॉग संदर्भ, Ecosystem नीति (ClaudeWatermarks हटाएँ; सूचीबद्ध परियोजनाओं को इस रेपो का उपयोग करने की आवश्यकता) (#292)।
v0.6.0 — व्यापक प्रारूप कवरेज, Layer A हार्डनिंग, प्लगइन और हुक वितरण, और डिटेक्शन-निर्देशित पुनर्लेखन
प्रारूप और कंटेनर कवरेज
- AVIF / HEIC: मूल stdlib मेटाडेटा और C2PA स्ट्रिपिंग (#84, #85)
- BMP / GIF / TIFF: stdlib डिटेक्शन, निरीक्षण, और मेटाडेटा सफ़ाई — GIF कमेंट/XMP एक्सटेंशन हटा दिए जाते हैं जबकि
NETSCAPE2.0लूपिंग और अन्य एनिमेशन चंक्स संरक्षित रहते हैं; TIFF IFD मेटाडेटा (XMP/EXIF/GPS/IPTC/MakerNote) हटा दिया जाता है, पेलोड शून्य किए जाते हैं और स्ट्रिप ऑफ़सेट रखे जाते हैं, क्लासिक और BigTIFF दोनों के लिए; BMP ट्रेलिंग मेटाडेटा को काट दिया जाता है और फ़ाइल-साइज़ फ़ील्ड फिर से लिखा जाता है (#107) - EPUB: stdlib कंटेनर सफ़ाई — OPF मेटाडेटा और XHTML meta/JSON-LD स्क्रब किए जाते हैं, एम्बेडेड रास्टर/SVG मीडिया हटाया जाता है, XHTML बॉडी टेक्स्ट पर Layer A लागू होता है, मार्कर-वाहक मेटाडेटा भाग हटा दिए जाते हैं, और OCF-एन्क्रिप्टेड भाग अछूते पास हो जाते हैं (#107)
- XLSX / PPTX / DOCX (OOXML): मूल stdlib कंटेनर मेटाडेटा, टेक्स्ट, और एम्बेडेड मीडिया स्क्रबिंग; DOCX
docPropsउद्गम फ़ील्ड हमेशा खाली करें;customXmlहटाने के बाद लटकते संबंधों को काटें; DOCX/ODT बॉडी टेक्स्ट पर Layer A चलाएँ; Layer A स्क्रब से पहले XML एंटिटी डिकोड करें (#91, #100, #76, #83, #73, #80, #74, #81, #142) - SGML/वेक्टर कंटेनर: SVG/ODT के लिए रैखिक-समय मेटाडेटा स्ट्रिपिंग (GHSA-7vpp-96qp-j9wh) (#147); SVGs, HTML, और Markdown में एम्बेडेड रास्टर डेटा URIs को पुनरावर्ती रूप से निरीक्षण और साफ़ करें (#87, #88)
- ऑडियो / वीडियो: MP4/MOV, WAV, और MP3 के लिए AI/C2PA मेटाडेटा स्ट्रिपिंग (#139); WAV RIFF C2PA चंक डिटेक्शन और रिमूवल; FLAC C2PA मेटाडेटा समर्थन; आंशिक ID3v2 फ़्रेम पार्सिंग अस्वीकार करें (#232); मेटाडेटा स्ट्रिप करते समय MP4 मीडिया ऑफ़सेट संरक्षित करें (#183)
- PDF: एम्बेडेड छवियों के अंदर रहने वाले मेटाडेटा तक पहुँचें और XMP स्ट्रिप करने के लिए PDF का आकार बदलना बंद करें; exiftool इंस्टॉल हो या न हो, डीप-इमेज पास चलाएँ; JPEG मार्कर फ़िल बाइट्स का सम्मान करें और एक सेगमेंट वॉकर साझा करें