c2pa-check 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,189 @@
1
- # Temporary Holding Version
1
+ # c2pa-check
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Verify Content Credentials (C2PA) in files and URLs, and fail a build when provenance disappears.
4
+
5
+ ```bash
6
+ brew install c2pa-design/tap/c2pa-check # macOS / Linux
7
+ npx c2pa-check photo.jpg # no install
8
+ cargo install c2pa-check # from source
9
+ ```
10
+
11
+ ```console
12
+ $ c2pa-check image.jpg
13
+ image.jpg
14
+ Content Credentials PRESENT
15
+ Manifest VALID
16
+ Trust TRUSTED (2026-08-13-a1b2c3d4e5f6)
17
+ Signer OpenAI Inc. · DigiCert
18
+ Digital source trainedAlgorithmicMedia (AI generated)
19
+ Actions c2pa.created
20
+ ```
21
+
22
+ ```bash
23
+ c2pa-check 'dist/**/*.{jpg,png,webp}' --coverage 100 # CI gate
24
+ c2pa-check https://cdn.example.com/hero.jpg --expect trusted
25
+ c2pa-check photo.jpg --format json | jq .result.credential.status
26
+ c2pa-check inspect photo.jpg # manifest tree
27
+ c2pa-check trust status # which list judged it
28
+ c2pa-check mcp # stdio MCP server
29
+ c2pa-check 'dist/**/*.jpg' --format junit --output c2pa-check.xml # report to a file
30
+ c2pa-check doctor # key, quota, network, webhook secret, Node
31
+ ```
32
+
33
+ `--format text|json|ndjson|junit` (default `text`) and `--output PATH` (default: stdout) apply
34
+ to the check command. JUnit marks every target that is not `valid_trusted` as a failure.
35
+
36
+ | Exit | Meaning |
37
+ |---|---|
38
+ | `0` | pass |
39
+ | `1` | expectation or coverage failed · `doctor` found a failing check · `carry --strict` found an unpaired file |
40
+ | `2` | usage or I/O error |
41
+ | `3` | unreadable asset (missing or unreadable local file, over 64 MiB, malformed manifest) |
42
+ | `4` | a URL could not be fetched |
43
+ | `5` | `carry` refused (rule in `--json`) |
44
+
45
+ ## Check your setup
46
+
47
+ ```console
48
+ $ npx -y c2pa-check doctor
49
+ ok api_key live key c2pa_live_AbC…
50
+ ok network https://api.c2pa.design/v1 answered HTTP 200
51
+ ok whoami live key, organization Acme, plan team, signatures 940 of 1000 left until 2026-11-01
52
+ ok webhook_secret valid (whsec_ + base64)
53
+ ok node Node.js 22.11.0
54
+ ```
55
+
56
+ | Check | Reads | Fails when |
57
+ |---|---|---|
58
+ | `api_key` | `C2PA_API_KEY` (deprecated fallback `C2PA_DESIGN_API_KEY`) | not `c2pa_live_` / `c2pa_test_` + 32 letters and digits (unset is a warning) |
59
+ | `network` | `C2PA_API_BASE` (deprecated fallback `C2PA_DESIGN_API_URL`, default `https://api.c2pa.design/v1`) | no HTTP answer |
60
+ | `whoami` | `GET {C2PA_API_BASE}/whoami` | the key is refused; an exhausted quota is a warning |
61
+ | `webhook_secret` | `C2PA_WEBHOOK_SECRET` | not `whsec_` + standard base64 (unset is skipped) |
62
+ | `node` | the Node.js running `npx` | older than 18 |
63
+
64
+ `doctor --json` prints `{ok, api_base, checks: [{name, state, detail, data?}]}` with `state`
65
+ `ok | warn | fail | skip`; the `whoami` check carries the API answer in `data`.
66
+
67
+ ## In CI
68
+
69
+ ```yaml
70
+ - uses: c2pa-design/c2pa-check-action@v1
71
+ with:
72
+ paths: "public/**/*.{jpg,png,webp}"
73
+ coverage: 100
74
+ format: junit
75
+ output: c2pa-check.xml
76
+ ```
77
+
78
+ The action installs the release named by `version` (default `v0.2.0`) and checks it against the
79
+ release's `SHA256SUMS` before running it. Every release publishes `SHA256SUMS` and a GitHub
80
+ build-provenance attestation (`gh attestation verify c2pa-check-<target>.tar.gz -R
81
+ c2pa-design/c2pa-check`); the npm packages are published with npm provenance
82
+ (`npm audit signatures`).
83
+
84
+ Outside GitHub Actions, any image with Node.js 18+ runs `npx -y c2pa-check@0.2.0`
85
+ (`node:22-bookworm-slim` is the smallest that also has the glibc tools most pipelines expect;
86
+ the binary itself is static musl, so `node:22-alpine` works too). Pin the version so `npx` hits
87
+ its cache instead of resolving `latest` on every run, and cache `~/.npm` between jobs; or skip
88
+ Node entirely and download the static binary from the release.
89
+
90
+ Add `urls:` to check what your CDN actually serves after a deploy — that is where credentials
91
+ usually disappear.
92
+
93
+ ## Keep the credential through conversion
94
+
95
+ Re-encoding, resizing or compressing a file drops its manifest, and copying the old manifest back
96
+ does not help: the signature covers the old bytes. `carry` writes a new manifest into the
97
+ converted copy with the original as its `parentOf` ingredient, so the chain back to the
98
+ generator survives.
99
+
100
+ ```bash
101
+ npx -y c2pa-check carry --from hero.png --to hero.webp # one pair, in place
102
+ npx -y c2pa-check carry 'public/**/*.{webp,avif}' --from-dir src/ # pairs by file name
103
+ c2pa-check carry --from clip.mov --to clip.mp4 --force # non-picture media
104
+ c2pa-check keygen --out-dir .c2pa # cert.pem + key.pem for CI
105
+ c2pa-check carry --from hero.png --to hero.webp --json # machine-readable result
106
+ ```
107
+
108
+ | Signer (first that is set) | Signed as | Verifies as |
109
+ |---|---|---|
110
+ | `C2PA_SIGN_CERT` + `C2PA_SIGN_KEY` (PEM, a path, or `*_FILE`) | your certificate | `valid_trusted` if your CA is on the C2PA trust list |
111
+ | `C2PA_API_KEY` (deprecated fallback `C2PA_DESIGN_API_KEY`) | "<your verified domain> via c2pa.design" | `valid_untrusted` |
112
+ | nothing | a local key in `~/.config/c2pa-check/identity` | `valid_untrusted` |
113
+
114
+ Pictures are compared first by perceptual hash; a different picture or an original without a
115
+ credential is refused with exit `5`. With an API key, c2pa.design checks the carry again and
116
+ signs only an honest one; if hosted signing is unavailable, `carry` signs locally and warns.
117
+ Never `COPY` or `ARG` a key into a Docker image: use `RUN --mount=type=secret`.
118
+
119
+ `carry --json` prints one object (an array for several files):
120
+
121
+ ```json
122
+ {"status": "refused", "rule": "different_picture", "message": "the two files are not the same picture",
123
+ "source": "hero.png", "derived": "hero.webp"}
124
+ ```
125
+
126
+ `status` is `carried | composed | skipped | refused | error | unpaired | ambiguous`; `output`
127
+ is the file written; `credential_status` is how the written file verifies. `rule` is set on a
128
+ refusal: `different_picture`, `source_unsigned`, `low_quality`, `not_comparable`, or the rule
129
+ c2pa.design returned with `carry_rejected` (`source_invalid`, `ingredient_mismatch`,
130
+ `action_not_allowed`, `generator_changed`, `certificate_mismatch`, …). Refusals exit `5`.
131
+
132
+ ### Composites and generated assets
133
+
134
+ An atlas, sprite sheet or collage is a new work, not a conversion. `--compose` signs the
135
+ rendered file with every source attached as a `componentOf` ingredient (each keeps its own
136
+ manifest) and a `c2pa.created` action (digital source type `composite`), plus `c2pa.placed`
137
+ per source:
138
+
139
+ ```bash
140
+ c2pa-check carry --compose a.png b.png c.png --to atlas.webp
141
+ c2pa-check carry --compose base.png logo.png --to banner.webp --edited # base.png is parentOf
142
+ ```
143
+
144
+ `--edited` records `c2pa.opened` on the first source (as `parentOf`), `c2pa.edited`, and
145
+ `c2pa.placed` for the rest. Composites are signed with `C2PA_SIGN_CERT` / `C2PA_SIGN_KEY` or
146
+ the local key; hosted signing does not accept composites yet, so with only an API key the
147
+ composite is signed locally and `carry` warns.
148
+
149
+ ## For AI agents
150
+
151
+ The agent skill lives in [c2pa-design/skills](https://github.com/c2pa-design/skills):
152
+
153
+ ```bash
154
+ npx skills add c2pa-design/skills
155
+ ```
156
+
157
+ MCP only: `claude mcp add c2pa-check -- npx -y c2pa-check mcp`. `initialize` echoes the client's
158
+ `protocolVersion` when supported (`2026-07-28`, `2025-11-25`, `2025-06-18`, `2025-03-26`), else `2026-07-28`.
159
+
160
+ ## How trust works
161
+
162
+ A signature proves the bytes have not changed since signing. It says nothing about *who*
163
+ signed. "Trusted" means the signer's certificate chains to the official
164
+ [C2PA trust list](https://github.com/c2pa-org/conformance-public/tree/main/trust-list), which is
165
+ compiled into every release; `c2pa-check trust status` prints the snapshot date and whether
166
+ upstream has moved, `trust update` fetches a newer one, and `--offline` pins CI to the bundled
167
+ copy. `docs/TRUST.md` has the details.
168
+
169
+ Absence of a credential proves nothing: most files on the internet carry none.
170
+
171
+ ## Library
172
+
173
+ `c2pa-check-core` produces the same normalized document (schema v1,
174
+ `crates/c2pa-check-core/schema/result.v1.json`) that the browser tools and the c2pa.design API
175
+ return, so a local check and a hosted check never disagree.
176
+
177
+ ```rust
178
+ let bundle = c2pa_check_core::TrustBundle::bundled();
179
+ let report = c2pa_check_core::verify(&bytes, "image/jpeg", &bundle, &Default::default())?;
180
+ println!("{}", report.credential.status.as_str());
181
+ ```
182
+
183
+ ## Licence
184
+
185
+ [MIT](LICENSE-MIT) OR [Apache-2.0](LICENSE-APACHE). Contributions under DCO, no CLA.
186
+
187
+ ---
188
+
189
+ Monitor production provenance across your whole pipeline at **[c2pa.design](https://c2pa.design)**.
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ const { spawnSync } = require("node:child_process");
3
+
4
+ const pkg = `c2pa-check-${process.platform}-${process.arch}`;
5
+ const exe = process.platform === "win32" ? "c2pa-check.exe" : "c2pa-check";
6
+ let bin;
7
+ try {
8
+ bin = require.resolve(`${pkg}/${exe}`);
9
+ } catch {
10
+ console.error(`c2pa-check: no prebuilt binary for ${process.platform}-${process.arch}; use cargo install c2pa-check`);
11
+ process.exit(2);
12
+ }
13
+ const r = spawnSync(bin, process.argv.slice(2), { stdio: "inherit", env: { ...process.env, C2PA_CHECK_NODE_VERSION: process.versions.node } });
14
+ if (r.error) throw r.error;
15
+ process.exit(r.status ?? 1);
package/package.json CHANGED
@@ -1,6 +1,26 @@
1
1
  {
2
2
  "name": "c2pa-check",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.2.0",
4
+ "license": "MIT OR Apache-2.0",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/c2pa-design/c2pa-check.git"
8
+ },
9
+ "homepage": "https://c2pa.design",
10
+ "description": "Verify Content Credentials from the command line and fail a build when provenance disappears.",
11
+ "bin": {
12
+ "c2pa-check": "bin/c2pa-check.js"
13
+ },
14
+ "files": [
15
+ "bin"
16
+ ],
17
+ "engines": {
18
+ "node": ">=18"
19
+ },
20
+ "optionalDependencies": {
21
+ "c2pa-check-linux-x64": "0.2.0",
22
+ "c2pa-check-linux-arm64": "0.2.0",
23
+ "c2pa-check-darwin-arm64": "0.2.0",
24
+ "c2pa-check-darwin-x64": "0.2.0"
25
+ }
6
26
  }