=== Encypher Provenance ===
Contributors: encypherai
Tags: content authenticity, provenance, verification, digital signature, copyright
Requires at least: 6.0
Tested up to: 7.0
Requires PHP: 8.0
Stable tag: 2.5.10
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Protect your content with cryptographic proof of authorship. Invisible C2PA signatures prove who wrote it and reveal tampering.

== Description ==

**Encypher Provenance** brings C2PA (Coalition for Content Provenance and Authenticity) compliance to WordPress. Automatically embed invisible cryptographic signatures into your blog posts and pages that prove when content was created, by whom, and whether it has been modified.

= Why Content Authenticity Matters =

In an era of rapid content reuse and misinformation, proving the authenticity of your content is more important than ever. Encypher Provenance helps you:

* **Prove Original Authorship** - Cryptographic signatures tied to your organization
* **Detect Tampering** - Know if content has been modified after signing
* **Build Trust** - Show readers your content is verified and authentic
* **Protect Against Misquotes** - Track sentence-level provenance

= Key Features =

* **Flexible Activation** - Start with a free site workspace or connect an existing Encypher account
* **Auto-Sign on Publish** - Content is automatically signed when you publish
* **Auto-Sign on Update** - Re-signs with edit history when content changes
* **Invisible Embeddings** - C2PA manifests are embedded using invisible Unicode characters
* **Public Verification** - Readers can verify content authenticity with one click
* **Gutenberg Integration** - Full sidebar panel in the block editor
* **Verification Badge** - Optional badge shows readers that content is signed and verifiable
* **Rights & Licensing** - Attach a license to each post (per block or per media asset on Enterprise) from a server-managed catalog; terms publish for crawlers and AI platforms to read

= C2PA Compliance =

Built on the same standards used by Google, BBC, OpenAI, Adobe, and Microsoft:

* C2PA 2.4 action model with C2PA 2.x text manifest embedding
* Unicode variation selector embedding (invisible, copy-safe)
* SHA-256 hard binding for tamper detection
* Provenance chain tracking for edit history

= One plugin, server-owned capabilities =

Every site installs the same plugin. WordPress contains no local license switch.
Encypher resolves commercial access from the workspace plan, staff-managed
overrides, and active add-ons, then sends a versioned capability projection to
the plugin.

**Included for every account**
* Auto-sign on publish/update
* Signing for normal publishing use, subject to fair-use guardrails
* Standard C2PA signing and reader verification
* Attribution indexing
* Batch signing of up to 10 documents
* Encypher-managed certificates
* Local hash signing for supported media, enabled by default

**Server-entitled capabilities**
* Reader branding controls and full manifest details
* Word-level segmentation, dual binding, and fingerprints
* Batch signing of up to 100 documents
* BYOK, multisite attachment, and advanced analytics
* Per-block and per-media rights
* Rich-media watermarks and signing-failure webhooks

Enterprise plans include these capabilities. Encypher staff can also grant a
known capability through an organization override or add-on. A stored plan,
feature flag, add-on ID, or Enterprise setting in WordPress grants nothing.

= Rights & Licensing =

Attach machine-readable usage rights to your content. In the block editor, pick a **Document License** from a searchable dropdown fed by your Encypher account: builtin licenses (Creative Commons variants, All Rights Reserved, News Wire, No AI Use, Inference Only, Full AI License, and more) plus any custom licenses your organization defines. Accounts with the server's rights capability can override the license per block or per media asset from the block inspector.

The plugin sends only your license choice; Encypher resolves the full terms server-side at sign time, so licenses stay consistent and never drift, and new custom licenses appear automatically. Published terms are readable by anyone at the public rights URL (and as an ODRL policy), which is how AI platforms and crawlers learn whether they may use your content before they ingest it.

== Ad Layouts (optional) ==

**Ad Layouts is available to organizations in the guided pilot.** Once access is granted, find it at **Encypher > Ad Layouts**. The local mode is off by default and independent of signing. Turning it on does not change how content is signed, and turning it off removes every container and withdraws every registered placement.

Ad Layouts does two things: it reserves structured ad containers on your pages, and it registers those containers as enumerable placements against your AdCP managed-seller enrollment. That is all it does.


= The four layout presets =

Each preset has a fixed `layout_key` and a fixed AdCP creative format, and activating it registers exactly one placement.

