Integrations

Add Laidline where media is made. Four ways in, one evidence log.

Pick the one that fits your stack. Every way writes the same watermark and chained entry, and a manifest on media files.

Works with

  • Lambda
  • Cloud Run
  • S3
  • R2
  • GCS
  • vLLM
  • ComfyUI
  • Diffusers
  • Pipecat
  • LiveKit
  1. Library Call a Marker in your own process
  2. Storage hook Marks objects as they land in the bucket
  3. Sidecar proxy In front of an OpenAI-compatible API
  4. Plugins One package per framework
  5. One evidence log Manifest, watermark and chained entry Whichever way in.

Your first marked file.

Four commands on any machine with Python 3.12 or newer, then a free registration with a work email.

  1. Install. Core plus image marking; audio, video and text are extras.

    pip install "laidline[image]"
  2. Write a config. A trial issuer number, payload key and development certificate.

    laidline init --system-name my-model --org "Yourcompany Inc"
  3. Mark a file. Watermarked, signed, timestamped and logged.

    laidline mark in.png
  4. Check it. Grade A here; elsewhere a shared mark, grade D.

    laidline detect in.marked.png
  5. Register, free. A permanent issuer number; elsewhere, marks read as yours.

    laidline register --email you@yourcompany.com --display-name "Yourcompany Inc" --accept-terms

No GPU? Install CPU PyTorch first and the environment is over 4 GB smaller. How, in the install guide.

Quickstart: the same steps with their output, 15 minutes Every extra and the GPU builds Or wire it into your stack

Mode 1

Library

  1. Inputpipe(...).images[0]A PIL image, bytes or a path
  2. Laidlinemarker.mark_image(image, fmt="png", ctx=…)Watermark · re-encode · sign · append an entry
    • WatermarkPixelSeal, invisible; AudioSeal for audio, VideoSeal for video
    • SignC2PA manifest over the final bytes, with your certificate
    • KeyYour payload key and issuer number, a trial number until you register
    • LogA hash-chained evidence entry, timestamped
  3. ResultYou get the marked file back, plus three IDsin.marked.png from the CLI, marked.data in code
    File ID asset_id
    ast_01M5…SRZ2
    The ID you look the file up by
    Signed record manifest_id
    urn:c2pa:9125…b8a6
    The C2PA record inside the file
    Log entry entry_hash
    7810…33fd
    This file's line in your evidence log
    Warnings warnings
    []
    Empty: nothing was skipped

    Real values from a run on 21 Oct 2026, shortened.

    DetectAnyone can check the file laterlaidline detect in.marked.png, on the marking host
    Verdict
    Marked by this system
    This install's own certificate or log knows it
    Grade
    A
    The best of A to D: a trusted signature with a timestamp

    Before you register, the watermark tells other machines only that the file is marked, not that it is yours: a shared mark, grade D.

Three steps in your own code. Images and audio mark on the CPU; video takes the async path.

  1. 1Load a marker once per process. It reads laidline.yaml at start, not per request.
  2. 2Mark before you encode or upload. mark_image watermarks, encodes, signs and logs the image.
  3. 3Return the file and its ID. marked.b64() is the signed file; keep marked.asset_id to find it later.

Methods: mark_image · mark_audio · mark_video · audio_stream · detect

from laidline import Marker, MarkContext

# 1 Load once per process
marker = Marker.from_config("laidline.yaml")

def handler(job):
    ctx = MarkContext(request_id=job["id"])
    prompt = job["input"]["prompt"]
    image = pipe(prompt).images[0]
    # 2 Mark before you encode or upload
    marked = marker.mark_image(
        image, fmt="png", ctx=ctx
    )
    # 3 Return the file and its ID
    return {
        "image_b64": marked.b64(),
        "asset_id": marked.asset_id,
    }

Mode 2

Storage hook

  1. Inputs3://bucket/generated/…/out.pngAn ObjectCreated event on the queue
  2. Laidlinelaidline ingestHEAD · GET · mark · PUT back in place
    • WatermarkPixelSeal; AudioSeal and VideoSeal once mime_allowlist names WAV or MP4
    • SignC2PA manifest signed with your KMS key; an upstream manifest is kept as an ingredient
    • KeyYour payload key and issuer number
    • LogAn entry with route ingest:s3_sqs and the upstream from the key prefix
  3. ResultThe marked file replaces the original, with its IDsSame key and content type, or under a prefix you name
    File ID laidline-asset-id
    Object metadata: the ID you look the file up by
    Signed record laidline-manifest-id
    Object metadata: the C2PA record inside the file
    Log line outcome=marked
    The worker's line for this object
    Its own write
    Skipped, already marked
    Writing back fires a new event; nothing is marked twice
    DetectAnyone can check the object laterlaidline detect on the object, on the marking host
    Verdict
    Marked by this system
    This install's own certificate or log knows it

    Only PNG, JPEG and WebP are marked by default: a WAV or MP4 is acknowledged as “Skipped, not on the allowlist” until you add it.

