=== Tessera for the Abilities API === Contributors: ibrahimhajjaj Tags: abilities-api, mcp, audit, rollback, safety Requires at least: 6.9 Tested up to: 6.9 Requires PHP: 8.1 Stable tag: 1.3.5 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Snapshot, audit, and rollback layer for plugins that register abilities via the WordPress Abilities API. == Description == Tessera is a developer library for plugin authors who register abilities via `wp_register_ability()` and want snapshot capture, audit logging, approval workflows, and one-click rollback for every invocation across REST, MCP, internal PHP, and WP-CLI without building it themselves. Declare what state your ability touches; Tessera handles the safety wrapper. = What you get out of the box = * **Pre + post snapshots.** Every safety-enabled invocation captures declared state before the callback and (on success) after, so the audit log can show a real diff. * **Audit log.** One row per invocation with ability name, caller (REST/MCP/CLI/internal), user, args, result, status, duration, pre/post hashes, and parent_invocation_id for nested calls. * **One-click rollback.** Restore captured state from post_meta, options, taxonomy term assignments, user roles + caps. File contents support tiered drift detection (mtime / mtime_size / critical_hash / full_hash) plus opt-in real byte-level rollback via `full_content` strategy. * **Drift check on rollback.** Live state is hashed and compared to the snapshot's post-state before restoring; if they differ the rollback returns an error unless forced. * **Concurrency lock.** Capture + execute is serialised per surface set via a MySQL advisory lock so two simultaneous invocations do not capture each other's mid-states. * **Encrypted redaction.** Scrub secrets out of args, results, and snapshots. Stores redacted values as AES-256-GCM envelopes so rollback can still restore them. * **Approval queue.** When `safety.requires_approval` is set, the wrapper blocks execution and returns a 202 pending response. A human approves or rejects via wp-admin, WP-CLI, or REST. Multi-stage sequential or parallel approval chains are supported. * **Multisite support.** Each subsite gets its own set of `wp__abilityguard_*` tables, with auto-install on `wp_initialize_site` and auto-drop on `wpmu_drop_tables`. * **Retention.** Daily WP-Cron prunes old log rows (defaults: 30 days normal, 180 days destructive) and orphaned snapshots. = Surfaces = * **PHP API** with `wp_register_ability( $name, [ ..., 'safety' => [...] ] )` and helpers `abilityguard_rollback`, `abilityguard_snapshot_meta`, `abilityguard_snapshot_options`. * **REST**: `/abilityguard/v1/log`, `/log/`, `/log/export`, `/rollback/`, `/rollback/bulk`, `/approval`, `/approval//approve`, `/approval//reject`, `/approval/bulk`, `/approval/export`, `/retention`, `/retention/prune`, `/health`. * **WP-CLI**: `wp abilityguard log list/show`, `wp abilityguard rollback `, `wp abilityguard approval list/approve/reject `, `wp abilityguard prune`. * **wp-admin**: Tools > Tessera. Hybrid timeline + command-palette search, snapshot drawer, JSON-highlighted Input/Result tabs, invocation chain navigation, and real rollback against the captured snapshot. = Example = ` wp_register_ability( 'my-plugin/update-product-price', array( 'label' => 'Update product price', 'description' => 'Updates the price on a WooCommerce product.', 'category' => 'woocommerce', 'input_schema' => array( /* ... */ ), 'permission_callback' => fn() => current_user_can( 'manage_woocommerce' ), 'execute_callback' => fn( $args ) => update_post_meta( $args['product_id'], '_price', $args['price'] ), 'safety' => array( 'destructive' => true, 'requires_approval' => false, 'snapshot' => fn( $input ) => array( 'post_meta' => array( $input['product_id'] => array( '_price', '_regular_price' ) ), 'options' => array( 'woocommerce_last_price_change' ), ), ), ) ); ` = Documentation = Full plugin-author documentation lives at the GitHub repo: https://github.com/ibrahimhajjaj/abilityguard == Source Code == The full source for Tessera, including the unminified React source for the admin app, lives on GitHub: https://github.com/ibrahimhajjaj/abilityguard * The admin bundle `assets/admin.js` is compiled from `assets/admin.jsx` (React + JSX, no preprocessor magic beyond JSX). * The bundler is [esbuild](https://esbuild.github.io/), configured in `scripts/build.mjs`. * To rebuild the admin bundle from a fresh checkout, run `npm install` once, then `npm run build` whenever `assets/admin.jsx` changes. This regenerates `assets/admin.js` in place. * The release zip published to the WordPress.org directory is produced by `scripts/build-release.sh`, which excludes development artifacts (tests, examples, build configs) but keeps everything required for the plugin to run. == Installation == 1. Upload the `abilityguard-mcp` folder to `/wp-content/plugins/`. 2. Activate the plugin through the Plugins menu in WordPress (or network-activate on multisite). 3. Visit Tools > Tessera to view the audit log. 4. In your own plugin, register abilities via `wp_register_ability()` with a `safety` config. Requires WordPress 6.9 or later (for the Abilities API) and PHP 8.1 or later. == Frequently Asked Questions == = Does this work without other plugins? = It will activate without registered abilities, but it only does work when other plugins register abilities with a `safety` config via `wp_register_ability()`. = What state surfaces are supported for snapshots? = post_meta, options, taxonomy term assignments, user roles + caps, and files (with five tiered strategies from mtime to full content rollback). = Does it support multisite? = Yes. Each subsite gets its own set of `wp__abilityguard_*` tables. New subsites are auto-installed via `wp_initialize_site`; deleted subsites have their tables dropped via `wpmu_drop_tables`. = How does it handle concurrent invocations? = Per-surface MySQL advisory locks (GET_LOCK) serialise capture + execute so simultaneous invocations do not capture each other's mid-states. = Are secrets encrypted in the log? = Yes. Redaction uses AES-256-GCM envelopes so rollback can still restore the original value when the encryption key is intact. == Screenshots == 1. Invocation timeline. Every ability call across REST, MCP, internal PHP, and WP-CLI, with caller attribution and per-row status. 2. Approvals queue. Pending requests waiting on a human, with the requesting context and a one-click approve or reject. 3. Invocation detail after a one-click rollback restored the captured pre-state. 4. Search-as-you-type in the log: ability name, caller, status. 5. Invocation detail, result tab, with redacted secret values restored on display when the encryption key is present. 6. Snapshot drawer showing the captured pre-state and post-state for a destructive invocation. 7. Multi-stage approval chain with per-stage capability and role routing. == Changelog == = 1.3.5 = * Release zip no longer ships `composer/installers` and its unused installer adapters. Cuts the published zip from 909K to 824K and 206 files to 98. = 1.3.4 = * Release zip now ships `vendor/autoload.php` so the plugin actually boots on a fresh install. (1.3.3 zip was missing the autoloader and fatal'd on activation.) = 1.3.3 = * Display name changed to "Tessera for the Abilities API" to clearly distinguish this plugin from any future official safety library. Slug, text domain, and internal namespace are unchanged. * `error_log()` calls in the rate-limiter and concurrency lock are now gated behind `WP_DEBUG`, so production hosts no longer accumulate noise from fail-open paths. * `readme.txt` gains a Source Code section documenting the GitHub repository, the esbuild-based build pipeline, and the `npm run build` command used to regenerate `assets/admin.js`. = 1.3.2 = * Slug renamed to `abilityguard-mcp` for the WordPress.org directory. * Snapshot file blobs now stored under `wp-uploads/abilityguard-mcp/` instead of `wp-content/abilityguard-staging/`. * Admin page CSS folded into the existing enqueued bundle; no more inline `