* **Leaderboard (top of page)** - `leaderboard_top`, format `display_728x90`. Rendered on the `wp_body_open` theme hook.
* **In content (between paragraphs)** - `in_content`, format `display_300x250`. Inserted after every N paragraphs of a single post or page, where N is a setting (1 to 20, default 3). Never inserted after the last paragraph, and never in feeds, archives, excerpts, or secondary queries.
* **Sidebar rail (widget)** - `sidebar_rail`, format `display_300x600`. Provided as the "Encypher Ad Slot (sidebar rail)" widget; drop it into any widget area.
* **Sticky footer** - `sticky_footer`, format `display_320x50`. Rendered on `wp_footer`.

There is also an **Encypher Ad Slot** block for placing a container by hand anywhere in the editor. The block is server rendered and ships no front-end script; its **Layout preset** control picks which of the four `layout_key` values the container belongs to.

= Placement keys =

Every slot has one placement key that is stable for this site and that slot: `wp-{first 12 hex of sha256 of your site URL}-{slot}`, for example `wp-1648707b9f8d-in-content`. The key always matches the AdCP placement-key format `^[a-z0-9][a-z0-9-]{1,62}$`.

Because the site URL is hashed into the key, two sites in a multisite network never collide. Because the key is derived rather than generated, deactivating and reactivating a slot reuses the same key: deactivation soft-removes the placement, reactivation restores it, and reporting stays continuous.

Each rendered container is fenced and self-describing:

`<div class="encypher-ad-slot encypher-ad-slot--in-content" data-encypher-placement="wp-1648707b9f8d-in-content" data-format="display_300x250" data-encypher-layout="in_content" data-encypher-state="reserved"></div>`

In the `reserved` state the container is empty and collapsed, so readers see nothing. A container can be filled later without any markup change.

Placements are registered in the background. If this site is not enrolled yet, or the Encypher API does not offer the enrollment feature, the mode says so, registers nothing, retries later, and keeps rendering containers locally. Nothing about your pages depends on the registration succeeding.

= Pasted ad tags: the trust model =

You may paste your own ad-server tag (Google Ad Manager / GPT, or any other snippet) into a slot. If you do, that tag is stored for that slot and emitted **verbatim, inside that slot's container only**. It is not filtered, escaped, or rewritten, because filtering it would break it.

**This is the same trust class as WordPress core's own Custom HTML widget.** Anything you paste runs on your site with the privileges of a page script. Treat an ad tag exactly as you would treat markup in a Custom HTML widget, and paste only tags from an ad server you control.

The plugin enforces that boundary the way core does:

* Reaching the settings page requires the `manage_options` capability.
* Saving the page requires the WordPress settings nonce for the Ad Layouts option group.
* **Changing an ad tag additionally requires the `unfiltered_html` capability.** On a single site that means administrators; on multisite it means super admins only, which is core's Custom HTML boundary. A user without it can still activate and deactivate slots, but their submitted tag is discarded, the stored tag is left exactly as it was, and the page tells them why.
* A tag is capped at 20,000 bytes and stripped of null bytes. Nothing else about it is altered.

Encypher never adds a tag of its own, and an empty slot emits nothing at all.

== AdCP Seller Network (optional) ==

If your Encypher organization has the managed seller network enabled, an **Encypher > Seller Network** screen appears. It enrolls this property as managed-seller inventory whose evidence trail is your own signed content. The screen is hidden entirely when the feature is not enabled for your organization: the plugin treats a 404 from the enrollment API as "feature absent" and shows nothing at all.

Enrollment never exposes your inventory to a buyer. Exposure requires a signed Encypher contract and an explicit grant, both of which happen outside WordPress.

= Verifying your domain in one click =

Encypher must confirm you control this exact host before it will enroll it. Selecting **Verify this domain** does three things:

1. Registers this host with Encypher using the organization credential already stored by the connect flow, and receives a one-time challenge token.
2. Serves that token from WordPress at `https://your-domain/.well-known/encypher.json` as `{"encypher_domain_verification":"<token>"}`. This is a WordPress route, not a file: no DNS record, no FTP, and no web-root access are required, so it also works on managed hosts where you only have wp-admin.
3. Asks Encypher to fetch that URL over HTTPS and confirm the token.

The token stops being served the moment verification succeeds. On a multisite network the document is served only by the site that owns the challenge, so one site can never prove another site's domain.

