github guillaumemeyer/watermarks-remover v0.6.0
v0.6.0 — wider format coverage, Layer A hardening, plugin & hook distribution, and detection-guided rewriting

4 hours ago

v0.6.0 is a big breadth release: the cleaners now reach many more formats, Layer A (invisible-Unicode) scrubbing is much more thorough, and the skill can be installed and run as a Claude Code plugin with a deterministic PostToolUse hook so files get cleaned whether or not the model cooperates. On the detection side, Layer B rewriting is now iterative and evaluation-driven, and a new stdlib-only keyed-Gumbel (EXP) detector lets you verify a mark clears without a GPU, model, or logits.

Everything in v0.5.0 keeps working — the CLIs remain the primary local interface, and the HTTP service now offers batch endpoints for cleaning, inspecting, and detecting in bulk.

Format & container coverage

  • AVIF / HEIC: native stdlib metadata and C2PA stripping (#84, #85)
  • BMP / GIF / TIFF: stdlib detection, inspection, and metadata cleaning — GIF comment/XMP extensions dropped while NETSCAPE2.0 looping is preserved; TIFF IFD metadata (XMP/EXIF/GPS/IPTC/MakerNote) dropped with payloads zeroed and strip offsets kept (classic and BigTIFF); BMP trailing metadata truncated with the file-size field rewritten (#107)
  • EPUB: stdlib container cleaning — OPF metadata and XHTML meta/JSON-LD scrubbed, embedded raster/SVG media stripped, Layer A applied to XHTML body text, marker-carrying parts dropped, OCF-encrypted parts untouched (#107)
  • XLSX / PPTX / DOCX (OOXML): native stdlib container metadata, text, and embedded media scrubbing; empty DOCX docProps provenance fields; prune dangling relationships after customXml removal; run Layer A over DOCX/ODT body text; decode XML entities before the Layer A scrub (#91, #100, #76, #83, #73, #80, #74, #81, #142)
  • SVG / ODT: linear-time metadata stripping (GHSA-7vpp-96qp-j9wh) (#147); recursively inspect and clean embedded raster data URIs in SVGs, HTML, and Markdown (#87, #88)
  • Audio / video: AI/C2PA metadata stripping for MP4/MOV, WAV, and MP3 (#139); WAV RIFF C2PA chunk removal; FLAC C2PA support; reject partial ID3v2 frame parsing (#232); preserve MP4 media offsets when stripping metadata (#183)
  • PDF: reach metadata inside embedded images and stop resizing the PDF to strip XMP; run the deep-image pass even without exiftool; honour JPEG marker fill bytes and share one segment walker
  • PNG: detect AI generator product names in PNG text metadata; detect AI markers in compressed PNG text (#127); keep the truncated tail instead of dropping it in png/isobmff strips (#182)

Layer A (invisible Unicode) hardening

  • Consolidated Layer A hardening (#133): strip reserved Default_Ignorable code points with no legitimate interchange use (U+2065, U+FFF0U+FFF8, U+E0000, U+E0080U+E00FF, U+E01F0U+E0FFF), the 66 noncharacters, and three blank-rendering Default_Ignorable carriers the Cf catch-all never saw (U+180F, U+3164, U+FFA0) — each with the same in-context preservation as its already-covered siblings, in both the service engine and the vendored lightweight-skill copy
  • Stop stripping visible-layout format controls next to their own script: Egyptian hieroglyph quadrat controls (U+13430U+1343F), Duployan shorthand controls (U+1BCA0U+1BCA3), and musical beam/tie/slur/phrase controls (U+1D173U+1D17A) are preserved when adjacent to their own script and still stripped (and flagged) when floating between unrelated text; --strip-emoji-glue paranoid mode still strips them everywhere
  • Emoji / script polish: preserve VS16 after emoji singletons outside the block ranges; preserve script joiners, flag emoji, and Arabic Cf marks; preserve multilingual Unicode during text cleanup (#34)

Layer B rewriting & watermark detection

  • Iterative, detection-guided Layer B rewriting: each round generates --candidates variants (default 1, WATERMARKS_REWRITE_CANDIDATES) and --max-loops caps evaluation rounds, stopping as soon as an attempt passes detection. Evaluator priority: MarkLLM (--markllm-scheme) > bigram-Jaccard lexical divergence. rewrite_text.py --json-stats now reports evaluator / max_loops / attempts_made / passed and per-attempt candidate_scores (#153)
  • Keyed-Gumbel (Aaronson EXP) same-key verification: new stdlib-only detect_gumbel.py (no GPU, model, or logits) implements the model-free replay test with an exact Gamma-tail p-value and repeated-window masking. rewrite_text.py --gumbel-key (env WATERMARKS_GUMBEL_KEY, preferred) makes it the iterative-loop evaluator (priority: gumbel > markllm > lexical divergence), and it's exposed as gumbel in /capabilities and /detect (#190). Same-key-only, not a vendor oracle; the key is never logged
  • Benchmarks: multi-scheme MarkLLM text benchmark and detection (#188); reproducible SynthID-text removal benchmark (#145, default variants paraphrase:3) with attempts-per-document in the report and CSV; --rewrite-loops mirrors --max-loops
  • Detection: vendor text-watermark detection (Gemini SynthID, Claude seam, MarkLLM) plus a SynthID image scorer sidecar (#109); zero-LLM statistical and stylometric AI text detector for CI and audits (#68, #69)

Distribution: plugin, hooks, and skill installs

  • Claude Code plugin & marketplace (.claude-plugin/plugin.json + marketplace.json) — both skills install with /plugin marketplace add guillaumemeyer/watermarks-remover then /plugin install watermarks-remover@watermarks-remover, and update in place. make plugin-validate runs claude plugin validate . --strict
  • install_skill.py grew --target (claude-code, claude-project, cowork, cursor) and a --skill selector, plus --list, --link, and CLAUDE_CONFIG_DIR. The cowork target builds a reproducible upload bundle (dist/<skill>.zip) with a single top-level skill directory and validates against the Agent Skills packaging rules and the 30 MB upload limit
  • Deterministic PostToolUse hook (hooks/hooks.json + hook_written_file.py): after the agent writes a file the harness runs the hook whether or not the model cooperates. check (default) reports marks to the model; clean strips them in place and tells the model the file moved, swapping only on a real difference so clean files keep their mtime. Mode comes from the plugin's hook_mode setting or WATERMARKS_HOOK_MODE. A hook still can't rewrite the assistant's chat message — no such hook point exists
  • Pre-commit hook integration for staged-file checking/cleaning (#138); lightweight Cursor text skill (#35)

HTTP service

  • Batch endpoints: POST /clean/batch and /inspect/batch (#137) plus POST /detect/batch (#151)
  • Preserve image format extensions in /clean and use safe writes in av_meta (#150)

Audit, security & reliability

  • audit_dir.py gained multi-worker concurrency and SARIF 2.1.0 export (#101, #102)
  • Security: refuse DTD/entity bombs in the sitemap parser (GHSA-pjg6-92pm-mmcf) (#146); route website binary formats to their real scanners (#177)
  • Reliability: a second --in-place run preserves the original .bak; keep collected evidence when a later zip member fails to read (#175); truncated ISOBMFF containers still run the C2PA byte-scan fallback (#176); distinguish a failed cleaner from an already-clean file (#159, #161); treat a failed c2patool run as inconclusive rather than "no C2PA" (#156); an unreadable text file is a failed scan, not a clean one (#169); validate clean option types (#111); never auto-select MPS device for text watermark detection (#99); Windows subprocess_creationflags fixes and no child console window

CI, tooling & docs

  • Ruff linting and formatting with CI enforcement (#103); macOS added to the test matrix (#152); CodeRabbit config (#222); CODEOWNERS for CODE_OF_CONDUCT/LICENSE and main-review owners; copyright attributed to Guillaume Meyer and contributors (#228)
  • Docs: voice-preserving rewrite guidance; Ecosystem additions (ClaudeWatermarks, unmark-web) plus a note discouraging look-alike names; arXiv 2402.14904 reference; Windows auto-start guide; portable base64 curl examples; Cursor-skill text engine pinned to the service copy (#96)

A big thanks to everyone who filed issues, reviewed PRs, and ran the harnesses — this release is a team effort. 🎉

Full changelog: README

Don't miss a new watermarks-remover release

NewReleases is sending notifications on new releases.