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:
| Mode | Manifest lives | When to use |
|---|---|---|
{ mode: "embedded" } | Inside the asset container | The default. The credential travels with the file; any viewer or verifier that reads the container sees it. |
{ mode: "remote" } | Indexed by the signer for recovery | The 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 indexed | Maximum 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
- Sign an asset
- File streaming: streaming sign and embed for large masters
- Soft binding: recovery for
remoteandboth