If another Encypher organization already holds an active verification for your domain, verification stops with a takeover-protection message. You cannot resolve that from WordPress: contact support@encypher.com from an address at that domain.

= Manual test: the rewrite-rule verification flow =

To confirm the challenge route works on a given host before enrolling:

1. Activate the plugin and connect the site (**Encypher > Setup and Health**). Activation registers the `.well-known/encypher.json` rewrite rule and flushes rewrite rules once; deactivation removes it and flushes again.
2. Open **Encypher > Seller Network**. If the screen is absent, the enrollment feature is not enabled for your organization (or the background capability probe has not run yet; it is scheduled about 30 seconds after the first admin page load).
3. Select **Verify this domain**.
4. From outside WordPress, fetch the challenge URL and confirm the response, for example:
   `curl -si https://your-domain/.well-known/encypher.json`
   While a challenge is live this returns `200` with `content-type: application/json` and a body of `{"encypher_domain_verification":"<token>"}`. With no challenge live it returns `404` and `{"error":"no_active_challenge"}`. It never returns a `200` with an empty proof.
5. Confirm the screen now reports the domain as verified and that the challenge URL returns `404` again, since the token is spent.
6. If step 4 returns your theme's 404 page instead of JSON, the request never reached WordPress: check that the host does not intercept `/.well-known/` paths, then re-save **Settings > Permalinks** to flush rewrite rules and retry. The plugin also matches the path directly, so verification succeeds even when rewrite rules are stale, but not when the web server never hands the request to WordPress.

= Enrolling, withdrawing, and declaring a sales house =

Once the domain is verified, the screen renders the current enrollment terms document, shows its SHA-256, and records that exact hash with your acceptance (context `wordpress`). The terms are re-fetched and re-hashed at the moment you accept: if they changed while the page was open, nothing is accepted and you are asked to review the new text. Enrollment is refused outright if the hash cannot be verified.

The status card reports enrollment state, how many signed publications back this property, how many placements are registered, and the declared sales house. **Withdraw** takes effect immediately, with no grace period. A sales-house declaration (registry key, display name, optional seat or network ID) authorizes fulfillment routing only: a sales house can never change your enrollment, your offers, or your content, and can be revoked from the same screen.

On multisite, enrollment is per site. The network batch-attach propagates the connection, not the enrollment: each site verifies and enrolls its own domain, and identifiers inherited from another site are ignored and cleared.

Every request this screen makes goes through the plugin's SSRF guard, requires `manage_options`, and carries a WordPress nonce.

== Installation ==

1. Upload the `encypher-provenance` folder to `/wp-content/plugins/` (or install the ZIP via **Plugins > Add New > Upload Plugin**)
2. Activate the plugin through the 'Plugins' menu in WordPress
3. Go to **Encypher > Setup and Health**
4. To start without an account, accept the terms and select **Start without an account**. To use an existing workspace, select **Connect an existing Encypher account** and approve the site.
5. Publish a post — it is signed automatically and a verification badge appears.

Already have an Encypher account? Use **Connect an existing Encypher account** (dashboard OAuth) or paste an existing **API key** from the same screen to attach this site to your existing workspace.

== Frequently Asked Questions ==

= Do I need an API key? =

No. Select **Start without an account** and accept the terms. The plugin provisions a free site workspace and stores its site-scoped credential. If you already use Encypher, connect your account or paste an existing API key instead.

= How do I connect an existing Encypher account? =

From **Encypher > Setup and Health**, choose **Connect an existing Encypher account** (log in and approve through the dashboard) or **Connect with API key** (paste a key that starts with `ency_`). Both attach this site to your existing workspace.

= Are the embeddings visible? =

No. C2PA manifests are embedded using invisible Unicode variation selectors. They don't affect how your content looks but can be extracted for verification.

= Does this work with the Classic Editor? =

Yes, the plugin supports both Gutenberg (block editor) and Classic Editor.

= What happens if I edit a signed post? =

The plugin automatically re-signs the content with a `c2pa.edited` action and maintains a provenance chain linking to the previous version.

= Can readers verify my content? =

Yes. A verification badge can be displayed on your posts. Clicking it tells readers who published the content, whether the current version matches the signed version, and whether it changed after signing.

= Is my content sent to external servers? =

