Part of #239. Blocked by #427, #428, and the first container slice to land (#429).
Goal
A dev-only differential oracle against c2pa-rs in a workspace-excluded tooling/c2pa-oracle,
exercising both directions the epic names:
- gamut reserves → an external signer completes →
c2pa-rs validates.
c2pa-rs embeds → gamut locates the identical byte range.
Direction 2 is available as soon as #429 lands. Direction 1 needs the first write slice
(#444, itself behind #443).
Scope
Shape
tooling/gamut-dng-real-conformance is the model: workspace-excluded, invoked by manifest path,
its own Cargo.toml, covered by the tooling fmt gate but not by the coverage or mutation gates.
Being a Rust crate, c2pa-rs needs no third_party/ submodule — this is the cheapest oracle
in the workspace to stand up.
Build configuration is not optional
c2pa = { version = "...", default-features = false, features = ["rust_native_crypto"] }
The default openssl feature is pulled vendored and would compile OpenSSL from C source into
a dev build. That is exactly the outcome the epic's "no crypto in the shipped graph" criterion
exists to prevent leaking, and it would also make the oracle the slowest thing in CI.
Three facts about c2pa-rs that shape the design
Builder's *_embeddable path plus composed_manifest(bytes, "application/c2pa") yields
the raw manifest-store bytes for a host to embed itself, and CallbackSigner carries a
reserve_size. c2pa-rs already models signing as reserve-then-fill — which is precisely the
seam gamut exposes, so direction 1 needs no adapter beyond a test signer.
- There is no cheap parse-only mode.
ValidationState::Invalid is also what you get when
verification is disabled, so c2pa-rs cannot serve as a "parse but don't judge" front end. This
is why gamut owns the locate/bound step rather than shelling out for it — record that in the
crate's docs so nobody tries to delete gamut's locator later.
- Do not build on
c2pa::jumbf_io. Its public functions name crate-private traits
(CAIRead/CAIReadWrite) — a private-in-public leak that will not compile against from outside.
What the tests assert
Release topology
mise run check-release-deps: a publishable crate must not dev-depend on another publishable
workspace crate without a normal dependency edge. A workspace-excluded tooling/ crate is outside
that rule, which is the other reason the oracle lives there.
Reference
C2PA Technical Specification 2.4 §15.12 (validation), §18.5–§18.6 (bindings). Staked by #427.
Oracle: c2pa-rs (Apache-2.0 / MIT), verified against 0.90.16.
Part of #239. Blocked by #427, #428, and the first container slice to land (#429).
Goal
A dev-only differential oracle against
c2pa-rsin a workspace-excludedtooling/c2pa-oracle,exercising both directions the epic names:
c2pa-rsvalidates.c2pa-rsembeds → gamut locates the identical byte range.Direction 2 is available as soon as #429 lands. Direction 1 needs the first write slice
(#444, itself behind #443).
Scope
Shape
tooling/gamut-dng-real-conformanceis the model: workspace-excluded, invoked by manifest path,its own
Cargo.toml, covered by the tooling fmt gate but not by the coverage or mutation gates.Being a Rust crate,
c2pa-rsneeds nothird_party/submodule — this is the cheapest oraclein the workspace to stand up.
Build configuration is not optional
The default
opensslfeature is pulled vendored and would compile OpenSSL from C source intoa dev build. That is exactly the outcome the epic's "no crypto in the shipped graph" criterion
exists to prevent leaking, and it would also make the oracle the slowest thing in CI.
Three facts about c2pa-rs that shape the design
Builder's*_embeddablepath pluscomposed_manifest(bytes, "application/c2pa")yieldsthe raw manifest-store bytes for a host to embed itself, and
CallbackSignercarries areserve_size. c2pa-rs already models signing as reserve-then-fill — which is precisely theseam gamut exposes, so direction 1 needs no adapter beyond a test signer.
ValidationState::Invalidis also what you get whenverification is disabled, so c2pa-rs cannot serve as a "parse but don't judge" front end. This
is why gamut owns the locate/bound step rather than shelling out for it — record that in the
crate's docs so nobody tries to delete gamut's locator later.
c2pa::jumbf_io. Its public functions name crate-private traits(
CAIRead/CAIReadWrite) — a private-in-public leak that will not compile against from outside.What the tests assert
same span, not merely overlapping. This is the assertion that would have caught the
LBox-only mis-bounding hazardgamut-heic/STATUS.mdrecords as deferred (gamut-heic: locate the C2PA manifest store and report its byte range #429).box_purpose = update, if c2pa-rs can bemade to emit one — the one in-spec BMFF layout gamut-heic: locate the C2PA manifest store and report its byte range #429's probe cannot distinguish. If it cannot,
say so in the crate docs: that is itself the empirical finding, and it closes the deferred row.
C2paPolicy), and c2pa-rs must report the derivative as unsigned rather than invalid.Release topology
mise run check-release-deps: a publishable crate must not dev-depend on another publishableworkspace crate without a normal dependency edge. A workspace-excluded
tooling/crate is outsidethat rule, which is the other reason the oracle lives there.
Reference
C2PA Technical Specification 2.4 §15.12 (validation), §18.5–§18.6 (bindings). Staked by #427.
Oracle:
c2pa-rs(Apache-2.0 / MIT), verified against 0.90.16.