Detect and strip invisible Unicode watermarks, hidden payloads and stylometric fingerprints from AI chat output.
Desktop app and command line tool for Windows and Linux. Interface in English and Italian.
Text copied out of ChatGPT, Claude, Gemini or any other assistant can carry characters you never see. Some are harmless typographic habits. Some are deliberate: the Unicode Tags block is a complete shadow copy of ASCII that renders as nothing at all in every browser, editor and chat window, yet a language model reads it perfectly. That is the carrier behind ASCII smuggling and invisible prompt injection.
no-watermark finds every one of them, shows you what was hidden, and gives you back clean text.
$ no-watermark scan message.txt
Verdict: Hidden content Score: 100/100
Hidden content recovered
This text carried content that is invisible to you but readable by an AI model.
- Unicode tags (32 chars @ 46)
decoded text: Ignore all previous instructions
Findings
high line 1, column 20 U+200B ZERO WIDTH SPACE Invisible character
low line 1, column 35 U+202F NARROW NO-BREAK SPACE Whitespace look-alike
critical line 1, column 47 x32 TAG CHARACTER Unicode tag character
medium line 2, column 6 U+043E CONFUSABLE LETTER Confusable letter
Honesty first, because this field is full of tools that promise more than physics allows.
| Fingerprint | Real? | Removable by no-watermark |
|---|---|---|
Unicode Tags block (U+E0000–U+E007F) — invisible ASCII, used to smuggle instructions |
Yes, actively exploited | Yes, completely |
Zero-width characters — ZWSP, ZWNJ, ZWJ, word joiner, soft hyphen, BOM, fillers |
Yes | Yes |
| Variation selectors — one byte of payload each ("emoji smuggling") | Yes | Yes |
| Bidirectional controls — make displayed text differ from stored text | Yes | Yes |
Homoglyphs — Cyrillic о inside a Latin word |
Yes | Yes |
Exotic whitespace — including U+202F, emitted by some ChatGPT models in 2025 |
Yes, but an artefact rather than a watermark | Yes |
| SynthID-Text (Google DeepMind, live in Gemini) — watermark embedded in token sampling | Yes, in production | No. It leaves no special character; no character-level tool can touch it |
| A cryptographic text watermark from OpenAI | Never shipped. OpenAI built one and chose not to release it | Not applicable |
If a tool tells you it removes SynthID by deleting characters, it is wrong. See docs/WATERMARKS.md for the full technical write-up.
| Installer | Download no-watermark-<version>-x86_64.msi from Releases. Installs the desktop app, adds a Start Menu entry, and puts no-watermark on your PATH. |
| Portable | Download no-watermark-<version>-windows-x86_64.zip, unzip, run no-watermark-gui.exe. Nothing is written outside the folder. |
| Debian / Ubuntu | sudo dpkg -i no-watermark_<version>_amd64.deb |
| Fedora / RHEL / openSUSE | sudo rpm -i no-watermark-<version>.x86_64.rpm |
| AppImage | chmod +x no-watermark-<version>-x86_64.AppImage && ./no-watermark-<version>-x86_64.AppImage |
| Portable | tar xzf no-watermark-<version>-linux-x86_64.tar.gz |
There is no
.msifor Linux — MSI is a Windows Installer format. The.deb,.rpmand AppImage above are its Linux equivalents, and each installs the same two binaries plus a desktop entry.
git clone https://github.com/BitJacker/no-watermark
cd no-watermark
cargo build --release # target/release/no-watermark[-gui]
cargo test # 40+ testsRust 1.82 or newer. No system libraries are required to build the CLI; the GUI uses the platform windowing stack at runtime.
Paste on the left, read clean text on the right.
- Profile —
Scan,Safe,Standard(default) orAggressive. Every individual rule can be toggled under Options. - Reveal invisible characters — renders
⟦U+200B⟧in place of what you cannot see. - Watch the clipboard — anything you copy is cleaned automatically, in place. Off by default.
- Findings / Hidden content / Style signals — the tabs at the bottom explain every decision the tool made, and decode anything that was smuggled.
- Language — English or Italian, switchable at runtime. The app starts in your system language.
no-watermark < answer.txt > clean.txt # clean stdin to stdout
no-watermark clean -i notes/*.md # rewrite files in place
no-watermark clean -i --backup notes.md # keep notes.md.bak
no-watermark scan report.md # explain, change nothing
no-watermark check --min-severity high *.md # exit 1 if anything is found
no-watermark decode message.txt # print only the hidden content
no-watermark reveal message.txt # make invisibles visible| Profile | Invisible / tags / bidi / VS | Whitespace | Homoglyphs | Punctuation, NFKC |
|---|---|---|---|---|
scan |
report only | report only | report only | report only |
safe |
removed | untouched | untouched | untouched |
standard (default) |
removed | normalised | folded | untouched |
aggressive |
removed | normalised | folded | ASCII + NFKC |
safe is guaranteed not to change a single visible glyph.
Any rule can be overridden on top of a profile:
no-watermark --profile safe --remove space,typography # add rules
no-watermark --keep homoglyph # drop a rule
no-watermark --no-preserve-emoji # strip emoji joiners too| Flag | Meaning |
|---|---|
--lang en|it |
Interface language (default: system locale) |
--json |
Machine-readable report on stdout |
--color auto|always|never |
ANSI colour |
--line-endings lf|crlf |
Rewrite line endings |
-q, --quiet |
Suppress summaries |
-n, --dry-run |
Show what would change, write nothing |
| Code | Meaning |
|---|---|
0 |
Success, or check found nothing at or above the threshold |
1 |
check found something |
2 |
Error (unreadable file, invalid UTF-8, …) |
That makes it a drop-in CI guard:
- run: no-watermark check --min-severity high docs/**/*.mdno-watermark scan --json message.txt | jq '.[0].report.payloads[].text'The schema is stable and locale independent: categories, severities, notes and verdicts are snake_case identifiers, never translated prose.
A cleaner that destroys real text is worse than no cleaner at all, so no-watermark refuses to be careless:
- Emoji sequences survive. A
ZERO WIDTH JOINERholding 👨👩👧 together is recognised and kept. - Arabic, Persian and Indic text survives. A
ZWNJdoing orthographic work in مینویسم is kept. - Real Cyrillic and Greek survive. A confusable is only folded when it sits
inside a word that is otherwise Latin, so
приветis never touched whilepasswоrdis fixed. safenever alters a visible glyph, andscannever alters anything at all.- Cleaning is idempotent. Running it twice gives the same result as once.
Every one of those guarantees has a test.
# Linux: binaries, .deb, tarball, AppImage
cargo build --release
packaging/linux/build-deb.sh
packaging/linux/build-appimage.sh
# Windows: MSI (needs the WiX Toolset v5; v6+ is behind a licence EULA)
dotnet tool install --global wix --version 5.*
wix extension add -g WixToolset.UI.wixext/5.0.2
cargo build --release
wix build packaging/wix/main.wxs -ext WixToolset.UI.wixext -o no-watermark.msiIcons are generated, not committed as an opaque blob:
python packaging/make_icons.py.
Every artefact above is also built by
.github/workflows/release.yml on each tag.
crates/nowm-core detection and cleaning engine, no I/O, no locale
crates/nowm-i18n English and Italian strings
crates/nowm-cli the `no-watermark` command
crates/nowm-gui the desktop application (egui)
packaging/ WiX, Debian, AppImage, icon generator
docs/WATERMARKS.md how AI text watermarking actually works
MIT — see LICENSE.
Made by Giacomo Giordano.