Agate Watermark
Putting the AI mark back after VAE Decode
- images
- agate
- images
Agate marks its own output - invisible watermark, provenance tags, the lot. But the Agate Sampler hands you a LATENT, and a LATENT cannot carry a watermark. By the time VAE Decode turns that latent into pixels, the mark is gone. This node puts it back.
When you need it (and when you don't)
Agate Generate and Agate Plan Viewer mark their images for you; watermark and metadata are on by default. So if you're on the all-in-one path, you can skip this node entirely.
You need it on the Sampler → VAE Decode path: the one where you keep the LATENT for img2img, denoise experiments, or the sampler's live preview. That graph goes Loader → Agate Sampler → VAE Decode → Agate Watermark → Save Image - the pack's agate_003_txt2img_watermark.json example. Quieter second use: the node also drops ai_generated and an agate object into ComfyUI's extra_pnginfo, so whatever save node runs after it picks up the tags without you swapping anything.
What the mark actually is
It's dwtDctSvd from ShieldMnt's invisible-watermark library, with a fixed 64-bit payload: the ASCII bytes of AGATE plus the release, so AGATE003. The pack reimplements the algorithm in plain numpy - bit-identical to the library, per its own tests - so you need neither OpenCV nor PyWavelets.
Rough mechanics: RGB goes to YUV, a Haar wavelet transform on the chroma channel, the detail band cut into 4×4 blocks, a DCT per block, and the largest singular value of each block nudged (step size 36) to encode one bit. Decoding runs the reverse, majority-votes per bit, and calls the image "detected" at 90% bit accuracy - 58 of 64 bits. The releases' payloads differ by a bit or two, so detected really means "an Agate output", and the release is only named when all eight bytes read exactly.
One detail that saves a false alarm: marked pixels go back into the graph as (v + 0.25) / 255, not v / 255, so ComfyUI's truncating save can't knock a pixel down by one and shred the readback.
Inputs and output
- images - the IMAGE batch, typically fresh out of VAE Decode.
- release -
003(default),002, or001. Ignored wheneveragateis connected. - agate (optional) - the loader's
agateoutput. Connect it and the release comes from the loaded checkpoint itself. - images (output) - the same batch, watermarked. Wire it into Save Image, Agate Save Image, or anything else downstream.
Connecting agate is the better habit, because it's the only way to name what the enum can't hold. A fine-tune isn't 001/002/003, and the loader resolves it to the unknown tag 000: payload AGATE000, provenance text degraded to "Agate-based model (LogoLabs Agate architecture)". Pick from the dropdown and you label a fine-tune as official release 003, which is worse than saying nothing.
Install
Manager → search Agate, or:
cd ComfyUI/custom_nodes
git clone https://github.com/logolabs/agate-comfyui
pip install -r agate-comfyui/requirements.txt
Restart ComfyUI. requirements.txt wants diffusers>=0.30 and transformers>=4.48 (the Ettin text encoder is a ModernBERT, and older ComfyUI installs ship an older transformers - the one version bump to watch), plus safetensors and huggingface_hub. Nothing in there touches torch. The checkpoint (agate-preview-003.safetensors, 529 MB) downloads into ComfyUI/models/agate/ on first sample; this node never loads weights itself.
Where it bites
Small images pass through unchanged. The embedder needs 256×256 pixels and returns anything smaller untouched - no error, no mark. Agate's native output is 256 or 512 px, so this normally isn't an issue, but crop a logo down to 200 px and you've silently stopped marking it. That also breaks release: auto in Agate Save Image later, since the mark it's supposed to read was never written.
Neither mark is tamper-proof, and the invisible one is fuzzier than people assume. Watermarks of this class survive light editing - colour tweaks, mild recompression - but crops, rotation, and heavy resampling move the pixels they live in, and the readback can drop under the 90% threshold. The pack's detector tries the image as-is (when the short side is ≥256) plus 256- and 512-px square resizes, keeping the best score, which buys robustness but not immunity. And roughly 1–2% of Agate's own fresh output won't read back at all - mostly flat logos on pure white. The metadata covers those; the pixels don't.
The text tags are a separate thing from the pixels. Run this into stock Save Image with metadata disabled - or launch ComfyUI with --disable-metadata - and the pixels are watermarked while the file carries no ai_generated entry. And unlike Agate Save Image, which writes those keys as plain text, stock Save Image JSON-encodes what it puts in extra_pnginfo, a different format from what a detector expecting the packages' exact values parses.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| images | IMAGE | — | |
| release | COMBO | 003 | The Agate release that made the images (ignored when agate is connected) |
| agateopt | AGATE_MODEL | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| images | IMAGE | — |