Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

To read C2PA provenance data from an image in Node.js, install @contentauth/c2pa-node, pass the image bytes with a known mimeType to Reader.fromAsset, and then inspect the manifest store from reader.json() and the active manifest from reader.getActive(). Any IPTC Digital Source Type term you find describes how the image was made, such as generated by AI, edited with generative tools, edited by humans, captured by a camera, or composited. It is a claim recorded in the manifest, not a verdict on the image.

The package is documented in the c2pa-node README, which now lives in the c2pa-js monorepo. The Content Authenticity Initiative’s JavaScript library documentation reports that the repository was merged into that monorepo in June 2026. The guidance below reflects documentation accessed on 7 October 2026.

Before you start

  • Package: @contentauth/c2pa-node. Install it with npm install @contentauth/c2pa-node.
  • Platform: The package uses a native binary. The README lists Node and native-binary platform prerequisites; check them against your operating system and architecture before you deploy.
  • Status: The library is documented as an early version. Pin the version you test, and recheck the README when you upgrade.

Choose how the library receives the image

The Reader accepts an asset. You can give it the bytes you already hold in memory, or a file-backed asset that points at the file on disk. The two forms behave differently with large or untrusted input, so choose before you write the parser.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Input form What the README documents Memory behavior MIME type handling Use it when
Buffer: { buffer, mimeType } Accepted by Reader.fromAsset. The complete file is allocated in memory first. The size rejection is applied after that allocation, so it does not protect memory. Supply mimeType whenever it is known. Small files that your application has already loaded and trusts.
File-backed asset Documented in the README. Use the exact asset shape shown there for your installed version. The README recommends this form for large or untrusted images, because the buffer path allocates the whole file before any size limit runs. Supply mimeType whenever it is known. User uploads, large media, and any file you have not vetted.

Buffer input

If the bytes are already in memory, read them with readFile from node:fs/promises and pass them with a MIME type. Do not treat a size check on a buffer as a defense against large uploads, because the bytes already exist when the check runs.

File-backed input

For uploads, store the file to disk first, validate its size and type at the HTTP layer, and then open it through a file-backed asset. This keeps the full file out of your Node process heap before the library decides whether to accept it.

Why the MIME type matters

The README states:

Always supply mimeType when it’s known, as byte-based detection is slower than a direct lookup and can be unreliable, which could surface as more confusing errors later on.

Source: the c2pa-node README. If your application already knows the format from its upload form or from a trusted extension check, pass that value. Byte-based detection is the fallback, not the default.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Read the manifest store and the active manifest

The following sequence reads an embedded manifest from a JPEG. It is adapted from the example in the official API documentation.

  1. Install the package with npm install @contentauth/c2pa-node.
  2. Load the bytes with readFile('image.jpg'), or prepare a file-backed asset for large or untrusted input.
  3. Open the Reader with await Reader.fromAsset({ buffer, mimeType: 'image/jpeg' }). The call is asynchronous.
  4. Read the full manifest store with reader.json(). This returns every manifest the file carries, so use it when you need the history.
  5. Read the active manifest with reader.getActive(). This is the manifest that describes the asset as it is now.
import { readFile } from 'node:fs/promises';
import { Reader } from '@contentauth/c2pa-node';

const buffer = await readFile('image.jpg');
const reader = await Reader.fromAsset({
  buffer,
  mimeType: 'image/jpeg',
});

const manifestStore = reader.json();
const activeManifest = reader.getActive();
console.log({ manifestStore, activeManifest });

The Reader can also report whether the manifest is embedded in the file or fetched from a remote URL. Check the README for the exact property names in your version, and record that fact alongside the result, because a remote manifest has a different trust path from one embedded in the file.

Find the source-type entries

The C2PA specification describes assertions as namespaced labels, commonly beginning with c2pa.. One manifest can contain several assertions, and more than one assertion can share the same type. Do not assume the manifest has one flat “AI label” property. Action records can include a digitalSourceType value, which is either an IPTC term or a C2PA-specific value.

Because the position of the field can change, a recursive search is more reliable than a fixed path. The helper below collects every digitalSourceType string it finds, and you can run it on the object returned by reader.getActive() for the current claim, or on reader.json() for the full history.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
function collectDigitalSourceTypes(node, found = []) {
  if (Array.isArray(node)) {
    for (const item of node) collectDigitalSourceTypes(item, found);
  } else if (node && typeof node === 'object') {
    for (const [key, value] of Object.entries(node)) {
      if (key === 'digitalSourceType' && typeof value === 'string') {
        found.push(value);
      } else {
        collectDigitalSourceTypes(value, found);
      }
    }
  }
  return found;
}

The C2PA specification also says that its schema material is there to aid understanding and does not recommend that manifest consumers perform schema validation as a general reading step. Parse the fields you need and handle unexpected shapes gracefully rather than rejecting the whole file.

