@oprindo/sdk
v1.0.0
Published
Oprindo SDK — C2PA signing identity and implementation evidence for generative-AI providers. Content-local by default.
Maintainers
Readme
@oprindo/sdk
TypeScript SDK for Oprindo — C2PA signing identity and implementation evidence for generative-AI providers.
Your content stays local
mark() assembles and embeds the C2PA manifest on your machine using the c2pa
native library. The only thing transmitted to Oprindo is the claim — kilobytes of
metadata and hashes. Your media bytes never leave your process in this mode.
Install
npm install @oprindo/sdkMark
import { Oprindo } from "@oprindo/sdk";
import { readFile, writeFile } from "node:fs/promises";
const oprindo = new Oprindo({ apiKey: process.env.OPRINDO_API_KEY! });
const buffer = await readFile("generated.jpg");
const result = await oprindo.mark(
{ buffer, mimeType: "image/jpeg" },
{ generator: { name: "YourProduct", version: "1.0.0" } },
);
await writeFile("generated.marked.jpg", result.asset.buffer);
console.log(result.trustState, result.evidenceId);Every signature carries an explicit trust state (sandbox, pre_conformance, or
trusted) — the SDK and API never blur them. Sandbox (test-key) signatures carry a
visible sandbox marker in the manifest's generator info.
Provenance actions
Omit actions and the SDK inspects the asset and picks the correct inception
action:
| Input | Action asserted |
| --- | --- |
| No prior manifest | c2pa.created + digitalSourceType: trainedAlgorithmicMedia |
| Already carries provenance | c2pa.opened, with the prior manifest referenced as a parentOf ingredient |
This matters for conformance. C2PA requires the inception action to reflect
origin: asserting c2pa.created over content that arrived with a manifest
claims an origin you cannot vouch for. Accordingly:
c2pa.createdon an asset that already carries provenance is refused.c2pa.openedmust not carry adigitalSourceType— the origin of content you opened is the parent manifest's to state — and is refused if there is nothing to open.- An asset whose existing provenance cannot be read is refused rather than marked, because "unreadable" is not "absent".
Verify
const report = await oprindo.verify({ buffer, mimeType: "image/jpeg" });
// { manifestPresent, claimGenerator, assertions, validationStatus }Verification runs locally. No bytes are transmitted. The C2PA Conformance
Program CA and TSA trust lists are loaded, so a credential chaining to a listed
root reads as trusted and certificate validity is judged at the trusted
timestamp rather than at "now". A manifest that cannot be parsed is reported as
an oprindo.manifestUnreadable validation status rather than thrown, so
verifying untrusted third-party media cannot crash your pipeline.
Conformance
Output conforms to the requirements the C2PA Conformance Program assessed
Oprindo against (record 019fa058-c512-7f87-a214-4d8cfbda73ce, Generator
Product, Assurance Level 1):
- every assertion in the claim's
created_assertions, withgathered_assertionsempty; - the actions assertion first in
created_assertions; - CA and TSA trust lists loaded on the validation and ingredient-ingestion paths;
- an inception action that reflects origin.
The second of those is not reachable through c2pa-rs configuration — it
assembles claims in a fixed order that never puts actions first when a claim
thumbnail or ingredient is present. The SDK therefore permutes the claim between
building and signing, moving raw CBOR byte slices so the claim keeps its exact
length. See src/c2pa/claim-order.ts.
Evidence
const { records } = await oprindo.evidence({ from: "2026-08-01" });Returns your append-only implementation-evidence records — each marking event, hash- chained and mapped to EU Transparency Code Section 1 measures.
Scope
This SDK helps you implement and evidence Article 50 marking measures. Compliance depends on your complete implementation and circumstances. Nothing here is legal advice.
License
Apache-2.0