Yes, content is sent to Encypher's API for signing. The API creates cryptographic signatures but does not store your full content. **Local media hashing** is available to every connected account and enabled by default. Supported media can be signed without uploading the full file: the plugin sends only structural bytes and a locally computed hash, then writes the returned signature into the file on your server. The **Full media uploads** setting controls the fallback for assets that cannot use hash mode. Leave it on to upload the full file for signing, or turn it off to keep every media payload on your server. See our [Privacy Policy](https://encypher.com/privacy) for details.

== Screenshots ==

1. Settings page with workspace connection and signing defaults
2. Gutenberg sidebar showing signing status
3. Frontend verification badge
4. Verification modal with content details
5. Setup & Health cockpit with content coverage

== Changelog ==

= 2.5.10 =
* Changed: protected publishing remains on after Encypher connects, while plugin activation alone no longer holds publication before a credential exists.
* Changed: local hash signing now defaults on for every account and remains enforced by the server-owned capability projection.
* Confirmed: clickable provenance info marks on media remain enabled by default.

= 2.5.9 =
* Changed: one plugin now reads a versioned, server-owned WordPress capability projection resolved from the Encypher plan, staff-managed organization overrides, and active add-ons.
* Security: local tier strings, feature flags, add-on IDs, and saved Enterprise settings cannot enable paid WordPress behavior. Invalid, stale, or organization-mismatched projections fail closed.
* Changed: local hash signing now defaults off and can be enabled only when Encypher grants the account capability.

= 2.5.8 =
* Added: fail-closed protected publishing is on by default. New content stays non-public until its exact signed generation commits, and updates keep the prior signed generation live until replacement signing succeeds.
* Added: publication receipts, registry delivery state, and signed-generation history in WordPress, including exact-generation retry and cancellation controls.
* Added: an Encypher pre-publish summary, persistent post-publish outcomes, public editorial metadata and source/AI declarations, and canonical status across the editor, lists, coverage, and recovery surfaces.
* Fixed: rich Gutenberg posts with images embed and verify completely; edited generations preserve their ancestry and cannot report success for stale or partially written artifacts.
* Fixed: reader verdicts, media trust labels, verification counters, queue recovery, and media-scan arithmetic now fail closed without contradictory success states.
* Changed: reader and attachment details show signer, trust reason, actions, ingredients, rights, and generation history in plain language, with technical JSON kept as an optional disclosure.

= 2.5.7 =
* Fixed: prepared local media signing now binds opened and edited actions to the source asset, matching multipart signing and producing valid C2PA action history.

= 2.5.6 =
* Changed: WordPress Ad Layouts now requires an Encypher staff-managed organization entitlement. Customers see pilot access status in the dashboard instead of an entitlement toggle, then opt in to placements from WordPress after access is granted.
* Security: missing, denied, or unreachable entitlement status hides every Ad Layout surface. The retired customer-managed account flag no longer grants access.

= 2.5.5 =
* Added: Ad Layouts can be enabled for a WordPress account from the Encypher dashboard. The plugin fails closed and hides every Ad Layout surface until the account setting is enabled.
* Added: one-click signed demo article with a hero image, source-note audio, short video, and in-content Ad Layout slot, all queued through the normal production signing path.
* Changed: local media hashing, image signing, and reader media badges are enabled by default on new installs. Unsupported hash-mode media falls back to full-file signing only when Full media uploads remains enabled.
* Fixed: Setup and Health no longer reports background signing as disabled merely because DISABLE_WP_CRON is set; it reports an issue only when queued work is actually stalled.

= 2.5.4 =
* Rights inventory identity: every production WordPress sign carries a stable install/post logical asset, a persisted generation operation key, and canonical WordPress source metadata so retries remain idempotent and intentional re-signs become distinct inventory generations.
* WordPress remains a thin publisher client: rights policy, inventory browsing, deal acceptance, verification policy, and clearance receipt issuance stay in the Encypher dashboard and API.

= 2.5.3 =
* Hash-mode media signing: plan, prepare, and finalize media provenance through hash-mode capabilities without uploading originals (planner, writer, block extractor, media library UI).

= 2.5.2 =
* Added: optional AdCP Seller Network enrollment (Encypher > Seller Network, hidden unless enabled for your organization). Verify your domain in one click with a challenge document served by WordPress itself, accept the enrollment terms, enroll this property as managed-seller inventory backed by your signed content, declare or revoke a sales house, and withdraw at any time. Enrollment never exposes inventory to a buyer without a contract and an explicit grant.
* Added: optional "Ad Layouts" mode (Encypher > Ad Layouts, off by default). Reserve leaderboard, in-content, sidebar-rail, and sticky-footer ad containers, register them as enumerable AdCP placements, and optionally paste your own ad-server tag per slot. Pasted tags follow the same trust model as WordPress's Custom HTML widget.

