npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

c2pa-rs-javascript-library

v0.2.4

Published

TypeScript bindings for C2PA signing and verification powered by Rust and WebAssembly

Readme

c2pa-rs-javascript-library

TypeScript/JavaScript bindings for C2PA (Coalition for Content Provenance and Authenticity) signing and verification, powered by Rust compiled to WebAssembly. Built on c2pa-rs with extended support for text-based formats.

What It Does

  • Sign images, PDFs, SVGs, audio files (MP3, WAV, FLAC), and text formats (JSONC, XML, Markdown) with C2PA manifests
  • Verify C2PA manifests and extract provenance data
  • Sidecar manifests — produce a separate .c2pa file for assets that cannot be modified (AI/ML datasets)
  • CAWG identity assertions — prepare, sign, and verify named-actor identity credentials (X.509 and ICA/W3C VC)
  • Structured text — first-class support for source code and document formats

Works in any bundler that supports WASM (Vite, webpack 5, Rollup, esbuild).

Supported Formats

| MIME type / format string | Format | |---|---| | image/jpeg | JPEG | | image/png | PNG | | image/svg+xml | SVG | | image/x-adobe-dng | DNG | | application/pdf | PDF | | audio/mpeg | MP3 | | audio/wav | WAV | | audio/flac | FLAC | | jsonc | JSONC / JSON with comments | | xml | XML | | md | Markdown |

Installation

npm install c2pa-rs-javascript-library

Quick Start

Verify an asset

import { verifyAsset } from 'c2pa-rs-javascript-library';

const bytes = new Uint8Array(await file.arrayBuffer());

const result = await verifyAsset('image/jpeg', bytes, []);
console.log(result.state);       // true if trusted
console.log(result.manifests);   // array of recognized manifests

Sign an asset

signAsset accepts an options object. The required fields are format, asset, manifestDefinition, signcert, pkey, and alg. Everything else is optional.

import { signAsset } from 'c2pa-rs-javascript-library';

const signcert = new Uint8Array(/* PEM bytes */);
const pkey     = new Uint8Array(/* private key bytes */);

const result = await signAsset({
  format: 'image/jpeg',
  asset: assetBytes,           // Uint8Array or string (string accepted for text formats)
  manifestDefinition: {
    claim_generator_info: [{ name: 'my-app' }],
    title: 'photo.jpg',
    assertions: [
      { label: 'c2pa.actions', data: { actions: [{ action: 'c2pa.created', digitalSourceType: 'http://cv.iptc.org/newscodes/digitalsourcetype/digitalCapture' }] } },
    ],
  },
  signcert,
  pkey,
  alg: 'es256',
  tsaUrl: 'http://timestamp.digicert.com', // optional
});

// result.signedAsset — Uint8Array of the signed file
// result.manifest   — Uint8Array of the raw JUMBF manifest

Sign an audio asset

Audio formats work the same way — just pass the appropriate MIME type:

const result = await signAsset({
  format: 'audio/mpeg',  // or 'audio/wav' or 'audio/flac'
  asset: audioBytes,
  manifestDefinition: {
    claim_generator_info: [{ name: 'my-app' }],
    title: 'recording.mp3',
    assertions: [
      { label: 'c2pa.actions', data: { actions: [{ action: 'c2pa.created', digitalSourceType: 'http://cv.iptc.org/newscodes/digitalsourcetype/digitalCapture' }] } },
    ],
  },
  signcert,
  pkey,
  alg: 'es256',
});

const outcome = await verifyAsset('audio/mpeg', result.signedAsset, [certPem]);

Signing with a thumbnail

Pass thumbnailFormat and thumbnailData together:

const result = await signAsset({
  format: 'image/jpeg',
  asset: assetBytes,
  manifestDefinition: manifest,
  signcert, pkey, alg: 'es256',
  thumbnailFormat: 'image/jpeg',
  thumbnailData: thumbnailBytes,
});

Signing with ingredients

Use signAssetWithIngredients to attach one or more ingredients. Each ingredient can optionally carry a sidecar for assets whose manifest lives in a separate .c2pa file.

import { signAssetWithIngredients } from 'c2pa-rs-javascript-library';

// Single parent (embedded manifest)
const result = await signAssetWithIngredients(
  'image/jpeg',
  derivedBytes,
  manifest,
  signcert, pkey, 'es256',
  [{ format: 'image/jpeg', asset: sourceBytes, title: 'source.jpg', relationship: 'parentOf' }]
);

// Multiple ingredients, mixed types
const result = await signAssetWithIngredients(
  'md',
  bodyBytes,
  manifest,
  signcert, pkey, 'es256',
  [
    { format: 'md', asset: docA.signedAsset, title: 'doc-a.md', relationship: 'parentOf' },
    { format: 'md', asset: docB.signedAsset, title: 'doc-b.md', relationship: 'componentOf' },
  ]
);