Map IPTC terms to their meanings

The IPTC Digital Source Type vocabulary describes “from which source a digital image was created.” Each term has its own definition, and the distinctions matter. The table lists the examples in the currently reviewed vocabulary at cv.iptc.org/newscodes/digitalsourcetype. The vocabulary contains more terms than these, so look up any term you do not list.

Term What the IPTC definition covers Source category
trainedAlgorithmicMedia Created using generative AI. Generated media
compositeWithTrainedAlgorithmicMedia Edited using generative AI, including generative fill or outpainting. Generative edit
humanEdits Augmentation, correction, or enhancement by humans using non-generative tools. Human edit
digitalCapture Captured from real life with a digital camera or recording device. Capture
composite A mix of several elements, which may or may not use generative AI. Composite
minorHumanEdits Retired. Use humanEdits. Retired term
softwareImage Retired in favor of more specific terms. Retired term

Do not render every term as “AI-generated.” A humanEdits entry and a compositeWithTrainedAlgorithmicMedia entry are both edits, but only the second involves generative AI. Likewise, composite does not tell you whether generative AI was used. Treat it as a flag that the image combines several sources, then read the rest of the manifest for detail.

The lookup below uses a Map so that unknown or inherited property names cannot match by accident. It keeps the last path segment of a URI, so it handles both bare term names and full IPTC URIs.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const IPTC_SOURCE_TERMS = new Map([
  ['trainedAlgorithmicMedia', 'Created using generative AI.'],
  ['compositeWithTrainedAlgorithmicMedia', 'Edited using generative AI, including generative fill or outpainting.'],
  ['humanEdits', 'Augmentation, correction, or enhancement by humans using non-generative tools.'],
  ['digitalCapture', 'Captured from real life with a digital camera or recording device.'],
  ['composite', 'A mix of several elements, which may or may not use generative AI.'],
]);

const RETIRED_TERMS = new Map([
  ['minorHumanEdits', 'humanEdits'],
  ['softwareImage', null],
]);

function describeSourceType(value) {
  const term = String(value).split('/').pop();
  if (IPTC_SOURCE_TERMS.has(term)) {
    return { term, status: 'current', meaning: IPTC_SOURCE_TERMS.get(term) };
  }
  if (RETIRED_TERMS.has(term)) {
    return { term, status: 'retired', replacement: RETIRED_TERMS.get(term) };
  }
  return { term, status: 'unrecognized' };
}

Report unrecognized values as they are. A C2PA-specific value or a term added after your mapping was written is not an error in the file, and it should not be silently mapped to a general category.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Keep validation and trust separate

A manifest can be read without being verified, and a verified manifest can still come from a signer you do not trust. Keep three questions apart in your code and in your interface.

  • Was the manifest parsed? The Reader returned the manifest store and the active manifest, so the assertions can be displayed.
  • Did cryptographic validation pass? The library reports whether the manifest’s binding and signature checks produced a valid result. The C2PA specification describes hard bindings as the way a validator establishes that a manifest belongs with the asset and that the covered asset bytes have not changed.
  • Is the signer trusted? This is a policy decision made by your application, using the trust settings you configure. A valid signature from an untrusted signer is not the same as a trusted one.

Configure verification and trust through Context, as described in the README. The README marks raw per-instance settings as deprecated, so avoid them in new code.

A decision table for user-facing output

Manifest parsed Cryptographic validation Signer trusted under your policy What to show
Yes Passed Yes The source-type claim, labeled as a claim by a trusted signer.
Yes Passed No The source-type claim, with a note that the signer is not in your trust policy.
Yes Failed Any Do not present the source-type claim as verified. Show the validation failure.
No manifest found Not applicable Not applicable State that no readable Content Credentials were found in the file.

Handle failures and edge cases

  • Wrong or missing MIME type: Byte-based detection can fail or produce errors that are harder to trace. If an upload fails to parse, check the MIME type you passed before you suspect the file.
  • Oversized input: Apply your size limit before you load the file. With a buffer, the file is already in memory when a size rejection runs.
  • Unrecognized or retired terms: Map them as described above and keep the raw value in your logs.
  • Several source-type entries: Report each entry with its assertion context. Do not collapse them into one flag.

What a source-type label does not establish

A digitalSourceType entry is a provenance claim made by whoever built the manifest. It is not an AI detector. An image without a label has not been shown to be unedited or human-made, and an image with a generative label has not been shown to be fake or misleading. Present the claim, its validation result, and the signer’s trust status, and let the reader judge from those facts.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Keep the parser current

Both the library and the vocabulary change. Check three things when you upgrade or deploy:

Keep your term table in one module so that a vocabulary update changes one place in your code.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.