= 2.5.1 =
* Added: direct installs now use WordPress core's Update URI API to discover Encypher releases. WordPress and the site administrator remain in control of manual and automatic installation.
* Added: a managed-host package excludes the Encypher update provider and disables Update URI for WordPress VIP and other Git-managed environments. The WordPress.org package also excludes the provider and leaves updates to WordPress.org.
* Fixed: the release manifest now keeps its package URL on the same apex trust origin pinned by v2.5.0, restoring fail-closed updates for existing direct installs.

= 2.5.0 =
* Added: a single Setup and Health cockpit replaces the multi-page control plane. Connect this site once, then publish normally.
* Added: secure accountless activation through ProvisionV2. "Start without an account" verifies control of this exact site over HTTPS and stores a sealed, site-scoped credential, so no Encypher account or admin email is required.
* Changed: settings are reduced to seven WordPress-local behaviors. Organization policy such as rights, tiers, and reporting is managed from the Encypher dashboard.
* Fixed: saving settings no longer drops a claimed workspace or install identity. The merge-based sanitizer preserves stored account, site, and claim state on every save (a 2.4.4 settings save could previously revert a claimed workspace).
* Removed: the email magic-link connect flow. Connect an existing account through dashboard OAuth, or paste an API key.
* Added: the Content coverage card reports local protected, needs attention, and not yet signed counts, plus a public verification count for the last 30 days.
* Added: contextual links to the Encypher dashboard for analytics, license templates, and organization management.
* Added: tier-neutral reader badges, Classic Editor parity, and a native post list provenance column, filter, and bulk sign action.
* Added: retroactive media library scan. Scan existing uploads for embedded C2PA provenance from Setup and Health; detection is local only, batched in the background, and free. Offloaded or missing files are skipped and counted.
* Added: a Media coverage line and a Media Library provenance filter (carries provenance, no provenance, not yet scanned) that share predicates so the count and the filter always agree.
* Added: richer attachment provenance details (signer, format, detected date, validity, and an Inspect in Encypher dashboard link), and a distinct "Signed by you" state for assets you sign versus "Carries provenance" for third-party C2PA.
* Added: standalone media signing. A "Sign with Encypher" row action and native bulk action on the Media Library sign the original file and record the result on the attachment.
* Added: a per-asset license selector on attachment details, drawn from your Encypher account catalog, with per-block, then per-asset, then document, then site rights precedence at signing time.
* Added: editor image blocks note when their media already carries third-party provenance or was signed with Encypher, and the composite sign panel lists blocks whose media carries third-party provenance.
* Added: a verification sparkline on the coverage card and a media pipeline Site Health report (scan status, sign errors, offloaded skips).
* Added: optional reader-facing teardrop markers on signed images (off by default). When enabled, a small teardrop appears on unmodified signed-original images and opens the same verification dialog; a resized or transformed rendition is never marked verified. It is a pure client-side overlay, so the signed page markup is never changed.
* Added: enterprise whitelabeling of the reader surfaces. Set a brand logo (PNG, JPG, or WebP) to replace the Encypher mark on the badge and verification dialog, and optionally hide the "Powered by Encypher" link. Available on Enterprise plans; the logo and link are enforced at save, at render, and are never writable by non-entitled tiers.
* Added: the Content coverage card highlights your most verified content and counts external referrers (referring domain only), so you can see which posts readers verify and where those verifications come from.
* Added: an AI crawler call to action on the coverage card that links to CDN analytics when external verification traffic looks like automated crawlers.
* Added: a background queue stall detector. When WP-Cron is not firing on a quiet site, the cockpit's overall status and the media pipeline Site Health report explain the cause and system-cron remedy, and a "Process queue now" button drains a bounded batch of signing and media scan work immediately.
* Added: activation funnel telemetry on the periodic status sync, recording connection, first signed content, and workspace claim milestones so onboarding health is visible in your workspace.
* Added: a deactivation exit survey on the plugins screen with four one-click reasons and an optional note. Answering is optional and deactivation always proceeds immediately whether you answer, skip, or dismiss.
* Added: a suggested privacy policy section (Tools, Privacy) that lists what leaves your site and what stays local.
* Changed: verification counters are updated atomically and stored per day, so concurrent verifications never lose a count.
* Changed: reader verification icons match each image to its attachment by the WordPress image id first, hardening overlay placement on themes that rewrite image markup.
* Changed: saving per asset rights now requires the edit_others_posts capability, so only editors and above can change licensing on content they do not own.
* Changed: loading and signing indicators now use the canonical Encypher brand loader and mark from the company icon library.
* Changed: Setup and Health leads with one protection state, local value signals, grouped recovery actions, and responsive disclosures instead of repeating status across dense cards.
* Changed: the reader verification dialog uses plain language, hides raw manifest data, distinguishes trusted publishers from unverified claims, and offers an in-dialog retry when verification is unavailable.
* Security: attachment provenance fields are read-only through the media REST API, featured-image signing enforces attachment edit rights, and anonymous verification rejects non-public posts.
* Security: every API redirect target must pass the public HTTPS guard, cross-origin redirects lose credentials, and integrity-valid untrusted claims are labeled as unverified rather than attributed to Encypher.

