ComfyUI
Read this if you generate images in ComfyUI and want every saved image marked. You will install the
LaidlineSaveImagenode and use it in place of Save Image. Skip if you call diffusers from Python (Diffusers) or mark through an OpenAI-shaped server (OpenAI proxy). Time: 10 minutes. Needs: a working ComfyUI, Python 3.12 in its environment, and either a runninglaidline serve(remote) or alaidline.yaml(embedded).
In short
- One node,
LaidlineSaveImage, replaces Save Image; it writes the marked file to ComfyUI's output directory. - Each image gets a watermark (PixelSeal by default), Content Credentials and an evidence entry.
- Remote mode (
LAIDLINE_URL) keeps keys and models out of ComfyUI; embedded mode (LAIDLINE_CONFIG) holds them there. - There is no mark-a-tensor node: a manifest signs encoded bytes, so only the node that writes the file can work.
- A marking failure is raised on the node; no unmarked file is written.
laidline-comfyui adds one node, LaidlineSaveImage ("Save Image (Laidline: sign + watermark)", category
image/laidline). Use it in place of ComfyUI's Save Image: each image in the batch is
watermarked (PixelSeal by default), signed (C2PA), logged to the evidence chain and written to ComfyUI's output
directory. Each saved image is timestamped by the configured TSA (DigiCert by default; one HTTP round trip per image).
Proven against ComfyUI 0.37.0 on CPU: a real server ran LoadImage -> LaidlineSaveImage as PNG and as JPEG, and each
output was checked for the watermark (resolved in the evidence log), the Trusted C2PA manifest and the evidence entry
with route: comfyui:LaidlineSaveImage. The same workflow, run by hand against the v0.37.0 release in remote and
embedded mode, gave outputs that detect as marked_by_this_system.
Install
Install ComfyUI first, with its own instructions. Laidline keeps the torch that ComfyUI installed.
Install the pack into ComfyUI's Python environment and link its directory into custom_nodes/:
# with ComfyUI's Python; remote mode needs nothing more pip install laidline-comfyui # embedded mode also needs the image extra: pip install laidline-comfyui "laidline[image]" ln -s "$(python -c 'import laidline_comfyui, os; print(os.path.dirname(laidline_comfyui.__file__))')" \ ComfyUI/custom_nodes/laidline_comfyui
The pack uses relative imports only, so it loads under ComfyUI's custom-node loader (which imports it under a
path-derived module name without custom_nodes/ on sys.path). ComfyUI's startup log lists laidline_comfyui under
"Import times for custom nodes"; GET /object_info/LaidlineSaveImage returns the node's schema. If the link dangles,
ComfyUI starts without the node and logs nothing about it, so check both after the first start.
Remote or embedded
The node picks its marker from the environment of the ComfyUI process, once:
| Mode | Set | Where keys and models live |
|---|---|---|
| Remote (sidecar) | LAIDLINE_URL (and LAIDLINE_API_KEY if your gateway wants a bearer token; LAIDLINE_DETECT_TOKEN for a keyed sidecar's full detect tier, sent only to /detect as X-Laidline-Detect-Token) |
a laidline serve process; ComfyUI only sends pixels over HTTP |
| Embedded | LAIDLINE_CONFIG pointing at a laidline.yaml |
the ComfyUI process (signing key or KMS credentials, PixelSeal weights) |
LAIDLINE_URL wins when both are set. With neither, the node fails with
Laidline: set LAIDLINE_URL (sidecar) or LAIDLINE_CONFIG (embedded). Remote mode keeps keys out of a process that runs
arbitrary third-party custom nodes; prefer it. In remote mode the node sends X-Laidline-Route: comfyui:LaidlineSaveImage, so the sidecar's evidence entry names the node rather than http:/v1/mark/image.
To see which mode ran, look for loading PixelSeal in the ComfyUI log (embedded) or the route
comfyui:LaidlineSaveImage in the sidecar's evidence (remote).
Inputs and outputs
| Input | Type | Meaning |
|---|---|---|
images |
IMAGE |
The batch to save. Saved as RGB: alpha is dropped, one channel is expanded to grey. |
filename_prefix |
STRING, default laidline |
As in Save Image; may include a subfolder. |
format |
png, jpeg or webp |
Output format. |
request_id |
STRING, optional |
Recorded as request_id in the evidence entry. |
The node passes images through unchanged. Its ui output lists the saved files under images and, under
laidline, one {asset_id, manifest_id, warnings} per file (for example watermark_skipped:too_small below 128 px on
the short side). A marking failure is raised on the node as Laidline: <reason>: <detail>. The prompt and workflow
JSON are not embedded in the file (the core node writes them as PNG text chunks): only the manifest and the invisible
watermark are.
Why there is no LaidlineMarkImage
A node that marks a tensor and passes it on cannot work: a C2PA manifest signs encoded bytes, and a tensor carries no bytes and no manifest. Any downstream node (Save Image, a preview, an upscaler) re-encodes the pixels and the signature is gone; only the watermark would survive, without the manifest it points to. So the only node is the one that writes the file.
What was proven
On 19 Oct 2026 the proof run used a real ComfyUI 0.37.0 on CPU with the sidecar in remote mode: a 512x384 PNG uploaded,
the LoadImage -> LaidlineSaveImage workflow queued as PNG and as JPEG, and each output fetched through ComfyUI's
/view and checked. Starting the sidecar took about 20 s on a cold model cache (most of it the 1.24 GB PixelSeal
download) and 3 s warm; each workflow then ran in a second or two. Embedded mode was run by hand against the
same release, not in that automated run.
Reference
- Integrations: what each integration was proven against, and how.
- Install: extras and the model downloads.
- Serve: the sidecar that remote mode talks to.
- Evidence: the entry the node writes, including
routeandrequest_id. - Config: the
laidline.yamlthat embedded mode reads.