Tessera for the Abilities API
·
Snapshot, audit, and rollback layer for plugins that register abilities via the WordPress Abilities API.
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_contentstrategy. - 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_approvalis 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_<N>_abilityguard_*tables, with auto-install onwp_initialize_siteand auto-drop onwpmu_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 helpersabilityguard_rollback,abilityguard_snapshot_meta,abilityguard_snapshot_options. - REST:
/abilityguard/v1/log,/log/<id>,/log/export,/rollback/<id>,/rollback/bulk,/approval,/approval/<id>/approve,/approval/<id>/reject,/approval/bulk,/approval/export,/retention,/retention/prune,/health. - WP-CLI:
wp abilityguard log list/show,wp abilityguard rollback <id>,wp abilityguard approval list/approve/reject <id>,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.jsis compiled fromassets/admin.jsx(React + JSX, no preprocessor magic beyond JSX). - The bundler is esbuild, configured in
scripts/build.mjs. - To rebuild the admin bundle from a fresh checkout, run
npm installonce, thennpm run buildwheneverassets/admin.jsxchanges. This regeneratesassets/admin.jsin 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.