= 2.4.4 =
* Added: one-click "Activate free protection" creates a free, site-scoped workspace after verifying control of this exact site over HTTPS. No Encypher account or admin email is required to start signing.
* Security: the site credential is delivered encrypted (libsodium sealed box) to a one-time key generated on your server and is never exposed in transit or logs. The retired email/domain auto-provision path is fully removed.
* Changed: an already-registered site now routes to authenticated sign-in instead of issuing a new key, and existing connected sites keep working unchanged.

= 2.4.3 =
* Changed: full WordPress VIP coding-standards compliance (all output escaped, prepared SQL, gated debug logging) with the ruleset enforced in CI
* Changed: the plugin package now ships only runtime files; badge verification results are cached and retried once before reporting failure
* Changed: minimum PHP version is now 8.0

= 2.4.2 =
* Added: the Document License picker is now a searchable dropdown loaded from your Encypher account - the standard templates plus any custom licenses your organization defines, available on every connected site without a plugin update
* Changed: license terms are resolved on Encypher's servers at signing time; the plugin no longer bundles license definitions, and existing per-post selections keep working unchanged

= 2.4.1 =
* Fixed: per-post rights templates (e.g. "No AI Use") now reach the signed manifest - previously only a site-level syndication flag survived and the AI training/inference/data-mining prohibitions were silently dropped from the signing request
* Changed: rights chosen per post or per block now also appear in Encypher's public rights registry, ODRL projection, and verify-time clearance, so AI crawlers see the exact terms your editors set

= 2.4.0 =
* Fixed: composite (media + text) signing works again for posts with clean media - a detection bug flagged every uploaded image, audio, and video file as already carrying C2PA provenance, which made all composite signing requests fail; media in your posts is now C2PA-signed as created works
* Added: the plugin package now includes the GPLv2 LICENSE file matching the license declared here
* Developer: new ENCYPHER_ALLOW_INSECURE_API_BASE wp-config constant for local development against a self-hosted Encypher API; has no effect unless explicitly defined

= 2.3.1 =
* Added: "Content source type" setting - declare how your content is made using the official IPTC vocabulary (e.g. "Digital creation" for human-authored newsroom content). Declaring a source type gives your manifests the stronger c2pa.created claim; when left unspecified, Encypher never guesses and records the truthful c2pa.opened under C2PA 2.4. AI-assisted content detected by the WordPress AI integration is always labeled as created using generative AI
* Changed: edit chains now record the prior version as a parentOf ingredient per C2PA 2.4, so third-party validators walk your provenance history correctly

= 2.3.0 =
* Added: rich re-signs now preserve the full provenance chain - previous version reference, C2PA action, document type, and IPTC digital source type are carried through composite (media + text) signing, including background bulk runs
* Added: unknown account tiers now keep your last known tier and show a dismissible admin notice instead of silently downgrading features to free
* Added: the local edit log now mirrors to Encypher's tamper-evident transparency log (org-scoped, idempotent); a compromised site database can no longer rewrite provenance history silently
* Removed: in-plugin billing display (payment method badge and add-on checkout) - these calls never worked and all billing management lives in the Encypher dashboard, which the plugin links to
* Fixed: the organization name shown in settings was always empty because the plugin read a field the API does not send
* Fixed: editing a post now completes signing reliably on every hosting stack (Apache mod_php included) - signing is offloaded to WP-Cron instead of a PHP shutdown hook that some hosts killed mid-run
* Security: hardened media signing authorization, self-hosted update package verification (SHA-256, fail-closed), SSRF protection on every outbound API call, rate limiting on public verification endpoints, and crash-safe signing write ordering

