Add C2PA Monitor experiment - #459
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## develop #459 +/- ##
=============================================
+ Coverage 74.42% 75.43% +1.01%
- Complexity 3116 3455 +339
=============================================
Files 132 139 +7
Lines 12179 13147 +968
=============================================
+ Hits 9064 9918 +854
- Misses 3115 3229 +114
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
ec97690 to
43d3bde
Compare
… tests - JSON postmeta contract; @see DIF wpai-monitor-record schema - Partial README (postmeta + constraints) Made-with: Cursor
- Format_Detector for JPEG/PNG/WebP C2PA segments - Synthetic fixtures and Format_DetectorTest Made-with: Cursor
- Streaming Manifest_Reader and Sidecar_Writer - README: sidecar layout, rationale, test fixtures Made-with: Cursor
- capture_for_attachment, sidecar, Record persistence - C2pa_MonitorTest; README: full flow, DIF schema cross-links, out of scope Made-with: Cursor
Made-with: Cursor
1e33baa to
8257f84
Compare
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Unlinked AccountsThe following contributors have not linked their GitHub and WordPress.org accounts: @lukenispel. Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases. If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
Synthetic fixtures are not valid renderable images. GD's WebP codec fatals when create_upload_object triggers wp_generate_attachment_metadata. Suppress intermediate_image_sizes_advanced in setUp/tearDown so GD is never invoked on fixture bytes. Made-with: Cursor
…kability Adds CONTEXT_URL constant to C2pa_Monitor pointing at the wpai-monitor-record context.json in DIF credential-schemas (interim raw.githubusercontent.com URL; @todo migrate to https://w3id.org/openverifiable/v1 at Phase 3). Every stored _wpai_monitor_record now includes: "@context": ["https://schema.org/", C2pa_Monitor::CONTEXT_URL] This makes the record a self-describing JSON-LD document that can be lifted directly into a credentialSubject without transformation. SCHEMA_VERSION stays at 1 — the addition is backwards-compatible (unknown keys are ignored by existing readers). Updates Record::REQUIRED_KEYS and default_for() so normalize() always fills @context even when callers omit it. Adds assertions to RecordTest and C2pa_MonitorTest. Updates README example block. Made-with: Cursor
|
I grabbed test files from https://github.com/c2pa-org/public-testfiles. Some testing feedback:
|
… media screens - Add attachment_fields_to_edit filter (add_attachment_fields) to show a read-only 'Content Credentials' field in the media modal and on the upload.php?item=<id> Attachment Details screen. - Add add_meta_boxes_attachment action (add_attachment_meta_box + render_attachment_meta_box) to show a 'Content Credentials' meta box in the side column of post.php?post=<id>&action=edit (Edit Media screen). - Extract shared get_status_html() helper used by render_media_column(), add_attachment_fields(), and render_attachment_meta_box(). - Pass the attachment URL as a ?source= query param on the CAI verify link so the verify tool can pre-load the image for one-click cryptographic verification (best-effort; works for publicly reachable sites). - Add tests: add_attachment_fields (enabled/disabled, all states), add_attachment_meta_box (registration enabled/disabled), render_attachment_meta_box (output states), verify link source param. - Update docs/experiments/c2pa-monitor.md to document all three admin surfaces and the updated verify link behaviour. Co-authored-by: Cursor <cursoragent@cursor.com>
3a. Looks like on the "Edit Media" screen that the CC info is separate from the metabox set up for that info and the "No credentials" and "✓ Credentials" text don't include the on-hover helper text.
3b. On the "Attachment details" screen the on-hover text shows but gets overlapped by some element and isn't fully rendered
|
…link Verify link / source param: - Revert the ?source= pre-fill. WordPress attachment URLs are not reachable by the CAI tool fetcher from outside the admin session (local, Playground, staging, auth-gated sites all fail silently). The link now points at the bare verify.contentauthenticity.org URL and help text tells the user to download and drag the file in. In-browser verification via @contentauth/sdk is deferred as a follow-up PR. Tooltip vs visible help text: - Add bool $with_tooltip param to get_status_html() (default true). CSS tooltip kept only in the compact Media Library column; detail screens use visible text instead. - Add get_status_help_text() returning a per-state plain-English explanation. Attachment details (media modal and upload.php?item=<id>): - Set show_in_edit => false on the attachment_fields_to_edit field to suppress it on the Edit Media screen where the meta box already renders, fixing the duplicate display jeffpaul observed. - Add helps key so WordPress renders a <p class="help"> beneath the badge, unaffected by the modal overflow that was clipping the CSS tooltip. Edit Media meta box (post.php?post=<id>&action=edit): - render_attachment_meta_box() now passes false to get_status_html() and appends a <p class="description"> help paragraph. w3id.org tracking link: - Correct CONTEXT_URL @see from pull/6007 to pull/6376 (the PR that merged). - Same correction in docs/experiments/c2pa-monitor.md. Tests: - Rename test_verify_link_includes_source_param to test_verify_link_has_no_source_param and invert the assertion. - Assert data-wpai-tooltip present in render_media_column output. - Assert show_in_edit => false and helps key in add_attachment_fields tests. - Assert tooltip absent and help text non-empty on both detail surfaces. - Extend meta box test to cover all three states. Docs: - List all UI hooks under Key Hooks & Entry Points. - Drop the source pre-fill claim; add verification note and SDK follow-up. - Document show_in_edit rationale and visible help text for detail screens. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Thanks @jeffpaul, addressing all three items from your Aug 13 testing pass: 1. Column states There are three: "? Credentials", "No credentials", and "-" (dash - no scan record, e.g. uploaded before the experiment was enabled or a non-image MIME type). The labels are intentionally minimal for now. Once we validate using the C2PA JS SDK and can surface decoded claim details (claim generator, digital source type, signing authority), we'll have the context needed to make the language more specific and can refine from there. 2. Verify link / ?source= pre-fill Good catch - the The right long-term fix is in-browser verification via 3a. Edit Media - duplicate display and missing tooltip The CC info was appearing in both the main column (via The tooltip was absent because 3b. Attachment details - tooltip clipping The CSS tooltip's Corrected w3id.org link (also in this commit): the PR description updated to reflect the current state: Reply posted via the Cursor coding assistant on behalf of @lnispel. |
Co-authored-by: Cursor <cursoragent@cursor.com>
- Fix extract_jpeg_app11_jumbf_slice(): read 8-byte CI/En/Z prefix, parse Z as uint32 BE, dispatch on Z===1 (first segment, slice +8) vs Z>1 (continuation, slice +16), strict jumb/jumd/C2PA UUID validation on first segment, correct length guards. - Fix sort_by_c2pa_column(): replace bare EXISTS meta_query clause with relation=>OR + EXISTS (named) + NOT EXISTS so WP uses a LEFT JOIN; unscanned attachments now appear at the bottom of DESC sorts rather than being silently dropped. - Rewrite all four JPEG fixture writers in Fixtures.php to the spec-correct APP11 layout (pack N for Z, real LBox/TBox/jumd box carrying C2PA UUID). Upgrade write_jpeg_with_jumbf_non_c2pa() to emit a genuine jumb box with a non-C2PA type UUID. - Vendor XCA.jpg and A.jpg from c2pa-node (MIT, Adobe) into tests/fixtures/c2pa/; add README.md with license notice and attribution in CREDITS.md. - Add byte-exact tests: LBox arithmetic, seam regression, pinned sha256 (XCA.jpg), clean negative (A.jpg), continuation-too-short guard. Repair seven existing synthetic JPEG tests for new fixtures. Fix test_sort_includes_unscanned_attachments() to use OR relation. - Remove 7 redundant is_enabled() guards from register()-hooked callbacks; drop disabled-branch test halves. - Rename print_column_styles() to print_admin_styles(), hook to admin_head-upload.php and admin_head-post.php; add scoped .compat-field-wpai_c2pa CSS for label alignment. - Accept dkotter's description fix: raw manifest -> sidecar file. File 5 tracking issues; update PR description and docs deferred section with issue links.
Replace the short ternary in the sort query with an explicit default, invert the meta box help check into an early return, and promote the C2PA box UUID to a class constant so its comparison is Yoda-compliant. Co-authored-by: Cursor <cursoragent@cursor.com>
Drop esc_url() from the hardcoded verify tool link, explain the two disabled PHPCS sniffs in Manifest_Reader and Sidecar_Writer, and repair a mojibake character in the Format_Detector sniff comment. Uninstall now clears both C2PA post meta keys and the sidecar directory. Files the plugin did not write are preserved, and the directory is only removed once it is empty. Co-authored-by: Cursor <cursoragent@cursor.com>
Review round follow-up@dkotter — I've replied inline on all 14 threads from your Aug 18 pass. Apologies for the silence: most of these were actually fixed on the 18th, but I never closed the loop on the threads themselves, so from your side the review looked ignored. Fixed in b3a3403, the push right after your review:
Fixed in 1607ed9, just pushed:
Also cleared three PHPCS failures that were blocking CI (short ternary, early exit, Yoda condition) and merged @jeffpaul — on your Aug 18 items:
3b. Fixed. The Content Credentials label and its value now render on the same row; On your separate question about public image links — yes, technically. The verify tool accepts a The better long-term answer is #954: verifying in the browser with the C2PA JS SDK needs no publicly reachable URL, and behaves identically on a laptop and a production host. It would also let us replace the bare Credentials presence badge with an actual validation result, which should make the copy question in item 1 much easier to settle. Separately, I'm revising #955. I had filed it as a CR icon badge, but the CR pin is a certification mark restricted to products on the C2PA conforming products list, so it is not ours to ship today. I'm rescoping that issue to a neutral, plugin-specific badge, with the trustmark revisited if WordPress joins the conforming products list. Replied via Cursor on behalf of @lnispel. |
Plugin Check rejects direct rmdir() calls, and the phpcs:ignore on that line named the wrong sniff code so it never applied. Initialise WP_Filesystem and use its delete() instead, degrading to leaving the empty directory in place if the filesystem cannot be initialised. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Thanks again for all the work here. I've put this through another round of review and testing and have found a few more things. In addition to what I've flagged, there's a couple other things:
- If I delete an image, the sidecar isn't deleted. I think ideally we hook into
delete_attachmentand clean that up - We show the
Content Credentialssection even if the media item isn't an image, say an audio file or video file. Should we only show that section if it's an image? - The UI in the modal still isn't great, which I see @jeffpaul flagged as well. Seems the content is vertically aligned in the container instead of top aligned, maybe?
| * @param \WP_Query $query The current query. | ||
| * @return void | ||
| */ | ||
| public function sort_by_c2pa_column( \WP_Query $query ): void { |
There was a problem hiding this comment.
This sorting still doesn't work for me. If I sort by either ASC or DESC, I never see any files that have credentials, they all just disappear
There was a problem hiding this comment.
Fixed in e18a1fc. The root cause was structural: the meta_query EXISTS + NOT EXISTS pattern with relation => OR emits an unconditional INNER JOIN for the EXISTS side, then reads ORDER BY wp_postmeta.meta_value from an arbitrary other meta row (e.g. a serialised _wp_attachment_metadata blob) for attachments that have no sort key — those strings sort above '1', so credentialed items get pushed to the back.
Replaced with a posts_clauses filter that injects a named LEFT JOIN ... ON ( post_id = ID AND meta_key = '_wpai_c2pa_present' ). One joined row per attachment, no DISTINCT, and COALESCE( meta_value + 0, -1 ) gives unscanned items a deterministic -1 so they sort last on DESC and first on ASC.
Also fixed the test that concealed the bug: the factory attachments had no other postmeta, so the ambiguous ORDER BY always hit the right value. The test now seeds _wp_attached_file and _wp_attachment_metadata on every fixture.
| * @return void | ||
| */ | ||
| public function sort_by_c2pa_column( \WP_Query $query ): void { | ||
| if ( ! is_admin() || ! $query->is_main_query() || 'wpai_c2pa' !== $query->get( 'orderby' ) ) { |
There was a problem hiding this comment.
Should likely also gate this to the attachment list screen
There was a problem hiding this comment.
Added in e18a1fc. The posts_clauses handler now checks get_current_screen()->base === 'upload' and returns the clauses unchanged on any other screen.
| * | ||
| * @return void | ||
| */ | ||
| public function print_admin_styles(): void { |
There was a problem hiding this comment.
So the media modal can load in lots of different places besides just the Media Library or Edit Media screen. This may be fine for now but might need extended in the future
There was a problem hiding this comment.
Agreed — the styles were limited to admin_head-upload.php and admin_head-post.php, so the block editor, customizer, and any third-party admin page that embeds the media frame would get no CSS. Changed in e18a1fc to admin_enqueue_scripts with wp_register_style( 'wpai-c2pa-monitor', false, array(), false ) + wp_add_inline_style(), which fires on every admin page.
| * @since x.x.x | ||
| */ | ||
| private static function delete_c2pa_sidecars(): void { | ||
| $uploads = wp_upload_dir(); |
There was a problem hiding this comment.
Might want to pass false for the second param here so it won't create new directories on what is supposed to be a cleanup routine
There was a problem hiding this comment.
Fixed in e18a1fc — the call is now wp_upload_dir( null, false ) so the cleanup path never creates directories.
| /** | ||
| * Required top-level keys for a valid record. | ||
| * | ||
| * @var string[] |
There was a problem hiding this comment.
| * @var string[] | |
| * @var list<string> |
| $writer = new Sidecar_Writer(); | ||
| $rel = $writer->write( $attachment_id, $manifest ); |
There was a problem hiding this comment.
If for some reason an image does have valid credentials but this write fails, the catch block fires and we lose the validation of the image. This may be expected but it will end up showing the image has not having valid credentials even though it did
There was a problem hiding this comment.
Fixed in e18a1fc. $c2pa is now populated from the manifest facts (including present: true) before the sidecar write is attempted. If the write throws, the failure goes into errors[] with stage: 'sidecar_write' and the detection result survives. The outer RuntimeException catch (for scanner failures) is now labelled stage: 'scan' so the two paths are distinguishable in the stored record.
Sorting bug (second attempt): Replace the meta_query EXISTS+NOT EXISTS approach with an explicit LEFT JOIN via posts_clauses. The old approach emitted an unconditioned INNER JOIN for the EXISTS side, which caused the ORDER BY to read meta_value from an arbitrary other row (e.g. a serialised _wp_attachment_metadata blob) for unscanned attachments, making credentialed items sort behind that garbage and disappear off page one. The clause injection uses COALESCE(meta_value + 0, -1) so unscanned attachments sort deterministically last. Gated to the upload screen. The test that concealed the bug (factory attachments had no other postmeta) is rewritten to seed _wp_attached_file and _wp_attachment_metadata on every fixture. Sidecar write failure erasing a detection: Populate $c2pa from the manifest before attempting the sidecar write. A write failure (disk full, permissions) now lands in errors[] and the detection result (present: true) is preserved. Delete sidecar on attachment deletion: Add delete_attachment handler + Sidecar_Writer::delete() to remove ai-c2pa/<id>.*.c2pa when an attachment is deleted. Image gating: wp_attachment_is_image() guards render_media_column, add_attachment_fields, and add_attachment_meta_box so audio/video attachments show nothing. Admin styles: Move from admin_head-upload.php / admin_head-post.php to admin_enqueue_scripts so styles load wherever the media modal opens (block editor, customizer, third-party pages). Small fixes: - wp_upload_dir() in Uninstall passes null/false to avoid creating directories during cleanup. - Record.php @var string[] -> @var list<string> per review suggestion. - Outer RuntimeException catch stage corrected from sidecar_write to scan. Co-authored-by: Cursor <cursoragent@cursor.com>
Review round 3 follow-up — e18a1fc@dkotter — replied inline on all six threads. Summary of the review-body items: 1. Sidecar not deleted when an image is deleted Added a delete_attachment hook that calls a new Sidecar_Writer::delete() method. It globs �i-c2pa/..c2pa — the literal . after the ID prevents 12..c2pa from matching 123.jpeg.c2pa. Postmeta is already removed by WordPress core. 2. Content Credentials section shown for audio/video All three entry points — 3. Modal vertical alignment still wrong The styles were hooked to �dmin_head-upload.php and �dmin_head-post.php only, so they were absent for the block editor, customizer, and any third-party page that opens the media frame. Moved to �dmin_enqueue_scripts via wp_register_style + wp_add_inline_style, which fires everywhere. The tooltip and compat-field alignment CSS are identical — the alignment issue in the screenshot was caused by the styles simply not being loaded on that screen, not by a wrong rule. Replied via Cursor on behalf of @lnispel. |
Verified the sorting fix against a real wp-env instance with realistic attachment postmeta. Confirmed the old meta_query approach ordered by CAST(wp_postmeta.meta_value AS CHAR) read from an unconditioned alias, putting unscanned attachments ahead of credentialed ones on a DESC sort - exactly the reported symptom. While verifying, found that applying the posts_clauses filter twice appends the LEFT JOIN twice, which MySQL rejects outright with "Not unique table/alias" and empties the Media Library rather than merely mis-sorting it. Guard against a duplicate alias and cover it with a regression test. Co-authored-by: Cursor <cursoragent@cursor.com>
Verified against a live install — 897e194I got a local wp-env instance running and reproduced all of this properly rather than reasoning about it, which turned up one more bug and one correction to what I said earlier. Reproduced the sorting failure. Seeded four attachments carrying realistic postmeta (
On a descending sort the unscanned items come first and the credentialed one lands third. That is exactly what you were seeing. Correction to my earlier reply. I said
reading from the unconditioned After the fix, same data:
New bug found while verifying. Applying the
which returns zero rows — an empty Media Library, worse than a mis-sort. The filter is registered once so this would not fire in normal operation, but it is one stray Also confirmed on the live install:
Posted via Cursor on behalf of @lnispel. |
… rewrite The previous commit added a wp_attachment_is_image() gate to the column, attachment fields, and meta box, and moved sorting onto posts_clauses. Both changes invalidated assumptions in the test file. wp_attachment_is() returns false unless get_attached_file() resolves, so attachments built with only a post_mime_type were no longer treated as images and every UI assertion saw empty output. Adds a create_image_attachment() helper that also seeds _wp_attached_file. The sort tests stubbed get_current_screen() with an anonymous class, but sort_by_c2pa_column() requires a real WP_Screen, so the filter silently returned the clauses untouched and the assertions compared against empty strings. Uses set_current_screen( 'upload' ) instead, and passes wp_the_query so is_main_query() holds. test_sort_includes_unscanned_attachments duplicated the production SQL inline, which meant it could not catch a regression in the code it was meant to guard. It now drives sort_by_c2pa_column() through a real WP_Query; verified by mutation that changing the COALESCE default fails the test. Co-authored-by: Cursor <cursoragent@cursor.com>




Read-only feature that detects C2PA Content Credentials in uploaded JPEG/PNG/WebP images at the
add_attachmenthook, captures the raw manifest store to a sidecar file underwp-content/uploads/ai-c2pa/, and persists a structured_wpai_monitor_recordpostmeta entry for downstream consumers.What?
Closes #421
Adds a new
C2pa_Monitorexperiment that:caBX, WebP RIFFC2PA. Hard byte caps throughout.c2pa/jumbtoken in their first 64 bytes.uploads/ai-c2pa/<attachment_id>.<format>.c2pa, hashing in flight (SHA-256). Postmeta stores the hash, length, and relative sidecar path - not the bytes themselves..htaccess(Apache deny) andindex.phphardening. nginx operators must add a deny rule manually (documented in the experiment README).try / catch ( Throwable )boundary: errors land in the record'serrors[]array and the upload itself is never blocked.Why?
C2PA Content Credentials are increasingly embedded in images uploaded to WordPress sites (camera firmware, AI image generators, editorial signing tools), but WordPress's image processing pipeline destroys them - APP11 segments don't survive GD/Imagick re-encoding. Capturing the manifest at
add_attachment, before subsize generation, is the only point in the WordPress lifecycle where the original bytes are still intact.This PR establishes that capture as read-only infrastructure: the manifest data becomes available to downstream consumers (admin UI, REST endpoints, verification tooling) without each of them having to re-parse containers.
How?
Implementation lives entirely under
includes/Experiments/C2pa_Monitor/with one entry point:C2pa_Monitor::capture_for_attachment()is hooked toadd_attachmentat priority 20, gated by MIME onimage/jpeg,image/png, andimage/webp.Format_Detectorwalks containers and returns a location descriptor (segments + total length) without reading payload bytes.Manifest_Readerconsumes that descriptor, streams the bytes viafreadinto a hash context and an in-memory buffer, and produces an immutableRaw_Manifestvalue object.Sidecar_Writerpersists the bytes, ensuring the directory and hardening files exist exactly once.Recordnormalizes the structured payload and persists it as JSON-encoded postmeta at_wpai_monitor_record.The admin UI is entirely read-only and consists of three surfaces wired in
register():manage_media_columns/manage_media_custom_column): "Content Credentials" column showing ? Credentials, No credentials, or - (not scanned). Sortable viamanage_upload_sortable_columns+pre_get_posts. Hover tooltip via inline CSS onadmin_head-upload.php.attachment_fields_to_edit): same three-state badge with a visible help text paragraph.show_in_edit => falsesuppresses it on the Edit Media screen to avoid duplicating the meta box.add_meta_boxes_attachment): "Content Credentials" side meta box with the badge and a<p class="description">help paragraph.Reviewing this PR. The diff is large but cleanly partitioned. The work was developed in five layers and the integration branch's commits are organized that way:
Raw_Manifest,Record, tests).Format_Detector, fixtures, tests).Manifest_Reader,Sidecar_Writer, tests).register()body,capture_for_attachment, end-to-end tests).Each layer is independently coherent if you'd rather review incrementally; otherwise, the integrated diff stands on its own.
Use of AI Tools
AI assistance: Yes
Tool(s): Cursor, Claude
Model(s): Claude Sonnet 4.6
Used for: Architecture and PR-segmentation planning, initial code drafts, test scaffolding. Final implementation, all design decisions, and the byte-level format work were reviewed and edited by me.
Testing Instructions
Capture pipeline
_wpai_monitor_record- it should be JSON withc2pa.present: true, a SHA-256 hash, and asidecar_path_relativevalue.wp-content/uploads/ai-c2pa/<attachment_id>.jpeg.c2paand that its bytes matchmanifest_sha256.c2pa.present: falseand no sidecar should be written..txtfile) - no postmeta should be written at all.Admin UI
wp-admin/upload.php?item=<id>for the same attachment. The same Content Credentials field should appear in the details form.wp-admin/post.php?post=<id>&action=edit. A Content Credentials meta box should appear in the sidebar with the badge and help text. The field from step 11 should not appear in the main column (it is suppressed withshow_in_edit => falseto prevent duplication).Verification
Automated coverage:
Format_Detector: magic bytes, single-segment APP11, multi-segment reassembly, interleaved APP0/APP1/APP2 around C2PA, generic JUMBF (non-C2PA) ignored, truncated input,JPEG_MAX_SEGMENTScap (positive and negative), PNG/caBX, WebP simple + extended (VP8X) + odd-length padding.Manifest_Reader: byte-exact roundtrip for JPEG/PNG/WebP, multi-segment reassembly, deterministic SHA-256,MAX_MANIFEST_BYTESrejection, missing file, empty segments, bad offsets.Sidecar_Writer: write + roundtrip, hardening files, format sanitization, overwrite, multi-attachment coexistence, custom.htaccesspreserved acrossensure_dir().Record: roundtrip, defaults on empty input, JSON-not-serialize storage format, null on corrupt JSON, null when absent.C2pa_Monitorend-to-end: JPEG/PNG/WebP present, JPEG absent, unsupported MIME, fail-open on bogus ID, truncated JPEG,duration_msrecorded,add_attachmenthook actually fires, file-deleted-on-disk produceserrors[0].stage = 'resolve_path', column enabled/disabled, column cell states (present/absent/dash/malformed), tooltip present in column, tooltip absent on detail surfaces,show_in_editfalse,helpskey populated, meta box registration, meta box renders help paragraph, verify link has nosource=query param, sort column registration and query modification.Synthetic fixtures are generated at runtime so no binary blobs land in the repo and there is no third-party fixture licensing question.
Deferred (out of scope for this PR)
c2pa.decodedclaim summary (claim generator, digital source type, action history).@contentauth/sdk, targeting 1.4.0) - would allow verification without leaving the admin and without relying on publicly reachable URLs.?source=).Screenshots or screencast
Demo video posted in thread: c2pa_monitor.mp4
Changelog Entry