// Sidecar-backed ingredient
const result = await signAssetWithIngredients(
  'image/png',
  derivedBytes,
  manifest,
  signcert, pkey, 'es256',
  [{
    format: 'image/jpeg',
    asset: sourceBytes,
    title: 'source.jpg',
    relationship: 'parentOf',
    sidecar: sidecarManifestBytes,  // .c2pa bytes from signAssetSidecar
  }]
);

Use signAssetSidecarWithIngredients when the output asset should also be sidecar-signed:

import { signAssetSidecarWithIngredients } from 'c2pa-rs-javascript-library';

const result = await signAssetSidecarWithIngredients(
  'image/png',
  assetBytes,
  manifest,
  signcert, pkey, 'es256',
  [{ format: 'image/jpeg', asset: sourceBytes, title: 'source.jpg', relationship: 'parentOf' }]
);
// result.signedAsset — original bytes unchanged
// result.manifest   — sidecar JUMBF bytes

Structured text (JSONC / XML / Markdown)

Pass the format string and the asset as a plain string (or Uint8Array). The placeholder comment required by C2PA is injected automatically:

import { signAsset, verifyMarkdownAsset } from 'c2pa-rs-javascript-library';

const result = await signAsset({
  format: 'md',
  asset: '# My document\n\nSome content.',  // string accepted directly
  manifestDefinition: manifest,
  signcert, pkey, alg: 'es256',
});

const outcome = await verifyMarkdownAsset(result.signedAsset, [certPem]);

Equivalent verify and clean helpers exist for each text format:

| Format | Verify | Clean | |---|---|---| | JSONC | verifyJsoncAsset(asset, certs) | cleanJsoncAsset(asset) | | XML | verifyXmlAsset(asset, certs) | cleanXmlAsset(asset) | | Markdown | verifyMarkdownAsset(asset, certs) | cleanMarkdownAsset(asset) |

Sidecar manifests (AI/ML datasets)

A sidecar produces a separate .c2pa manifest file — the original asset is never modified. This follows the C2PA AI/ML specification.

import { signAssetSidecar, verifyAssetFromSidecar } from 'c2pa-rs-javascript-library';

// Sign — returns the original (unmodified) asset and a sidecar manifest
const result = await signAssetSidecar({
  format: 'image/jpeg',
  asset: datasetBytes,
  manifestDefinition: manifest,
  signcert, pkey, alg: 'es256',
});

// result.signedAsset — original bytes, unchanged
// result.manifest   — JUMBF sidecar bytes (.c2pa file)

// Verify — pass both the asset and its sidecar
const outcome = await verifyAssetFromSidecar({
  format: 'image/jpeg',
  asset: datasetBytes,
  sidecar: result.manifest,
  trustedCertificates: [certPem],
});

signAssetSidecar accepts the same optional fields as signAsset (thumbnailFormat/thumbnailData and all identity fields). For sidecar output with ingredients, use signAssetSidecarWithIngredients.

CAWG X.509 identity assertions

Single-pass signing embeds an identity assertion directly:

import { signAsset } from 'c2pa-rs-javascript-library';

const result = await signAsset({
  format: 'image/png',
  asset: assetBytes,
  manifestDefinition: manifest,
  signcert, pkey, alg: 'es256',
  // identity fields
  identitySigncert: idSigncert,
  identityPkey: idPkey,
  identityAlg: 'es256',
  identityOptions: {
    sigType: 'cawg.x509.cose',
    reserveSize: 4096,
    referencedAssertions: ['c2pa.actions'],
    roles: ['cawg.creator'],
  },
});

For an external-signer / HSM flow use the two-step API:

import {
  prepareIdentityAssertion,
  signIdentityAssertionPayloadX509,
  finalizeIdentityAssertion,
} from 'c2pa-rs-javascript-library';

// Step 1 — capture the signer payload
const prepared = await prepareIdentityAssertion(
  'image/png', assetBytes, manifest, signcert, pkey, 'es256',
  { sigType: 'cawg.x509.cose', reserveSize: 4096, roles: ['cawg.creator'] }
);

// Step 2 — sign with an external key / HSM
const signature = signIdentityAssertionPayloadX509(
  prepared.signerPayloadCbor, identitySigncert, identityPkey, 'es256'
);

// Step 3 — embed the real signature
const result = await finalizeIdentityAssertion(prepared, signature);

ICA (Identity Claims Aggregation) signing

import { computeIcaIssuerDid, signAsset } from 'c2pa-rs-javascript-library';

// Derive the did:jwk DID for the issuer's Ed25519 key (32 raw bytes).
const issuerDid = computeIcaIssuerDid(issuerPrivateKeyBytes);