= 2.2.0 =
* Added: the plugin now shares your site's admin email with Encypher (on connect and on status sync) so we can notify you about security fixes and plugin updates. This is account contact metadata only; it does not change your account or how signing works.

= 2.1.0 =
* Added: automatic update notifications - the plugin now checks encypher.com for new releases and surfaces them in Dashboard > Updates with a native one-click "Update now" and a "View details" changelog, so direct-download installs no longer miss updates

= 2.0.3 =
* Fixed: verifying unsigned content no longer claims "Manifest found" - the badge modal now reports "No C2PA provenance markers were found" with sign-first guidance, and unsigned posts are no longer stamped with a verification_failed status
* Added: server-side background bulk signing - a WP-Cron queue processes archive runs without an open browser tab (crash-safe, resumable, pause/cancel); the in-tab mode remains available via the new "Run in background" toggle
* Added: Site Health check "Encypher provenance integrity" - fetches your latest signed post and verifies the served page against the API, detecting themes/plugins/output buffers that alter signed text
* Changed: media file writes go through the WordPress Filesystem API with read-back verification (WordPress VIP and managed-host compatible); direct writes only as a guarded fallback on plain hosts
* Changed: signed-text injection and the verification badge now render in a single final-priority content filter, so no third-party content filter can alter signed bytes after injection
* Fixed: signing failure records from admin and bulk flows now use the same canonical error shape as the editor flow

= 2.0.2 =
* Fixed: live-page byte integrity - signed text is now injected after wptexturize/wpautop (priority 12) so the served bytes are exactly the signed bytes; previously texturize rewrote apostrophes/quotes after injection, making DOM-level verifiers (Chrome extension) report "Invalid Signature" on micro-embedded content
* Fixed: the public provenance report no longer shows a false "Not Verified" for freshly signed content - it now performs a live verification when the cache is empty, and signed-but-unverifiable content gets a neutral "Signature Present" verdict instead of a negative one
* Fixed: provenance report readability - removed the partial dark-mode theme that painted dark-mode text colors over light backgrounds (unreadable in dark-mode browsers); the report now always renders its light branded design with higher-contrast verdict text
* Fixed: WordPress 6.9 compatibility for the Abilities API - encypher/sign and encypher/verify now register with the core signature (args array with execute/permission callbacks, input/output schemas, and a registered "encypher" ability category) and only on the wp_abilities_api_init action, eliminating the "called incorrectly" notices that broke wp-admin headers under WP_DEBUG
* Fixed: editing or reordering blocks after signing no longer risks serving the pre-edit signed snapshot on the public page - render-time marker injection now checks the provenance status and falls back to the live clean content until the post is re-signed
* Fixed: compound posts with audio, video, or media-text blocks no longer fail text embedding - visible captions and body copy from those blocks are now included in the signed text, so all HTML fragments match during embedding (previously up to 3 fragments per post could mismatch and trip the fail-closed gate)
* Fixed: the public /c2pa-verify/ report page no longer shows "Not Found" for valid content - the URL-encoded instance id is now decoded before lookup
* Fixed: signed media-rich posts now pass reader-side C2PA verification - the rich signing request forwards the embedded-manifest options (embed_c2pa) so compound posts carry the same verifiable text artifact as text-only posts
* Changed: the signed marker-bearing HTML is stored alongside the clean editor content for rich posts, keeping the editor copy clean while the published page carries verifiable markers

