Skip to main content

Storage modes

sign always returns a sidecar, the manifest as a standalone byte string. The storage option (a ManifestStorage object on options, not a field on ManifestSpec) declares where the manifest is meant to live, and you then realize that intent: embed the sidecar into the container, distribute it alongside the asset, or both.

Choosing a mode

options.storage is a required ManifestStorage object on every sign() call, selected by its mode:

ModeManifest livesWhen to use
{ mode: "embedded" }Inside the asset containerThe default. The credential travels with the file; any viewer or verifier that reads the container sees it.
{ mode: "remote" }Indexed by the signer for recoveryThe container can't or shouldn't carry the manifest, or you expect copies to be stripped or transcoded. The manifest is recovered from the registry by binding.
{ mode: "both" }Embedded and indexedMaximum durability: the credential travels with the file, and a stripped copy can still be recovered from the registry.

For remote and both, soft bindings keep transcoded copies recoverable — a bindings list carried on the persisted storage mode itself ({ mode: "remote", bindings }), so embedded has no bindings field and the illegal pairing is impossible. For an image, compute its ISCC Image-Code with Binding.iscc(image) and add it; verify(bytes, [Binder.iscc()]) recovers by that same code. Watermark tokens and out-of-band video ISCCs are { alg, value } bindings you add the same way, and a manifest can carry several. See Soft binding.

Embed vs sidecar

Once you have the sidecar, embed it into the container with embed:

const signed = embed(original, sidecar);

Or distribute it as a separate .c2pa file alongside the asset:

import { writeFile } from "node:fs/promises";

await writeFile("photo.c2pa", sidecar);

A sidecar .c2pa file is useful when the container format can't hold a manifest, or when you don't want to rewrite a very large asset.

The embed signature

embed takes asset bytes and the sidecar:

embed(data: Uint8Array, sidecar: Uint8Array, format?: string): Uint8Array

The embedded bytes are returned. For large video masters, see File streaming.

See also