Outputs land in S3, R2 or GCS; a worker marks each one as it arrives. Idempotent.

  1. 1Watch the bucket's queue. ingest names the queue, the folders to watch and the types to mark.
  2. 2Sign with your KMS key. signing names the key that signs each file's C2PA record.
  3. 3Run the worker. laidline ingest marks each new object, writes it back in place and logs it.

Runs on: S3 + SQS · R2 + Queues · GCS + Pub/Sub · container, Lambda or Cloud Run

# laidline.yaml
# 1 Watch the bucket's queue
ingest:
  source: s3_sqs
  queue_url:
    https://sqs.eu-central-1.amazonaws.com/…/outputs
  prefixes: ["generated/"]
  writeback: in_place
  mime_allowlist: [image/png, image/jpeg,
    image/webp, audio/wav, video/mp4]
# 2 Sign with your KMS key
signing:
  backend: aws-kms
  aws:
    key_id: arn:aws:kms:eu-central-1:…:key/…
    region: eu-central-1

# 3 Run the worker
$ laidline ingest

Mode 3

Sidecar proxy

  1. InputPOST /v1/images/generationsAny OpenAI-compatible client, pointed at :8080
  2. Laidlinelaidline serveForwards upstream, marks the images in the answer
    • WatermarkPixelSeal for images, AudioSeal for /v1/audio/speech
    • SignC2PA manifest; an input image's manifest becomes an ingredient
    • KeyYour payload key and issuer number
    • LogAn entry with route proxy:/v1/images/generations, request id and tenant
  3. ResultThe client gets its usual response, images markedSame shape as the upstream's answer, plus the IDs
    Image data[].b64_json
    The marked bytes, or a URL under /v1/assets/
    IDs data[].laidline
    Each image's file ID, signed record and log entry
    Headers x-laidline-asset-id · x-laidline-entry-hash
    The same IDs, comma-joined when there are several
    DetectAnyone can check the file laterPOST /detect with the file, on the marking host
    Verdict
    Marked by this system
    This install's own certificate or log knows it

    Chat and completions pass through; their text mark comes from laidline-vllm in the upstream.

In front of any OpenAI-compatible or JSON API. Images, speech and chat come back marked.

  1. 1Name the API it fronts. upstream is where requests go on; assets keeps the files it serves by URL.
  2. 2Map another schema, if yours differs. An adapter rule says where the images sit in the answer.
  3. 3Start it and point clients at it. laidline serve listens on :8080 and marks each answer.

Routes: /v1/images/* · /v1/audio/speech · /v1/chat/completions · any other through adapter.rules

# laidline.yaml
# 1 The API it fronts
upstream:
  base_url: http://vllm:8000
  auth: env
  api_key_env: UPSTREAM_KEY
assets: { path: ./assets }
# 2 Another schema (optional)
adapter:
  rules:
    - name: vendor
      path: /v1/generate
      method: POST
      jsonpath: $.images[*].b64
      kind: b64

# 3 Start it; clients talk to :8080
$ laidline serve

Mode 4

Plugins

  1. InputLaidlineSaveImage · attach(pipe) · tts_node · vllm serveThe step the framework already saves or samples at
  2. LaidlineMarker, or a sidecar over LAIDLINE_URLIn-process keys, or none on the framework host
    • WatermarkPixelSeal for images, AudioSeal for TTS frames, SynthID for text in the sampling loop
    • SignC2PA manifest on image and audio files
    • KeyYour payload key and issuer number; key_seed for text
    • LogAn entry per output: comfyui:LaidlineSaveImage, pipecat:tts, livekit:tts_node
  3. ResultEach framework hands back its usual output, markedIn the place the framework already puts it
    ComfyUI
    The saved PNG or JPEG
    Diffusers out.assets[i]
    One marked file per image the pipeline returns
    Pipecat, LiveKit
    The TTS audio frames, marked
    vLLM x-laidline-watermarked: 1
    A response header; the mark is in the text itself
    DetectAnyone can check the output laterlaidline detect, or detect --text for text, on the marking host
    Verdict
    Marked by this system
    This install's own certificate or log knows it

    Known gap: text through POST /detect still reads “Signals present, unverified” where the CLI says “Marked by this system”.

One package per framework, marking where the framework already saves or samples.

  1. 1Install the package for your framework. Each card opens its install line.
  2. 2Add it at the save or sampling step. A node, attach(pipe, marker), a processor or a wrapper.
  3. 3Use the output as before. It comes back marked, with an entry in your log.

Needs: LiveKit needs laidline[audio] beside it · vLLM needs a nightly and VLLM_USE_FLASHINFER_SAMPLER=0

Where it fits. Across the stack you run.

Each card opens its guide: the setup, and in full detail what it was tested against.

  1. Generate with

    Model frameworks and APIs

  2. Speak through

    Voice agents and calls

  3. Store in

    Buckets, marked as objects land

  4. Run on

    Where the sidecar or worker runs

  5. Also designed for

    The same mechanisms, past the names above

Every guide, with what was tested

Mark your first output today.