const result = await signAsset({
  format: 'image/png',
  asset: assetBytes,
  manifestDefinition: manifest,
  signcert, pkey, alg: 'es256',
  // ICA identity fields
  issuerDid,
  issuerPrivateKey: issuerPrivateKeyBytes,  // 32-byte Ed25519 seed
  verifiedIdentities: [
    {
      type: 'cawg.social_media',
      username: 'myhandle',
      uri: 'https://social.example.com/myhandle',
      verifiedAt: '2024-01-01T00:00:00Z',
      provider: { id: 'https://social.example.com', name: 'Example Social' },
    },
  ],
  icaOptions: {
    sigType: 'cawg.identity_claims_aggregation',
    reserveSize: 8192,
    roles: ['cawg.creator'],
  },
});

API Reference

See src/index.ts for full TypeScript signatures.

Core

| Function | Description | |---|---| | signAsset(options) | Sign any supported format (see SignAssetOptions) | | verifyAsset(format, asset, trustedCerts) | Verify and parse manifests | | cleanAsset(format, asset) | Remove any embedded C2PA manifest | | getResource(format, asset, uri) | Retrieve a named resource from a signed asset | | signAssetWithIngredients(format, asset, manifest, cert, key, alg, ingredients, tsaUrl?) | Sign with one or more ingredients (supports sidecar via ingredient.sidecar) | | signAssetSidecarWithIngredients(format, asset, manifest, cert, key, alg, ingredients, tsaUrl?) | Sidecar output with ingredients |

Sidecar manifests

| Function | Description | |---|---| | signAssetSidecar(options) | Sign without modifying the asset; returns sidecar bytes (see SignAssetSidecarOptions) | | verifyAssetFromSidecar(options) | Verify an asset using a separate sidecar manifest |

Identity assertions (CAWG)

| Function | Description | |---|---| | prepareIdentityAssertion(...) | Capture signer payload for external signing | | finalizeIdentityAssertion(prepared, signature) | Embed externally produced signature | | signIdentityAssertionPayloadX509(cbor, cert, key, alg) | Sign a CBOR payload with X.509 | | verifyIdentityAssertions(format, asset, trustedCerts) | Verify CAWG identity assertions | | computeIcaIssuerDid(privateKey) | Derive did:jwk from a 32-byte Ed25519 seed |

CAWG metadata

| Function | Description | |---|---| | addCawgMetadataAssertion(manifest, metadata) | Add a cawg.metadata assertion to a manifest definition | | signAssetWithCawgMetadata(...) | Sign and attach CAWG metadata in one step |

Structured text helpers

| Format | Verify | Clean | Parse | |---|---|---|---| | JSONC | verifyJsoncAsset | cleanJsoncAsset | parseJsonc | | XML | verifyXmlAsset | cleanXmlAsset | — | | Markdown | verifyMarkdownAsset | cleanMarkdownAsset | — |

Signing text formats is done via signAsset with format: 'jsonc' | 'xml' | 'md' and asset: string | Uint8Array.

SignAssetOptions

type SignAssetOptions = {
  // Required
  format: SupportedFormat;
  asset: Uint8Array | string;   // string accepted for jsonc, xml, md
  manifestDefinition: object;
  signcert: Uint8Array;
  pkey: Uint8Array;
  alg: SigningAlg;

  // Optional
  tsaUrl?: string;

  // Thumbnail
  thumbnailFormat?: string;
  thumbnailData?: Uint8Array;

  // X.509 identity assertion
  identitySigncert?: Uint8Array;
  identityPkey?: Uint8Array;
  identityAlg?: SigningAlg;
  identityOptions?: IdentityAssertionOptions;
  identityTsaUrl?: string;

  // ICA identity assertion
  issuerDid?: string;
  issuerPrivateKey?: Uint8Array;
  verifiedIdentities?: IcaVerifiedIdentity[];
  icaOptions?: IdentityAssertionOptions;
};

SignAssetSidecarOptions has the same shape (without the ingredient fields; use signAssetSidecarWithIngredients for that).

Manifest definition

{
  claim_generator_info: [{ name: string; version?: string }];
  title?: string;
  assertions?: { label: string; data: unknown }[];
  instance_id?: string;      // auto-generated if omitted
  label?: string;            // auto-generated if omitted
  assertion_salt?: number[]; // optional fixed salt for deterministic assertion hashes
}

Actions and digitalSourceType

C2PA requires that every c2pa.created action includes a digitalSourceType URI from the IPTC digital source type vocabulary. Common values:

| Value | Meaning | |---|---| | http://cv.iptc.org/newscodes/digitalsourcetype/digitalCapture | Photo/scan taken by a camera | | http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia | AI-generated content | | http://cv.iptc.org/newscodes/digitalsourcetype/compositeWithTrainedAlgorithmicMedia | Human + AI composite | | http://cv.iptc.org/newscodes/digitalsourcetype/algorithmicMedia | Purely algorithmic (non-AI) |

Signing algorithms

SigningAlg: 'es256' | 'es384' | 'es512' | 'ps256' | 'ps384' | 'ps512' | 'ed25519'

License

MIT OR Apache-2.0