c2pa-utilities
c2pa-utilities is a home for shared utilities and libraries used by both c2pa-web and c2pa-node. Most clients will get these transitively as a dependency of one of those two packages, but @contentauth/c2pa-utilities is also published standalone for cases where you want to reuse a piece of it directly.
Installation
npm install @contentauth/c2pa-utilities
API reference documentation
Complete API documentation is generated from TypeScript source using TypeDoc and published to GitHub Pages.
Utilities
Context and Settings
Settings is a plain, JSON-serializable object configuring SDK behavior around trust anchors, verification options, and Reader/Builder options.
Context is a small, immutable wrapper around Settings, and is the recommended way to configure a Reader/Builder. Context objects are passed directly to the call that creates a Reader/Builder instance, so one running SDK instance can freely create many different Reader/Builders, each with its own Context. Context changes do not propagate, since the Context is snapshotted when used to construct a Reader/Builder. Therefore, it can be safely used to construct multiple instances.
See c2pa-web's README for an example.
Creating a Context
import { Context } from '@contentauth/c2pa-utilities';
const context = new Context({
verify: {
verifyTrust: true
},
trust: {
trustAnchors: 'https://example.com/trust-anchors.pem'
}
});
Combining Settings
Each Context holds whatever single Settings object was passed to its constructor and does not handle any merging of settings. To combine more than one Settings source, merge them first with mergeSettings(), then construct a Context from the single, merged result:
import { Context, mergeSettings } from '@contentauth/c2pa-utilities';
const base = { verify: { verifyTrust: true } };
const override = { verify: { verifyAfterSign: true } };
const context = new Context(mergeSettings(base, override));
// context.settings is { verify: { verifyTrust: true, verifyAfterSign: true } }
Resolving a Context to JSON
Settings are passed across the WASM (c2pa-web)/native(c2pa-node) boundary as a JSON string. toJson() resolves any trust-anchor URLs embedded in the settings (fetching and validating them) and serializes the result:
const contextJson = await context.toJson();
// '{"verify":{"verify_trust":true},"trust":{"trust_anchors":"-----BEGIN CERTIFICATE-----..."}}'
This step is asynchronous, and can throw if a trust-anchor URL fails to resolve. Bindings call it once, right after building the base Context, rather than on every Reader/Builder call. Internally, toJson() is a thin wrapper over resolveSettings, below.
Building Settings directly
Context is built on a handful of lower-level Settings helpers, which remain available directly for cases that don't need a Context at all:
import {
createTrustSettings,
createVerifySettings,
mergeSettings,
resolveSettings
} from '@contentauth/c2pa-utilities';
const trustSettings = createTrustSettings({
trustAnchors: 'https://example.com/anchors.pem'
});
const verifySettings = createVerifySettings({
verifyTrust: true,
verifyAfterReading: true
});
const settings = mergeSettings(trustSettings, verifySettings);
// Resolves trust-anchor URLs and serializes to the snake_case JSON string the native SDK expects.
const settingsJson = await resolveSettings(settings);
resolveSettings is the entry point most direct callers want. It resolves any trust/cawgTrust URL fields (fetching and inlining the PEM content, retrying transient failures), and returns the result as a snake_case JSON string. settings is merged on top of this package's defaults, so resolveSettings always returns a value, even when called with undefined. To combine more than one Settings object first, merge them with mergeSettings() before calling resolveSettings, as above.
Other exports:
createTrustSettings/createCawgTrustSettings/createVerifySettings— construct aSettingsfragment for one section.mergeSettings— deep-merge any number ofSettingsfragments, with later arguments overriding earlier ones. Nested fields are merged rather than overwritten.settingsToJson— serialize aSettingsobject to its snake_case JSON form without resolving trust-anchor URLs.snakeCaseify— the lower-level camelCase-to-snake_case object convertersettingsToJson/resolveSettingsuse internally.loadSettingsFromUrl— fetch a settings JSON document from a URL, with retry.resolveTrustSettings— resolve just aTrustSettingsobject's URL fields in place; used internally byresolveSettings.
Fetch with retry
fetchWithRetry and fetchWithRetryRaw wrap fetch with exponential backoff, Retry-After handling, and (for fetchWithRetry) a response size cap. The retry mechanism is fixed; however, policy details such as retry count, backoff timing, which statuses/errors are retryable, and the maximum honored Retry-After delay are configurable per call via FetchWithRetryOptions.
import { fetchWithRetry, fetchWithRetryRaw } from '@contentauth/c2pa-utilities';
// GET as text, retrying on network errors, 429, and 5xx, capped at 1 MB by default.
const text = await fetchWithRetry('https://example.com/anchors.pem');
// For other methods, headers, or bodies, or to handle the response yourself, use fetchWithRetryRaw.
const response = await fetchWithRetryRaw('https://example.com/upload', {
method: 'POST',
body: payload
});
Both functions accept an FetchWithRetryOptions object to override the defaults:
await fetchWithRetry(url, {
maxRetries: 5,
initialRetryDelayMs: 500,
maxRetryDelayMs: 5_000,
maxRetryAfterMs: 60_000,
maxResponseBytes: 5 * 1024 * 1024,
isRetryableStatus: (status) => status === 429 || status >= 500,
isRetryableError: (error) => true,
fetch: myFetchImplementation
});
An AbortError is never retried, and a malformed URL throws immediately rather than being retried.
Asset size validation
validateAssetSize is the shared size check used by both Reader implementations before reading an asset.
import { validateAssetSize, AssetTooLargeError, DEFAULT_MAX_SIZE_IN_BYTES } from '@contentauth/c2pa-utilities';
try {
validateAssetSize(sizeInBytes, maxSizeInBytes); // pass 0 to use DEFAULT_MAX_SIZE_IN_BYTES
} catch (e) {
if (e instanceof AssetTooLargeError) {
// asset exceeds the resolved limit
}
}
Signing algorithm
SigningAlg is the lowercase signing algorithm type accepted/produced by the core native library at the signer construction boundary (e.g. Signer.newSigner(cert, key, alg)). It's derived from the PascalCase SigningAlg exported by @contentauth/c2pa-types, which describes the casing used when a manifest's SignatureInfo.alg is serialized.
Library development
Prerequisites
Ensure the repo-wide prerequisites are installed:
See the c2pa-js README for details.
Building
To build:
nx build c2pa-utilities
Testing
This library uses Vitest, with msw to mock fetch in the settings and fetch-with-retry tests.
To run the tests:
nx test c2pa-utilities