= 2.0.1 =
* Changed: all provenance state icons (editor status hero, header chip, settings connection status, media library indicators, verification report verdicts, AI provenance badge) now use versions of the Encypher seal logo - checkmark for signed/verified, question glyph for pending/unknown, X glyph for tampered/failed - instead of generic dashicons, emoji, and text characters
* Changed: signing and verifying animations now use the branded Encypher seal loading spinner and stay visible for a short minimum time so the progress is perceivable even on fast operations
* Removed: "Encypher powers this provenance workflow" banner from the Encypher Provenance editor sidebar panel
* Fixed: posts are no longer reported as signed when the C2PA text embedding step fails - the post now shows a "Signing failed" state (new embedding_failed status) instead of a false "Signed" badge
* Fixed: a crashed or timed-out signing run no longer leaves the editor stuck on "Signing" forever - the in-progress flag is now timestamped and automatically expires after 5 minutes
* Fixed: signing failures (invalid key, quota, API down) now surface live in the editor sidebar with the failure reason instead of only appearing as an admin notice after reload
* Fixed: coalition auto-enrollment now calls the correct API endpoint (/coalition/opt-in) - previously every enrollment attempt returned 404
* Fixed: email connect polling now detects expired secure links and stops after 15 minutes with a "Send a new link" option instead of polling forever
* Fixed: bulk signing progress is saved locally and can be resumed after a browser crash or page reload; transient network errors retry with backoff (honoring Retry-After) instead of cancelling the whole run
* Added: monthly signing quota is checked before signing starts, so free-tier publishers over the cap get an immediate, clear error instead of a silent failure
* Changed: free tier signing is no longer capped at 1,000 documents/month - signing is now unlimited for normal publishing use (fair-use guardrails apply)

= 1.3.1 =
* Fixed: connection health check no longer fails when Cloudflare or CDN returns 403 on probe endpoints - now falls through to next probe instead of aborting
* Fixed: invalid API key error now shows actionable guidance ("starts with ency_") instead of cryptic "Tier lookup failed with status 403"
* Fixed: email connect error now includes HTTP status code and backend detail instead of generic "Unable to start WordPress connect flow"
* Added: client-side API key format validation catches non-Encypher keys (ED25519, random strings) before server round-trip

= 1.3.0 =
* Fixed: CDN signing endpoints no longer return 500 errors (broken import)
* Fixed: quick-connect no longer saves invalid API keys silently

= 1.2.0 =
* Added WordPress/ai integration: auto-signs AI-generated content from all five WordPress/ai experiments (Title Generation, Excerpt Generation, Summarization, Review Notes, Alt Text) before it is committed to the post
* Added WordPress Abilities API support: registers `encypher/sign` and `encypher/verify` as first-class abilities callable by any plugin via `wp_get_ability()->execute()`
* Added "AI Content Provenance" Gutenberg sidebar panel with shield badge (green/yellow/red/grey) showing per-post AI provenance status and signed experiment list
* Added `GET encypher-provenance/v1/wordpress-ai-status` REST endpoint powering the new sidebar panel
* Added Coalition auto-enrollment: enabling the toggle in settings automatically enrolls the site in the Encypher Coalition via `/coalition/opt-in`
* Added WordPress/ai Integration settings section with enable toggle and Coalition auto-enroll toggle
* AI-signed experiment records are stored in post meta (`_encypher_wpai_experiments`) for audit trail and REST lookups

= 1.1.0 =
* Added email-based secure connect flow with automatic API key provisioning
* Added WordPress approval page flow for emailed connect links
* Added session polling and automatic connection completion in plugin settings
* Updated dashboard and plugin documentation for guided WordPress onboarding

= 1.0.0-beta =
* Public beta release for Encypher Provenance
* Settings UI streamlined: hard binding is always on and no longer configurable
* Added dashboard support contact section with direct email CTA
* Unified branded full-wordmark headers across Dashboard, Content, Settings, Analytics, Account, Bulk Sign, and Coalition pages
* Improved analytics cards/status presentation and coalition early-rollout placeholder experience

== Upgrade Notice ==

= 2.3.0 =
Provenance chains now survive edits on media-rich posts, signing completes reliably on all hosting stacks, and a security review hardened update verification and media authorization. Recommended for all users.

= 1.2.0 =
Adds WordPress/ai integration, WordPress Abilities API support, and Coalition auto-enrollment. No breaking changes. If you use the WordPress/ai plugin, enable the new integration toggle in Encypher > Settings to auto-sign AI-generated content.

= 1.1.0 =
This release adds secure email-based connection and automatic API key provisioning for WordPress installs while preserving manual API key setup for existing workspaces.

= 1.0.0-beta =
Public beta with polished admin UX and production-ready signing/verification defaults. Hard binding is enforced by default.

== Privacy Policy ==

Encypher Provenance sends your post content to Encypher's API (api.encypher.com) for cryptographic signing. We do not store your full content - only metadata needed for verification. See [encypher.com/privacy](https://encypher.com/privacy) for our complete privacy policy.
