ComfyUI

Read this if you generate images in ComfyUI and want every saved image marked. You will install the LaidlineSaveImage node 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 running laidline serve (remote) or a laidline.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 route and request_id.
  • Config: the laidline.yaml that embedded mode reads.