sign-ai-media
v0.1.2
Published
Sign media with C2PA metadata that declares it as AI-generated.
Maintainers
Readme
sign-ai-media is a small open-source Node.js package for adding signed C2PA manifests to generated images and videos. It writes a standards-based AI source signal, lets you control the generator identity shown in the manifest, and exposes both a CLI and a TypeScript API.
It is designed for model hosts, generation apps, internal media pipelines, dataset tooling, and any workflow that wants to label generated media without pretending to be another provider.
What It Writes
The default manifest includes:
- A
c2pa.actions.v2assertion withaction: "c2pa.created". - A configurable
softwareAgentobject withnameand optionalversion. - The IPTC digital source type
trainedAlgorithmicMedia. - A
stds.schema-org.CreativeWorkassertion for optional generator, model, producer, prompt, and custom metadata. - A C2PA signature. You can provide your own certificate/private key, or use the bundled development signer for lower-friction local use.
The default AI source marker is:
http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMediaInstallation
npm install sign-ai-mediaYou can also run the CLI without installing it first:
npx sign-ai-media input.png output.png --software-agent "my-generator"This package uses @contentauth/c2pa-node, which ships native bindings. Prebuilt binaries are available for common macOS, Linux, and Windows platforms. Other platforms may need a local Rust toolchain.
CLI Usage
npx sign-ai-media input.png output.png \
--software-agent "acme-image-model" \
--version "1.0.0" \
--generator "Acme Image API" \
--model "acme-diffusion-v1" \
--producer "Acme Labs"For production provenance, pass your own signing credentials:
npx sign-ai-media input.png output.png \
--software-agent "acme-image-model" \
--certificate ./certs/signing-cert.pem \
--private-key ./certs/signing-key.pem \
--algorithm es256 \
--tsa-url "https://timestamp.example.com"Viewing Metadata
To inspect AI/C2PA metadata in a signed file, use --view with the input file:
npx sign-ai-media --view output.pngThe viewer does not modify the file. It reads the active C2PA manifest, extracts the AI-generation fields written by this package, and prints a readable summary:
AI media metadata: output.png
Status: C2PA manifest found
Title: output.png
Format: image/png
Claim generator: acme-image-service/1.0.0
Generator: Acme Image API
Model: acme-diffusion-v1
Producer: Acme Labs
Software agent: {"name":"acme-image-model","version":"1.0.0"}
Action: c2pa.created
Created at: 2026-05-02T09:00:00.000Z
Digital source type: http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia
Validation: No validation issues reported.
Assertions:
- c2pa.actions.v2
- stds.schema-org.CreativeWorkIf no C2PA manifest is present, the viewer prints:
AI media metadata: input.png
Status: No C2PA manifest found.Useful metadata options:
--source-type ai-generated
--claim-generator "acme-image-service/1.0.0"
--prompt "A red fox in a snowy forest"
--prompt-file ./prompt.txt
--negative-prompt "low quality, blurry"
--seed 12345
--scheduler "euler-a"
--cfg-scale 7.5
--steps 30
--created-at "2026-05-02T09:00:00Z"
--digital-source-type "http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia"
--creative-work-json '{"usageInfo":"internal","copyrightNotice":"Acme Labs"}'
--mime-type image/png
--remote-manifest-url "https://cdn.example.com/manifests/image.c2pa"
--no-embedAction/provenance options:
--action c2pa.created
--action-description "Generated from a text prompt"
--action-parameters-json '{"pipeline":"txt2img"}'
--ingredient ./source-image.png
--ingredient ./mask.png
--parent ./original.png
--model-uri "https://example.com/models/acme-diffusion-v1"
--model-hash "sha256:..."
--input-uri "https://example.com/inputs/reference.png"
--input-hash "sha256:..."Training and data-mining policy options:
--ai-training-use notAllowed
--ai-generative-training-use notAllowed
--data-mining-use constrained
--ai-inference-use allowed
--training-constraint-info "See https://example.com/ai-use-policy"Viewer options:
npx sign-ai-media --view output.png --json
npx sign-ai-media --view output.png --verify-trust --trust-anchors ./anchors.pem
npx sign-ai-media --view output.png --fetch-remote-manifest
npx sign-ai-media --view output.png --no-fetch-remote-manifestWhy not c2patool?
c2patool is the official general-purpose command line tool for C2PA. It can read C2PA manifest reports, inspect low-level manifest data, and add manifests to supported media files.
Use c2patool when you want a broad, standards-level C2PA utility.
Use sign-ai-media when you want a smaller tool focused on AI-generated media workflows:
- It has simple flags for common AI metadata such as
--software-agent,--model,--prompt,--source-type,--seed, and training/data-mining policy. - It writes an AI-oriented manifest shape by default, including
c2pa.actions.v2,trainedAlgorithmicMedia, CreativeWork metadata, and optional CAWG training/data-mining assertions. - It includes a TypeScript API for Node.js apps, queues, media pipelines, and CMS integrations.
- It includes a focused
--viewmode that extracts the AI/C2PA fields this package writes, instead of only showing a full generic manifest report.
In short: c2patool is the toolbox. sign-ai-media is the shortcut for signing and reading AI media provenance in apps and automation.
TypeScript API
import { signAiGeneratedMedia, viewAiGeneratedMedia } from "sign-ai-media";
await signAiGeneratedMedia({
input: "input.png",
output: "output.png",
metadata: {
softwareAgent: "acme-image-model",
version: "1.0.0",
claimGenerator: "acme-image-service/1.0.0",
generator: "Acme Image API",
model: "acme-diffusion-v1",
modelVersion: "2026-05-02",
producer: "Acme Labs",
prompt: "A red fox in a snowy forest",
negativePrompt: "low quality, blurry",
seed: 12345,
scheduler: "euler-a",
cfgScale: 7.5,
steps: 30,
actionDescription: "Generated from a text prompt",
trainingMining: {
"cawg.ai_training": { use: "notAllowed" },
"cawg.ai_generative_training": { use: "notAllowed" },
},
},
});
const metadata = await viewAiGeneratedMedia({
input: "output.png",
});viewAiGeneratedMedia() returns a JSON-serializable object, so it can be logged, sent from an API route, or stored directly:
console.log(JSON.stringify(metadata, null, 2));Example result:
{
"input": "output.png",
"hasManifest": true,
"metadata": {
"title": "output.png",
"format": "image/png",
"claimGenerator": "acme-image-service/1.0.0",
"generator": "Acme Image API",
"model": "acme-diffusion-v1",
"producer": "Acme Labs",
"prompt": "A red fox in a snowy forest",
"softwareAgent": {
"name": "acme-image-model",
"version": "1.0.0"
},
"digitalSourceType": "http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia",
"createdAt": "2026-05-02T09:00:00.000Z",
"action": "c2pa.created",
"signatureIssuer": "Example Signing Cert",
"signatureTime": "2026-05-02T09:00:00+00:00"
},
"validationStatus": [],
"assertionLabels": ["c2pa.actions.v2", "stds.schema-org.CreativeWork"]
}When no manifest is found, the result is:
{
"input": "input.png",
"hasManifest": false,
"metadata": null,
"validationStatus": [],
"assertionLabels": []
}Pass a signer when you want the manifest signed with your own production identity:
import {
SigningAlgorithm,
createLocalSigner,
signAiGeneratedMedia,
} from "sign-ai-media";
const signer = await createLocalSigner({
certificatePath: "./certs/signing-cert.pem",
privateKeyPath: "./certs/signing-key.pem",
algorithm: SigningAlgorithm.ES256,
tsaUrl: "https://timestamp.example.com",
});
await signAiGeneratedMedia({
input: "input.png",
output: "output.png",
signer,
metadata: {
softwareAgent: "acme-image-model",
version: "1.0.0",
claimGenerator: "acme-image-service/1.0.0",
generator: "Acme Image API",
model: "acme-diffusion-v1",
producer: "Acme Labs",
prompt: "A red fox in a snowy forest",
},
});Metadata Fields
metadata.softwareAgent is required. It should name the model, service, app, or pipeline that created the media.
Optional metadata:
version: version of the software agent or model.claimGenerator: C2PA user-agent style claim generator. Defaults tosoftwareAgent/version.generator: friendly generator name for CreativeWork metadata.model: model name for consumers that display richer generator details.modelVersion: model revision or version.modelUriandmodelHash: model or model-card reference.inputUriandinputHash: remote input reference.producer: organization, service, or creator responsible for the output.prompt: prompt text to embed. Treat this as public metadata.negativePrompt: negative prompt text to embed. Treat this as public metadata.seed,scheduler,cfgScale, andsteps: generation parameters.createdAt: ISO timestamp. Defaults to the current time.digitalSourceType: override the IPTC source type URL.action: C2PA action name. Defaults toc2pa.created.actionDescription: free-text action description.actionParameters: extra action parameters for advanced C2PA workflows.trainingMining: CAWG Training and Data Mining assertion entries.creativeWork: extra properties merged into the schema.org CreativeWork assertion.
Digital Source Presets
Use --source-type for common source types without remembering the full vocabulary URL:
ai-generated: created by a trained generative AI model.ai-edited: human or tool edits using generative AI, such as inpainting or outpainting.algorithmic: algorithmic media not based on sampled training data.algorithmically-enhanced: algorithmic enhancement such as denoise or sharpen.composite-ai: composite media with at least one generative AI element.composite: composite of several elements.composite-capture: composite where all elements are captures of real life.capture: digital camera or sensor capture.screen-capture: screen capture.human-edited: non-generative human edits.digital-art,digital-creation,software-image, anddata-driven: other common IPTC source categories.empty: C2PA empty asset source type.ai-data: C2PA trained algorithmic data source type for non-media data.
You can still pass a full URL with --digital-source-type.
Ingredients and Parents
Use --ingredient to attach source media that contributed to the signed output. Repeating the flag adds multiple componentOf ingredients:
npx sign-ai-media output.png signed.png \
--software-agent "acme-image-model" \
--ingredient ./reference.png \
--ingredient ./mask.pngUse --parent when the output is derived from an original asset:
npx sign-ai-media edited.png signed.png \
--software-agent "acme-editor" \
--parent ./original.png \
--action c2pa.edited \
--action-description "Inpainted background"Training and Data Mining
The --ai-training-use, --ai-generative-training-use, --data-mining-use, and --ai-inference-use flags write a cawg.training-mining assertion using the CAWG Training and Data Mining Assertion.
Each flag accepts allowed, notAllowed, or constrained. When using constrained, add --training-constraint-info to explain the policy or link to a license.
Supported Media
MIME type is inferred from common media extensions:
.png.jpgand.jpeg.webp.avif.tifand.tiff.mp4.mov.avi
Pass --mime-type or mimeType when the extension is missing or unusual.
Signing Credentials
C2PA manifests need a signer, but this package does not require users to bring a certificate/private-key pair just to get started. If no signer is provided, signAiGeneratedMedia() and the CLI use bundled test credentials.
For production provenance, pass a local certificate/private-key pair through createLocalSigner() or another compatible @contentauth/c2pa-node signer object. Do not use another company's name, certificate, or identity fields.
Where to get production certificates
Production C2PA signing credentials should come from a certificate authority on the C2PA trust list. The official Content Authenticity Initiative docs explain that conforming generator products must use a certificate that chains back to a trusted C2PA certificate authority, and that C2PA maintains separate trust lists for claim-signing certificates and timestamp authorities.
Start here:
- Getting a signing certificate from the CAI open-source docs.
- C2PA trust lists, including the C2PA trust list and C2PA TSA trust list.
- C2PA Conformance Explorer for the current readable trust-list view.
- C2PA public trust-list repository for the PEM trust-list files.
Certificate authorities listed by the CAI docs include:
- DigiCert C2PA Media Trust
- SSL.com C2PA Enterprise Content Authenticity Solutions
- Tauth Labs
- Trufo Trust Certificate Authority
Provider availability, onboarding requirements, and assurance levels can change, so use the official C2PA trust-list sources above as the source of truth before issuing production credentials.
Development certificates
The bundled signer is only for local development and demos. It can prove that the package writes and signs a manifest, but it does not give your output a production identity that verifiers should trust.
Use development/test credentials for:
- Local CLI experiments.
- Integration tests.
- Demo assets where trust is not implied.
Use production credentials for:
- Public releases.
- User-facing generated media.
- Workflows where platforms or verifiers should recognize your organization as the signer.
Verifying Output
After signing, inspect the output with a C2PA-compatible verifier such as the Content Authenticity Initiative Verify tooling or another C2PA reader. For PNG files, the embedded manifest appears in a caBX chunk and should include c2pa.actions.v2 plus the trainedAlgorithmicMedia source type.
Development
npm install
npm run build
npm run typecheck
node dist/cli.js --helpThe package is intentionally small. Most of the heavy lifting is delegated to @contentauth/c2pa-node; this project focuses on shaping a clear AI-generation manifest and giving applications a stable CLI/API around it.
License
GPL-3.0-or-later
