Skip to content

Conformance

The store implements the C2PA Soft Binding Resolution API of the upcoming C2PA specification 2.5, whose OpenAPI document still carries version 2.4.0. This page lists what it implements, where it departs from the C2PA OpenAPI definition, and how it is tested against it.

Implemented

Group Routes
Store POST /v1/manifests, DELETE /v1/manifests/{manifestId}
Fetch GET and HEAD /v1/manifests/{manifestId}
Query GET and POST /v1/matches/byBinding
Service GET /v1/services/supportedAlgorithms, /capabilities, /status, /.well-known/c2pa-soft-binding-resolution

/v1/services/capabilities reports c2paSpecificationVersion as the info.version of the C2PA OpenAPI subset the store implements (2.4.0), and the one optional capability it has: storeManifests. The discovery document at /.well-known/c2pa-soft-binding-resolution gives apiEndpoint, capabilitiesEndpoint and statusEndpoint as absolute URLs. Every GET route also answers HEAD.

Not implemented: byContent, byReference, the bindings routes and receipts.

The served contract, /openapi/openapi.yaml, references the C2PA schemas in /openapi/c2pa-sbr.json, a verbatim copy (CC BY 4.0) of the operations the store implements. The test suite checks that every C2PA operation keeps its operationId, parameters and response schemas in the store's contract.

Deviations

Topic C2PA OpenAPI The store
Uploads OAuth 2.0 with the scope store:manifests Anonymous, limited per client address
Fetches and queries OAuth 2.0 with the scope fetch:manifests Open, limited per client address
Deletes OAuth 2.0 with the scope store:manifests The operator's bearer token; 401 with WWW-Authenticate: Bearer without it
returnReceipt Requests a receipt for the stored manifest Accepted and ignored; no receipts
returnActiveManifest Requests only the active manifest Accepted and ignored; the full Manifest Store is returned
declare Not defined Extension of POST /v1/manifests: whether the store declares the ISCC soft binding
Response fields manifestId; manifestId, similarityScore Extension fields manifestUrl and declaration on uploads, isccId on matches
Further statuses 200, 204, 400, 403, 404, 414, 500 Also 409 (ID taken), 410 (deleted), 413 (too large), 415 (media type), 429 (rate)

Matches never carry an endpoint: every match is stored in the same repository, and the C2PA API defines that a match without endpoint is fetched from the API that answered the query.

Soft-binding values written as text

The C2PA specification defines the value of a soft-binding block as a byte string, and c2pa-rs reports byte strings as base64 text. The store therefore reads a text value as base64 and indexes the decoded bytes, which is what queries send. Some manifests in circulation write the value as plain text instead (for example hex or binary digits). Such a value cannot be told apart from base64 in the c2pa-rs report: text that is not valid base64 is not indexed, and text that happens to be valid base64 is indexed as the bytes it decodes to, so an exact-match query for it may not find it.

Known issue in the C2PA schema

The C2PA schema c2pa.softBindingAlgList places its oneOf (either watermarks or fingerprints required) on the items of watermarks instead of on the list itself. A strict validator therefore rejects every non-empty watermarks list, although each entry has the alg the specification intends. GET /v1/services/supportedAlgorithms answers with the intended shape, so its answer fails strict validation against the verbatim schema as soon as the store has indexed a watermark.

Conformance harness

The Cognitive Proof Soft Binding API conformance harness (version 1.0.0) runs against the container image in CI and locally with uv run poe conformance (scripts/conformance.sh, needs Node).

The harness decides which tests to run from the capabilities the store reports. scripts/conformance.sh removes storeManifests from them (see below), so the harness runs only its tests that need no capability:

Harness suite Tests run Result
Service discovery endpoints capabilities, status, supportedAlgorithms and the discovery document passes
supportedAlgorithms shape and agreement Every entry has an alg; capabilities and discovery report one version passes
GET /manifests/:manifestId An unknown manifest ID answers 404 passes
Auth on GET /manifests/:manifestId An unauthenticated fetch answers 200, 401, 403 or 404 passes
Store round trip, auth on store routes, receipt Need storeManifests skipped
Bindings Need storeBindings and storeManifests skipped

Harness 1.0.0 queries /matches/byBinding only inside its bindings suite, so the query routes have no harness coverage, and it never fetches a stored manifest: its fetch checks are the 404 for an unknown ID and the status of an unauthenticated fetch. Queries, fetches of stored manifests and the store routes are covered by the test suite of the repository instead, with Manifest Stores signed by c2pa-python.

storeManifests is withheld because the store suites assume a different store: they post plain text as application/c2pa and expect 401 without a token, and they expect 404 for a deleted manifest. The store refuses such a body with 400, as its contract documents for anything that is not an acceptable Manifest Store, accepts anonymous uploads, and answers 410 for a deleted manifest.

Harness 1.0.0 cannot run from its npm package as published; the script installs it into a temporary directory and runs its tests from there.