{"id":291225,"date":"2026-04-02T19:33:16","date_gmt":"2026-04-02T19:33:16","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/auraworker\/"},"modified":"2026-09-21T16:21:25","modified_gmt":"2026-09-21T16:21:25","slug":"digitizer-site-worker","status":"publish","type":"plugin","link":"https:\/\/pe.wordpress.org\/plugins\/digitizer-site-worker\/","author":9810718,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"2.19.2","stable_tag":"2.19.2","tested":"7.1.2","requires":"6.2","requires_php":"7.4","requires_plugins":null,"header_name":"SiteAgent for Aura","header_author":"Digitizer","header_description":"Remote site management agent for Aura dashboard. Enables secure updates, health monitoring, and maintenance operations via REST API.","assets_banners_color":"114354","last_updated":"2026-09-21 16:21:25","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/my-aura.app\/siteagent","header_author_uri":"https:\/\/www.digitizer.studio","rating":0,"author_block_rating":0,"active_installs":30,"downloads":2001,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"2.0.0":{"tag":"2.0.0","author":"benkalsky","date":"2026-06-11 23:19:26","revision":3569430},"2.0.1":{"tag":"2.0.1","author":"benkalsky","date":"2026-06-12 00:02:20","revision":3569442},"2.0.2":{"tag":"2.0.2","author":"benkalsky","date":"2026-06-18 22:20:58","revision":3577917},"2.1.0":{"tag":"2.1.0","author":"benkalsky","date":"2026-06-24 16:08:30","revision":3585092},"2.10.0":{"tag":"2.10.0","author":"benkalsky","date":"2026-08-21 23:48:43","revision":3659754},"2.10.1":{"tag":"2.10.1","author":"benkalsky","date":"2026-08-22 01:21:59","revision":3659795},"2.10.2":{"tag":"2.10.2","author":"benkalsky","date":"2026-08-23 00:39:23","revision":3661272},"2.10.3":{"tag":"2.10.3","author":"benkalsky","date":"2026-08-23 17:49:12","revision":3662172},"2.11.0":{"tag":"2.11.0","author":"benkalsky","date":"2026-08-26 10:14:58","revision":3666706},"2.12.0":{"tag":"2.12.0","author":"benkalsky","date":"2026-08-28 20:17:33","revision":3670889},"2.13.0":{"tag":"2.13.0","author":"benkalsky","date":"2026-08-30 12:27:02","revision":3672516},"2.14.0":{"tag":"2.14.0","author":"benkalsky","date":"2026-08-31 17:18:17","revision":3674705},"2.15.0":{"tag":"2.15.0","author":"benkalsky","date":"2026-09-02 10:58:18","revision":3677813},"2.16.2":{"tag":"2.16.2","author":"benkalsky","date":"2026-09-09 07:58:14","revision":3687860},"2.17.0":{"tag":"2.17.0","author":"benkalsky","date":"2026-09-11 09:09:28","revision":3691099},"2.17.1":{"tag":"2.17.1","author":"benkalsky","date":"2026-09-11 12:24:03","revision":3691364},"2.17.2":{"tag":"2.17.2","author":"benkalsky","date":"2026-09-11 15:23:02","revision":3691655},"2.17.3":{"tag":"2.17.3","author":"benkalsky","date":"2026-09-12 12:03:55","revision":3692657},"2.17.4":{"tag":"2.17.4","author":"benkalsky","date":"2026-09-16 12:52:02","revision":3698603},"2.17.5":{"tag":"2.17.5","author":"benkalsky","date":"2026-09-16 16:34:17","revision":3698997},"2.18.0":{"tag":"2.18.0","author":"benkalsky","date":"2026-09-17 05:56:48","revision":3699602},"2.18.1":{"tag":"2.18.1","author":"benkalsky","date":"2026-09-17 12:13:08","revision":3700248},"2.18.2":{"tag":"2.18.2","author":"benkalsky","date":"2026-09-17 15:57:04","revision":3700564},"2.18.3":{"tag":"2.18.3","author":"benkalsky","date":"2026-09-17 22:17:38","revision":3701056},"2.18.4":{"tag":"2.18.4","author":"benkalsky","date":"2026-09-18 00:21:03","revision":3701133},"2.19.0":{"tag":"2.19.0","author":"benkalsky","date":"2026-09-20 18:57:14","revision":3704551},"2.19.1":{"tag":"2.19.1","author":"benkalsky","date":"2026-09-20 21:14:15","revision":3704620},"2.19.2":{"tag":"2.19.2","author":"benkalsky","date":"2026-09-21 16:21:25","revision":3705937},"2.2.1":{"tag":"2.2.1","author":"benkalsky","date":"2026-06-25 20:06:07","revision":3586545},"2.2.2":{"tag":"2.2.2","author":"benkalsky","date":"2026-06-25 20:40:16","revision":3586566},"2.2.3":{"tag":"2.2.3","author":"benkalsky","date":"2026-06-29 11:00:51","revision":3589938},"2.2.4":{"tag":"2.2.4","author":"benkalsky","date":"2026-06-29 18:15:03","revision":3590490},"2.3.0":{"tag":"2.3.0","author":"benkalsky","date":"2026-06-29 22:19:56","revision":3590656},"2.6.1":{"tag":"2.6.1","author":"benkalsky","date":"2026-07-04 00:31:59","revision":3595617},"2.7.0":{"tag":"2.7.0","author":"benkalsky","date":"2026-07-04 19:17:30","revision":3596174},"2.7.1":{"tag":"2.7.1","author":"benkalsky","date":"2026-07-04 21:36:05","revision":3596208},"2.8.0":{"tag":"2.8.0","author":"benkalsky","date":"2026-07-11 19:08:09","revision":3604256},"2.8.1":{"tag":"2.8.1","author":"benkalsky","date":"2026-07-13 00:17:58","revision":3605287},"2.8.2":{"tag":"2.8.2","author":"benkalsky","date":"2026-07-15 09:50:40","revision":3608591},"2.9.0":{"tag":"2.9.0","author":"benkalsky","date":"2026-08-20 18:23:53","revision":3657534},"2.9.1":{"tag":"2.9.1","author":"benkalsky","date":"2026-08-20 20:45:25","revision":3657776}},"upgrade_notice":{"2.10.3":"<ul>\n<li>Fix (security): <strong>&quot;Regenerate Token&quot; revealed a new site token without ever\nstoring it.<\/strong> The option was registered as a read-only setting, and the\ncallback enforcing that ran on every write \u2014 not only on the settings form \u2014\nso the handler&#039;s write was discarded while the one-time reveal still\nappeared. Two consequences: an admin rotating a leaked token was told it was\nrevoked when the old token stayed valid, and a site disconnected from the\ndashboard could not be reconnected, because no token the screen displayed\never authenticated. The token is no longer registered as a setting (it is\ndisplay-only, so nothing submits it), and regeneration now stores the new\nhash with a single compare-and-swap, out of reach of any option filter \u2014 a\ntoken is revealed only when that one statement reports it wrote the row, and\na site whose row is missing or empty can be given its first token the same\nway.<\/li>\n<\/ul>","2.10.2":"<ul>\n<li>Fix: a site moved from one Aura client to another while the old client&#039;s\nlast push was still in flight could end up holding the old client&#039;s ruleset\nand refuse the new client&#039;s rules until it was reconnected. The connect\ncallback now names the client the site belongs to (a signed, optional field\n\u2014 older dashboards keep working unchanged) and writes that binding into the\nruleset store itself, so a ruleset for any other client is refused from then\non, whatever was in flight.<\/li>\n<\/ul>","2.10.1":"<p>Fixes audit_rules under-reporting the current hour&#039;s block\/warn counts on\nsites with a persistent object cache, and a spurious 500 when two first\nrulesets race. No change to enforcement. Recommended.<\/p>","2.10.0":"<p>Operator rules: write &quot;do not touch checkout&quot; once in Aura and every connected\nsite refuses the matching change \u2014 even an approved one \u2014 until the rule is\nreleased. No ruleset, no change in behaviour. Recommended for every site.<\/p>","2.9.1":"<p>Security hardening: tools that change your site can no longer be run through\nanother plugin&#039;s MCP server without an approval grant. Recommended for every\nsite, and especially any running a second AI assistant alongside SiteAgent. No\naction required; the Aura connection and read-only tools are unaffected.<\/p>","2.9.0":"<p>Adds five read-only audit tools, including one that reports which other MCP\nservers are registered on the site and how many of your abilities are\ndiscoverable to one \u2014 worth running if anything else here exposes an AI\nassistant. Nothing existing changes behaviour; no action required.<\/p>","2.8.2":"<p>Security hardening: snapshot restores now reject tampered payloads instead of\nunserializing arbitrary objects. Recommended for all users. No action required.<\/p>","2.8.1":"<p>Documentation only \u2014 the listing now describes the optional Power Pack companion\nplugin and its governance model. No code changes; no action required.<\/p>","2.8.0":"<p>Internal snapshot-engine primitives (groundwork for reversible Elementor and\nbulk-post editing, not yet exposed over the API) and a clearer SEO-meta\nwrite-failure error. No action required; existing connections keep working.<\/p>","2.3.0":"<p>Token-only connection: the Aura Site Token alone now authorizes site management. Existing connections keep working \u2014 no action required.<\/p>","2.2.4":"<p>Fixes one-click &quot;Connect to Aura&quot;: the magic-link onboarding now targets the Aura app host (<code>app.my-aura.app<\/code>) instead of the marketing domain, so connect works out of the box. Sites that set the <code>AURA_DASHBOARD_URL<\/code> constant are unaffected.<\/p>","2.2.3":"<p>Accuracy fixes for the auditor tools: <code>set_seo_meta<\/code> refreshes Yoast&#039;s cache, <code>perf_check<\/code> counts all WP 6.6+ autoload values, <code>scan_broken_links<\/code> reports true totals, <code>scan_seo<\/code> scores missing excerpts, and <code>scan_a11y<\/code> checks page language. No content changes.<\/p>","2.2.2":"<p>Adds on-site SEO-meta tools (<code>get_seo_meta<\/code> \/ <code>set_seo_meta<\/code>) for Rank Math, Yoast, and SEOPress \u2014 read and update a page&#039;s SEO title, description, and focus keyword, even where a WAF blocks the SEO plugin&#039;s REST endpoint. Writes are approval-gated through Aura.<\/p>","2.2.1":"<p>Adds two read-only auditor tools \u2014 <code>perf_check<\/code> and <code>scan_broken_links<\/code> \u2014 for performance and link triage across your fleet. No changes to your site; <code>scan_broken_links<\/code> performs no outbound HTTP.<\/p>","2.2.0":"<p>Adds two read-only auditor tools \u2014 <code>scan_seo<\/code> and <code>scan_a11y<\/code> \u2014 for SEO and accessibility checks across your fleet. No changes to your site; run on demand through Aura.<\/p>","2.1.0":"<p>Adds five new MCP agent tools (database info, security scan, user list, cache flush, transient cleanup). Read tools run on demand; cache\/transient tools are mutating and gated by Aura&#039;s approval policy.<\/p>","2.0.2":"<p>Fixes the plugin page screenshot caption rendering on WordPress.org. No code changes.<\/p>","2.0.1":"<p>Documentation update \u2014 corrected feature list, security description, endpoint reference, and admin menu location. No code changes.<\/p>","2.0.0":"<p>Major update: plugin rollback\/backup, site health checks, magic-link admin access, and MCP tools. Tested with WordPress 7.0. Recommended for all users.<\/p>","1.3.5":"<p>Enhanced security with timing-safe comparison and IP whitelisting. Recommended for all users.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3580090,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3580090,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3670769,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3670769,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["2.0.0","2.0.1","2.0.2","2.1.0","2.10.0","2.10.1","2.10.2","2.10.3","2.11.0","2.12.0","2.13.0","2.14.0","2.15.0","2.16.2","2.17.0","2.17.1","2.17.2","2.17.3","2.17.4","2.17.5","2.18.0","2.18.1","2.18.2","2.18.3","2.18.4","2.19.0","2.19.1","2.19.2","2.2.1","2.2.2","2.2.3","2.2.4","2.3.0","2.6.1","2.7.0","2.7.1","2.8.0","2.8.1","2.8.2","2.9.0","2.9.1"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3670889,"resolution":"1","location":"assets","locale":"","width":1726,"height":961},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3670889,"resolution":"2","location":"assets","locale":"","width":1726,"height":961},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3670889,"resolution":"3","location":"assets","locale":"","width":1726,"height":961},"screenshot-4.png":{"filename":"screenshot-4.png","revision":3670889,"resolution":"4","location":"assets","locale":"","width":1726,"height":961},"screenshot-5.png":{"filename":"screenshot-5.png","revision":3672516,"resolution":"5","location":"assets","locale":"","width":1726,"height":961},"screenshot-6.png":{"filename":"screenshot-6.png","revision":3672516,"resolution":"6","location":"assets","locale":"","width":1726,"height":961},"screenshot-7.png":{"filename":"screenshot-7.png","revision":3672516,"resolution":"7","location":"assets","locale":"","width":1726,"height":961}},"screenshots":{"1":"Settings \u2192 SiteAgent in wp-admin: site token status, optional IP \/ domain allowlists, one-click Connect to Aura, and a live connection test.","2":"Aura \u2192 Apps \u2192 Connection tab: connection status, plugin version, release channels (stable \/ beta), re-test, disconnect, and credential rotation.","3":"Health tab: WordPress \/ PHP \/ MySQL versions, server info, settings, active theme, and the full plugin inventory with status.","4":"Updates tab: SiteAgent rollout control (release channel, auto-update, policy and risk state), update status, and per-plugin database migration status.","5":"Fleet AI: the catalog of agent tools every connected site exposes \u2014 each tagged Read or Power \u2014 runnable across the whole fleet from one control plane.","6":"SiteAgent Power Pack: the companion plugin's write and code tools, each off until armed in wp-config.php and approval-gated through Aura.","7":"Connections: provider connections (Cloudways, Cloudflare, Bunny, Hostinger, Vultr, xCloud) with resource counts, status, and credential-rotation reminders."}},"plugin_section":[],"plugin_tags":[2353,569,732,2550,41933],"plugin_category":[52],"plugin_contributors":[260321],"plugin_business_model":[],"class_list":["post-291225","plugin","type-plugin","status-publish","hentry","plugin_tags-ai","plugin_tags-automation","plugin_tags-maintenance","plugin_tags-updates","plugin_tags-wordpress-management","plugin_category-performance","plugin_contributors-benkalsky","plugin_committers-benkalsky","plugin_support_reps-godigitizer"],"banners":{"banner":"https:\/\/ps.w.org\/digitizer-site-worker\/assets\/banner-772x250.png?rev=3670769","banner_2x":"https:\/\/ps.w.org\/digitizer-site-worker\/assets\/banner-1544x500.png?rev=3670769","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/digitizer-site-worker\/assets\/icon-128x128.png?rev=3580090","icon_2x":"https:\/\/ps.w.org\/digitizer-site-worker\/assets\/icon-256x256.png?rev=3580090","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/digitizer-site-worker\/assets\/screenshot-1.png?rev=3670889","caption":"Settings \u2192 SiteAgent in wp-admin: site token status, optional IP \/ domain allowlists, one-click Connect to Aura, and a live connection test."},{"src":"https:\/\/ps.w.org\/digitizer-site-worker\/assets\/screenshot-2.png?rev=3670889","caption":"Aura \u2192 Apps \u2192 Connection tab: connection status, plugin version, release channels (stable \/ beta), re-test, disconnect, and credential rotation."},{"src":"https:\/\/ps.w.org\/digitizer-site-worker\/assets\/screenshot-3.png?rev=3670889","caption":"Health tab: WordPress \/ PHP \/ MySQL versions, server info, settings, active theme, and the full plugin inventory with status."},{"src":"https:\/\/ps.w.org\/digitizer-site-worker\/assets\/screenshot-4.png?rev=3670889","caption":"Updates tab: SiteAgent rollout control (release channel, auto-update, policy and risk state), update status, and per-plugin database migration status."},{"src":"https:\/\/ps.w.org\/digitizer-site-worker\/assets\/screenshot-5.png?rev=3672516","caption":"Fleet AI: the catalog of agent tools every connected site exposes \u2014 each tagged Read or Power \u2014 runnable across the whole fleet from one control plane."},{"src":"https:\/\/ps.w.org\/digitizer-site-worker\/assets\/screenshot-6.png?rev=3672516","caption":"SiteAgent Power Pack: the companion plugin's write and code tools, each off until armed in wp-config.php and approval-gated through Aura."},{"src":"https:\/\/ps.w.org\/digitizer-site-worker\/assets\/screenshot-7.png?rev=3672516","caption":"Connections: provider connections (Cloudways, Cloudflare, Bunny, Hostinger, Vultr, xCloud) with resource counts, status, and credential-rotation reminders."}],"raw_content":"<!--section=description-->\n<p><strong>SiteAgent<\/strong> turns every WordPress site into one an AI agent can safely operate \u2014 update, maintain, audit, and fix. Once the site is connected to Aura and its approval key is provisioned, mutating actions are gated behind human approval and recorded in a full audit trail; safe batch updates and block edits are snapshotted so they can be rolled back. It's the on-site half of <a href=\"https:\/\/my-aura.app\">Aura<\/a>, the governed control room agencies use to run whole fleets of client sites alongside their servers, CDN, and DNS.<\/p>\n\n<p>Plugin and core updates are the riskiest thing you do on a live client site. SiteAgent makes them safer: safe batch updates run behind health checks and <strong>roll back automatically<\/strong> if the site breaks, and every plugin in that path is zip-snapshotted first. With Aura's approval key in place, an agent can't silently push a change \u2014 mutating actions wait for a human to approve them. Think of it as the undo button for AI on your clients' sites.<\/p>\n\n<p>Install this plugin on any WordPress site to connect it to Aura \u2014 no SSH, no wp-admin juggling, no manual logins.<\/p>\n\n<h4>What You Can Do<\/h4>\n\n<ul>\n<li><strong>Monitor site health<\/strong> \u2014 See WordPress version, PHP version, installed plugins &amp; themes, database info, and disk usage in real time.<\/li>\n<li><strong>Update plugins, themes &amp; core remotely<\/strong> \u2014 Push updates to any connected site from the Aura dashboard, no wp-admin login.<\/li>\n<li><strong>Safe batch updates with auto-rollback<\/strong> \u2014 Run chunked updates with health checks; if an update breaks the site, the plugin restores the previous version automatically.<\/li>\n<li><strong>Per-plugin rollback<\/strong> \u2014 Every update is zip-snapshotted first; restore any plugin to its last good state on demand.<\/li>\n<li><strong>Bulk translation &amp; database upgrades<\/strong> \u2014 Update all language packs and run WordPress database migrations remotely.<\/li>\n<li><strong>One-click connect (magic link)<\/strong> \u2014 Connect a site to Aura straight from wp-admin \u2014 no manual token copy\/paste.<\/li>\n<li><strong>AI-agent ready (27 MCP tools)<\/strong> \u2014 Exposes machine-readable, JSON-schema tools for AI-driven management, including SEO\/accessibility\/performance\/broken-link auditors, on-site SEO-meta read\/write (Rank Math, Yoast, SEOPress), and Gutenberg block read\/edit. Read tools run on demand; mutating tools are approval-gated through Aura, and every call is audited.<\/li>\n<li><strong>Zero frontend impact<\/strong> \u2014 The plugin only registers REST API endpoints. No scripts, no styles, no database queries on visitor-facing page loads.<\/li>\n<\/ul>\n\n<h4>How It Works<\/h4>\n\n<p>After activation, click <strong>Connect to Aura<\/strong> on the <strong>Settings \u2192 SiteAgent<\/strong> page for a one-click magic-link connection, or copy the Site Token shown once and paste it into your Aura dashboard manually. From that point, Aura communicates with your site over a signed, authenticated REST API to pull health data and push updates.<\/p>\n\n<h4>Security<\/h4>\n\n<p>Defence-in-depth protects every request:<\/p>\n\n<ol>\n<li><strong>WordPress Application Password<\/strong> \u2014 Standard WordPress auth with capability checks (<code>manage_options<\/code> \/ <code>update_*<\/code>). Only authorized administrators can trigger actions.<\/li>\n<li><strong>Hashed Site Token<\/strong> \u2014 A per-site token sent via the <code>X-Aura-Token<\/code> header. Only a SHA-256 <strong>hash<\/strong> is stored (never the raw token), compared timing-safely. Tokens from older versions migrate to a hash automatically.<\/li>\n<li><strong>Brute-force throttling<\/strong> \u2014 Repeated bad-token attempts from an IP are blocked.<\/li>\n<li><strong>Signed magic-link connect<\/strong> \u2014 The onboarding callback is HMAC-signed with a one-time secret and timestamp, so the token exchange can't be hijacked or replayed.<\/li>\n<li><strong>IP \/ Domain allowlist<\/strong> (optional) \u2014 Restrict API access to your Aura instance, with Cloudflare and reverse-proxy header support.<\/li>\n<\/ol>\n\n<p>You can rotate the token anytime from <strong>Settings \u2192 SiteAgent \u2192 Regenerate Token<\/strong>.<\/p>\n\n<h4>REST API Endpoints<\/h4>\n\n<p>Core endpoints under <code>\/wp-json\/aura\/v1\/<\/code>:<\/p>\n\n<ul>\n<li><code>GET \/status<\/code> \u2014 Full site health report<\/li>\n<li><code>GET \/updates<\/code> \u2014 Check available updates (core, plugins, themes, translations)<\/li>\n<li><code>POST \/update\/core<\/code> \/ <code>\/update\/plugin<\/code> \/ <code>\/update\/theme<\/code> \/ <code>\/update\/translations<\/code> \u2014 Apply updates<\/li>\n<li><code>POST \/update\/database<\/code> \u2014 Run WordPress database upgrades<\/li>\n<li><code>POST \/connect<\/code> \u2014 Magic-link token exchange (public, HMAC-signed, 10-minute expiry)<\/li>\n<\/ul>\n\n<p>Version 2 endpoints under <code>\/wp-json\/aura\/v2\/<\/code>:<\/p>\n\n<ul>\n<li><code>GET \/health<\/code> \u2014 HTTP, PHP fatal, white-screen and DB connectivity checks<\/li>\n<li><code>POST \/update\/batch<\/code> \u2014 Chunked batch updates with auto-rollback on health failure<\/li>\n<li><code>POST \/rollback\/{plugin}<\/code> \u2014 Restore a plugin from its most recent backup<\/li>\n<li><code>POST \/rules<\/code> \u2014 Receive Aura's signed operator ruleset; carrying <code>unbind: true<\/code> it ends this site's binding instead<\/li>\n<\/ul>\n\n<p>While a site is disconnected by Aura, every write endpoint answers\n    403 aura_site_unbound and reads keep working; the disconnect answer carries\n    cleanup_complete and <code>leftovers<\/code> (what the site still holds), and <code>GET \/status<\/code>\nreports <code>unbound<\/code> until the site is reconnected or the remaining Aura data is removed\nfrom the settings screen.<\/p>\n\n<p>MCP tools under <code>\/wp-json\/aura\/mcp\/<\/code>:<\/p>\n\n<ul>\n<li><code>POST \/tools\/list<\/code> \/ <code>POST \/tools\/execute<\/code> \u2014 Enumerate and run AI-agent tools<\/li>\n<li><code>GET \/context<\/code> \u2014 Full site context for AI decision-making<\/li>\n<\/ul>\n\n<h4>AI Agent Tools (MCP)<\/h4>\n\n<p>SiteAgent ships <strong>29 built-in tools<\/strong> for AI agents. Read tools return information and run on demand; write tools change the site and are queued for human approval through Aura \u2014 an agent can never silently mutate a production site.<\/p>\n\n<p>Read tools:<\/p>\n\n<ul>\n<li><code>get_site_context<\/code> \u2014 WordPress\/PHP\/theme\/plugin\/disk\/performance snapshot with detected issues<\/li>\n<li><code>get_database_info<\/code> \u2014 Database size, largest tables, autoloaded-options weight, expired transients<\/li>\n<li><code>scan_security<\/code> \u2014 Scored security posture (file-edit lockdown, debug exposure, SSL, default admin\/prefix, open registration, PHP version)<\/li>\n<li><code>scan_seo<\/code> \u2014 SEO posture (search-engine visibility, permalinks, XML sitemap, site title) plus a sampled content audit (thin content, missing excerpts\/featured images)<\/li>\n<li><code>scan_a11y<\/code> \u2014 Accessibility audit over sampled content (images missing alt text, non-descriptive link text, heading structure, document language)<\/li>\n<li><code>perf_check<\/code> \u2014 Performance posture (persistent object cache, OPcache, page-cache plugin, PHP version, autoload weight, active plugin count, memory limit)<\/li>\n<li><code>scan_broken_links<\/code> \u2014 Link triage over a content sample with no outbound HTTP (empty\/anchor-only links, dev\/staging hosts, unresolved internal links)<\/li>\n<li><code>list_users<\/code> \u2014 Users with roles and post counts, administrators flagged (never returns secrets)<\/li>\n<li><code>check_health<\/code> \u2014 Live health gate: HTTP status, PHP fatals, white-screen, database connectivity<\/li>\n<li><code>scan_error_log<\/code> \u2014 Tails and severity-groups the error log, surfacing recent fatals<\/li>\n<li><code>check_vulnerabilities<\/code> \u2014 Plugin update-currency check against WordPress.org (a version check, not a vulnerability\/CVE feed; covers wp.org-hosted plugins)<\/li>\n<li><code>check_core_checksums<\/code> \u2014 Core-file integrity against the official WordPress.org checksum manifest (modified\/missing\/unexpected files, fetched over HTTPS only)<\/li>\n<li><code>scan_executable_files<\/code> \u2014 Uploads-directory observations: PHP\/executable files, .htaccess overrides, and symlinks (reported, never followed)<\/li>\n<li><code>audit_admin_accounts<\/code> \u2014 Privileged-account facts: administrators with recency, admin capabilities outside the role, application-password counts, multisite super admins<\/li>\n<li><code>audit_cron<\/code> \u2014 Bounded WP-Cron inventory with sub-60-second-schedule and unresolved-callback fact-flags<\/li>\n<li><code>audit_mcp_exposure<\/code> \u2014 Which other MCP servers are registered on this site, and how many abilities pass the discovery rule such a server applies (abilities are registered site-wide, not per-plugin, so a server resolving targets from that registry picks up mutating ones outside SiteAgent's approval path). The counts describe the abilities, not what any server currently serves. Reports only; changes nothing<\/li>\n<li><code>audit_agent_code<\/code> \u2014 Executable code an AI agent authored or can author on this site: Angie code snippets (recorded, agent-authored, live per environment), the SiteAgent Power Pack's execute-php \/ file-write \/ wp-cli flags, and third-party exec stores. Counts and presence only; never file contents, never a verdict. Reports only; changes nothing<\/li>\n<li><code>audit_rules<\/code> \u2014 Whether a signed operator ruleset is present and how old it is, 24h block\/warn counts, expired-but-listed rules, and the enforcement points in this build. Reports only; changes nothing<\/li>\n<li><code>get_seo_meta<\/code> \u2014 Read a post\/page's SEO title, description, and focus keyword from the active SEO plugin (Rank Math, Yoast, or SEOPress)<\/li>\n<li><code>list_page_blocks<\/code> \u2014 Read a page's Gutenberg block structure (block names, attributes, nesting)<\/li>\n<li><code>snapshot_get<\/code> \u2014 Retrieve a stored snapshot of page content (reversible write metadata)<\/li>\n<\/ul>\n\n<p>Write tools (approval-gated):<\/p>\n\n<ul>\n<li><code>elementor_replay_ability<\/code> \u2014 Executes an approved held Elementor write (destructive, requires Aura approval): claims the hold, re-judges it against the current ruleset, and runs the original Elementor mutation as the user who asked<\/li>\n<li><code>update_plugin_safely<\/code> \u2014 Backup, update, health-check, auto-rollback on failure<\/li>\n<li><code>clear_caches<\/code> \u2014 Flush object\/opcode caches and detected page-cache plugins<\/li>\n<li><code>cleanup_transients<\/code> \u2014 Remove expired transients to reduce autoload bloat<\/li>\n<li><code>cleanup_orphaned_assets<\/code> \u2014 Find and remove unused media (dry-run by default)<\/li>\n<li><code>backup_plugins<\/code> \u2014 Zip-snapshot one or all active plugins as a rollback safety net<\/li>\n<li><code>set_seo_meta<\/code> \u2014 Write a post\/page's SEO title \/ description \/ focus keyword on the active SEO plugin (Rank Math, Yoast, or SEOPress) \u2014 on-site, so it works even when a WAF blocks the plugin's own REST endpoint<\/li>\n<li><code>update_page_block<\/code> \u2014 Update a Gutenberg block's content or attributes (snapshot-first, reversible)<\/li>\n<li><code>create_page_from_blocks<\/code> \u2014 Create a new page from a Gutenberg block spec (draft-first)<\/li>\n<\/ul>\n\n<p>Tools are classified by verb so the Aura Fleet gateway applies the right risk and approval policy automatically.<\/p>\n\n<h4>Pro: the SiteAgent Power Pack<\/h4>\n\n<p>Everything above is free and ships in this plugin. Agencies that need an agent to <em>fix<\/em> a site \u2014 not just report on it \u2014 can add the <strong>SiteAgent Power Pack<\/strong>, a separate companion plugin that registers higher-capability tools through this plugin's own tool registry.<\/p>\n\n<p>It is <strong>not<\/strong> included in this download and is <strong>not<\/strong> distributed on WordPress.org \u2014 the tools it adds execute code, so they don't belong in a hosted repository. It comes with the Aura <strong>Agency<\/strong> and <strong>Studio<\/strong> plans, or as <strong>SiteAgent Pro<\/strong>.<\/p>\n\n<p>What it adds:<\/p>\n\n<ul>\n<li><code>read_file<\/code> \u2014 read a text file from inside wp-content (jailed; refuses wp-config.php).<\/li>\n<li><code>db_query<\/code> \u2014 a single read-only SQL statement (SELECT \/ SHOW \/ EXPLAIN), row-capped.<\/li>\n<li><code>write_file<\/code> \u2014 write a file inside wp-content, snapshot-first so it can be rolled back.<\/li>\n<li><code>run_wp_cli<\/code> \u2014 run an allowlisted WP-CLI command, with no shell and no metacharacters.<\/li>\n<li><code>execute_php<\/code> \u2014 run a PHP snippet against the full WordPress API.<\/li>\n<\/ul>\n\n<p>These are governed harder than anything in the free set, deliberately:<\/p>\n\n<ol>\n<li><strong>Off until you arm them.<\/strong> The write and code tools do nothing until the site owner sets an explicit constant in <code>wp-config.php<\/code> for each one. Installing the Power Pack alone enables no writes and no code execution.<\/li>\n<li><strong>Human approval, cryptographically enforced.<\/strong> Once the site holds Aura's approval key (provisioned when you connect the site), each of these calls requires a single-use, signed grant bound to that exact tool and its exact parameters \u2014 one only the Aura dashboard can mint, after a human approves the action. A leaked Site Token cannot run them. (Until a site has that key, the gate is dormant \u2014 so connect the site from Aura before arming anything.)<\/li>\n<li><strong>Reversible where it can be.<\/strong> File writes are snapshotted first, so there's a previous state to restore.<\/li>\n<\/ol>\n\n<p>The safety model is governance, not a sandbox: <code>execute_php<\/code> is powerful by design. The controls are the constant you set, the human who approves the call, and the audit trail \u2014 not a promise that arbitrary code is safe.<\/p>\n\n<p>Learn more at <a href=\"https:\/\/my-aura.app\/siteagent\">my-aura.app\/siteagent<\/a>.<\/p>\n\n<h4>About Aura<\/h4>\n\n<p>Aura is a full-stack operations dashboard by <a href=\"https:\/\/digitizer.studio\">Digitizer<\/a> that brings servers, applications, DNS zones, and CDN pull zones from Cloudways, Hostinger VPS, Cloudflare, and Bunny.net into a single unified interface.<\/p>\n\n<p>SiteAgent extends that reach into every WordPress installation \u2014 so you can manage your entire infrastructure, including WordPress sites, from one place.<\/p>\n\n<h4>Free to Use<\/h4>\n\n<p>The plugin is completely free and open source (GPLv2+). You need a free or paid Aura account to connect your sites. <a href=\"https:\/\/my-aura.app\">Sign up at my-aura.app<\/a>.<\/p>\n\n<h4>Links<\/h4>\n\n<ul>\n<li><a href=\"https:\/\/my-aura.app\">Aura Dashboard<\/a><\/li>\n<li><a href=\"https:\/\/my-aura.app\/siteagent\">Documentation<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/Digitizers\/SiteAgent\">GitHub Repository<\/a><\/li>\n<li><a href=\"https:\/\/digitizer.studio\">Digitizer<\/a><\/li>\n<\/ul>\n\n<!--section=installation-->\n<h4>Via WordPress Admin (Recommended)<\/h4>\n\n<ol>\n<li>Go to <strong>Plugins \u2192 Add New<\/strong> in your WordPress admin.<\/li>\n<li>Search for <strong>SiteAgent<\/strong>.<\/li>\n<li>Click <strong>Install Now<\/strong>, then <strong>Activate<\/strong>.<\/li>\n<li>Navigate to <strong>Settings \u2192 SiteAgent<\/strong>.<\/li>\n<li>Click <strong>Connect to Aura<\/strong> for one-click magic-link onboarding \u2014 or copy the Site Token (shown once) and paste it into your Aura dashboard manually.<\/li>\n<\/ol>\n\n<h4>Via WP-CLI<\/h4>\n\n<pre><code>wp plugin install digitizer-site-worker --activate\n<\/code><\/pre>\n\n<h4>Manual Upload<\/h4>\n\n<ol>\n<li>Download the plugin ZIP from WordPress.org.<\/li>\n<li>Go to <strong>Plugins \u2192 Add New \u2192 Upload Plugin<\/strong>.<\/li>\n<li>Upload the ZIP and click <strong>Install Now<\/strong>, then <strong>Activate<\/strong>.<\/li>\n<li>Navigate to <strong>Settings \u2192 SiteAgent<\/strong> to connect or get your Site Token.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"do%20i%20need%20an%20aura%20account%3F\"><h3>Do I need an Aura account?<\/h3><\/dt>\n<dd><p>Yes, you need an Aura account to connect your WordPress sites. Aura offers a free tier that includes up to 3 WordPress sites. <a href=\"https:\/\/my-aura.app\">Sign up at my-aura.app<\/a>.<\/p><\/dd>\n<dt id=\"is%20this%20plugin%20safe%20to%20use%3F\"><h3>Is this plugin safe to use?<\/h3><\/dt>\n<dd><p>Yes. The plugin uses defence-in-depth: WordPress Application Passwords (the same standard mechanism used by the block editor), a per-site token stored only as a SHA-256 hash and verified timing-safely, per-IP brute-force throttling, an HMAC-signed onboarding handshake, and an optional IP\/domain allowlist. No data is transmitted unless a request is made by your Aura instance.<\/p><\/dd>\n<dt id=\"how%20do%20i%20enable%20the%20approval%20gate%20for%20write%20actions%3F\"><h3>How do I enable the approval gate for write actions?<\/h3><\/dt>\n<dd><p>SiteAgent can require a per-action, cryptographically signed approval before it runs a state-changing <strong>MCP tool<\/strong> (cleanups, cache flushes, SEO writes, safe plugin updates run through the tool interface). Once enabled, each such write must carry a single-use signature that only the Aura dashboard can mint, after a human approves the action \u2014 so a leaked Site Token cannot run those tools on its own.<\/p>\n\n<p>This gate turns on automatically once the site holds Aura's approval key, which is provisioned securely during connection. <strong>If you installed or updated the plugin but have not reconnected the site since, the gate is dormant<\/strong> and the site runs in the standard token-only mode. To activate it, simply <strong>reconnect the site from your Aura dashboard<\/strong> \u2014 no reinstall is needed.<\/p>\n\n<p>Note: the approval gate currently covers the MCP tool path. Core, plugin, and theme updates performed over the plugin's direct REST update endpoints are still authorized by the Site Token alone (the standard site-management model), so treat the Site Token as a sensitive credential regardless. Grant coverage for those update endpoints is on the roadmap.<\/p><\/dd>\n<dt id=\"does%20it%20slow%20down%20my%20site%3F\"><h3>Does it slow down my site?<\/h3><\/dt>\n<dd><p>No. The plugin registers only REST API endpoints. It does not load any code, scripts, or database queries on frontend page loads. Your visitors experience zero impact.<\/p><\/dd>\n<dt id=\"what%20wordpress%20versions%20are%20supported%3F\"><h3>What WordPress versions are supported?<\/h3><\/dt>\n<dd><p>WordPress 6.2 or higher is required. This is needed for full Application Password support. The plugin has been tested up to WordPress 7.1.<\/p><\/dd>\n<dt id=\"what%20php%20versions%20are%20supported%3F\"><h3>What PHP versions are supported?<\/h3><\/dt>\n<dd><p>PHP 7.4 or higher. PHP 8.0+ is recommended.<\/p><\/dd>\n<dt id=\"can%20i%20restrict%20which%20ip%20addresses%20can%20access%20the%20api%3F\"><h3>Can I restrict which IP addresses can access the API?<\/h3><\/dt>\n<dd><p>Yes. The plugin supports an optional IP whitelist. If configured, only requests from the specified IP addresses will be accepted. Cloudflare and reverse proxy headers (<code>CF-Connecting-IP<\/code>, <code>X-Forwarded-For<\/code>, <code>X-Real-IP<\/code>) are fully supported for IP detection.<\/p><\/dd>\n<dt id=\"does%20this%20work%20with%20wordpress%20multisite%3F\"><h3>Does this work with WordPress multisite?<\/h3><\/dt>\n<dd><p>The plugin is designed for single WordPress installations. Multisite support is not currently available but is on the roadmap.<\/p><\/dd>\n<dt id=\"where%20is%20the%20site%20token%20stored%3F\"><h3>Where is the Site Token stored?<\/h3><\/dt>\n<dd><p>Only a SHA-256 <strong>hash<\/strong> of the Site Token is stored, in the WordPress option <code>aura_worker_site_token<\/code> \u2014 the raw token is never persisted. It is generated on first activation and shown once so you can copy it; the Aura dashboard keeps the only raw copy. Tokens created by older versions are upgraded to a hash automatically on first use.<\/p><\/dd>\n<dt id=\"can%20i%20regenerate%20the%20site%20token%3F\"><h3>Can I regenerate the Site Token?<\/h3><\/dt>\n<dd><p>Yes. Use <strong>Regenerate Token<\/strong> on the <strong>Settings \u2192 SiteAgent<\/strong> page. The new token is shown once. Regenerating invalidates the old token and disconnects the site from Aura until you reconnect with the new one.<\/p><\/dd>\n<dt id=\"how%20do%20i%20disconnect%20a%20site%20from%20aura%3F\"><h3>How do I disconnect a site from Aura?<\/h3><\/dt>\n<dd><p>Remove the site from your Aura dashboard, or deactivate or delete the plugin. If you deactivate the plugin, the REST API endpoints are unregistered and Aura can no longer communicate with the site.<\/p>\n\n<p>Since 2.13.0, disconnecting from the Aura dashboard happens in two phases. The site is marked disconnected immediately and refuses every change from that moment on \u2014 reads keep working \u2014 and Aura then has it revoke the Application Password it minted, clear the stored ruleset and gateway key, and finally delete the site token. If the site was mid-disconnect when it lost contact, it finishes the job by itself on its next page load.<\/p><\/dd>\n<dt id=\"aura%20disconnected%20my%20site%20but%20something%20is%20left%20behind%20%E2%80%94%20what%20do%20i%20do%3F\"><h3>Aura disconnected my site but something is left behind \u2014 what do I do?<\/h3><\/dt>\n<dd><p>Open <strong>Settings \u2192 SiteAgent<\/strong>. A disconnected site says so (\"Disconnected by Aura at \u2026\") and offers <strong>Remove remaining Aura data<\/strong>, which revokes what is left and clears the disconnect record \u2014 but only once everything it names, the site token included, is proven gone. If it tells you it cannot say which user holds an Application Password, revoke it under <strong>Users \u2192 Profile \u2192 Application Passwords<\/strong> and try again. Reconnecting the site to Aura also clears the record, after settling what the previous connection still owed.<\/p><\/dd>\n<dt id=\"does%20aura%20store%20my%20wp-admin%20credentials%3F\"><h3>Does Aura store my wp-admin credentials?<\/h3><\/dt>\n<dd><p>No. Aura uses WordPress Application Passwords, not your main admin password. Application Passwords are scoped specifically for REST API access and can be revoked at any time from <strong>Users \u2192 Your Profile<\/strong> in wp-admin.<\/p><\/dd>\n<dt id=\"is%20the%20plugin%20open%20source%3F\"><h3>Is the plugin open source?<\/h3><\/dt>\n<dd><p>Yes. SiteAgent is open source under the GPLv2 or later license. The source code is available on <a href=\"https:\/\/github.com\/Digitizers\/SiteAgent\">GitHub<\/a>.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>2.19.2<\/h4>\n\n<ul>\n<li><code>audit_agent_code<\/code> now counts what is in a third-party agent tool's sandbox store (<code>wp-content\/emcp-sandbox<\/code>): <code>third_party.emcp_sandbox<\/code> gains <code>active<\/code> (the plugin is loaded) and <code>store<\/code> \u2014 how many files, how many with an executable extension, and the newest modification date. Until now an empty directory and one holding forty PHP files looked the same to a fleet audit. Counts only: no file is opened and no file name leaves the site. Links are never followed; a sandbox directory that is itself a link is reported as such and not walked. A directory that cannot be read is reported as unreadable, never as empty. Read-only, no settings, nothing changes on the site.<\/li>\n<\/ul>\n\n<h4>2.19.1<\/h4>\n\n<ul>\n<li><code>audit_mcp_exposure<\/code>'s <code>elementor.governor<\/code> block reports <code>proxy_refused_30d<\/code> \u2014 how many times in the last 30 days the door governor turned an authenticated non-browser caller (an Application Password or bearer identity: an agent) away from Elementor's editor proxy (<code>403 aura_door_proxy_closed<\/code>, 2.19.0). Anonymous requests get the same 403 but are not counted, so no unauthenticated traffic can write to the site. Same hourly buckets and <code>counters_as_of<\/code> cutoff as the four existing counters; <code>null<\/code> when the count could not be read.<\/li>\n<\/ul>\n\n<h4>2.19.0<\/h4>\n\n<ul>\n<li>Security: Elementor's editor-internal MCP proxy (<code>elementor\/v1\/mcp-proxy<\/code>) is closed to anything but a browser session while the Aura door governor is active \u2014 an Application Password or bearer client is answered <code>403 aura_door_proxy_closed<\/code> and pointed at the governed <code>\/elementor\/mcp<\/code> door. Six of the eleven governed Elementor writes were reachable there past the governor since 2.16.0; the editor's own Global Classes \/ Variables UI is unaffected.<\/li>\n<li>Security: the door governor's route matchers are case-insensitive, as WordPress's REST dispatcher is \u2014 a mixed-case spelling of a governed route no longer walks past the transport closer.<\/li>\n<li><code>audit_mcp_exposure<\/code>'s <code>elementor<\/code> block now reports Elementor 4.3.0-beta3's kill switch on the token door (<code>switch<\/code>: the <code>elementor_mcp_enabled<\/code> option, absent means off) and which vendored WP MCP adapter and <code>elementor-mcp-composer<\/code> copies actually resolve on the site (<code>adapter<\/code>, <code>composer<\/code>: version and ABSPATH-relative path).<\/li>\n<\/ul>\n\n<h4>2.18.4<\/h4>\n\n<ul>\n<li>Security: an empty port after a webhook host (<code>https:\/\/hooks.zapier.com:\/hooks\/...<\/code>, <code>hooks.zapier.com:\/...<\/code>) is now read as a URL parser reads it, so such URLs are redacted in plain text too \u2014 the case 2.18.3 listed as a known limit. A colon followed by anything but a port and a slash is still not a port.<\/li>\n<\/ul>\n\n<h4>2.18.3<\/h4>\n\n<ul>\n<li>Security: redaction now reads a URL the way a browser's URL parser does. A backslash used as the path separator under http, https, ws, wss, ftp or a protocol-relative prefix (<code>https:\/\/hooks.zapier.com\\hooks\\catch\\1\/\u2026<\/code>, also percent- or HTML-encoded) is redacted whole, including the part after the first backslash that earlier releases left in place.<\/li>\n<li>Security: hostname characters a URL parser normalises away (fullwidth and mathematical letters, the ideographic full stop, zero-width and soft-hyphen characters \u2014 Unicode 18.0.0's IDNA mapping table) are mapped before matching, so <code>\uff48ooks.zapier.com<\/code> and <code>hooks&amp;#x3002;zapier.com<\/code> are redacted like <code>hooks.zapier.com<\/code>.<\/li>\n<li>A URL parser's userinfo (<code>a@b@host<\/code>, an encoded slash or backslash inside it) and an empty port (<code>host:\/<\/code>) are read as the parser reads them in the backslash and encoded forms above; a plain <code>https:\/\/host:\/path<\/code> with nothing encoded is still matched by the older rule that wants a port digit (tracked as #121).<\/li>\n<li>Redacting a very large field with millions of words now takes about three times the field's size in memory instead of eighteen.<\/li>\n<li>Kept as they are: a backslash after a bare host with no scheme, Windows and UNC paths, <code>file:<\/code> URLs.<\/li>\n<\/ul>\n\n<h4>2.18.2<\/h4>\n\n<ul>\n<li>Security: redaction now decodes HTML references (including the double encoding WordPress itself produces on save and render), percent escapes and JSON escapes before looking for webhook URLs, so encoded or double-encoded Make, Zapier, Slack, Discord, IFTTT or Telegram hook URLs are now redacted.<\/li>\n<li>The whole encoded word is replaced, not just the part that decoded into a match.<\/li>\n<li>An encoded word whose host only looks like a webhook host is redacted too.<\/li>\n<li>More than four encoding layers are replaced as a field.<\/li>\n<\/ul>\n\n<h4>2.18.1<\/h4>\n\n<ul>\n<li>Security: receiver URLs are now redacted even when their <code>\/<\/code>, <code>:<\/code> or <code>@<\/code> are percent-encoded or HTML-escaped, including numeric character references left without a trailing semicolon. Before this, an encoded Make, Zapier, Slack, Discord, IFTTT or Telegram hook URL could reach an agent unredacted.<\/li>\n<li>The redaction walk now runs under a total node budget, so a self-referencing payload no longer runs until the request times out; a snapshot payload that exhausts the budget is withheld as <code>payload: null, payload_redacted: true<\/code> instead.<\/li>\n<li>The gateway's early placeholder\/grant check now only fires for a caller the IP\/Origin allowlist and token throttle would already admit, so a throttled or disallowed caller no longer learns whether its token was right.<\/li>\n<li>A legacy plaintext token is migrated before an MCP-path grant is verified.<\/li>\n<\/ul>\n\n<h4>2.18.0<\/h4>\n\n<ul>\n<li>Security: known webhook endpoints no longer leave the site in a REST response an AI agent reads \u2014 Aura's gateway tools, an MCP client using an Application Password, or any logged-in non-browser caller. Make, Zapier, Slack, Discord, IFTTT and Telegram hook URLs, and Elementor Pro form webhooks on any host, are replaced with <code>aura-redacted:v1:&lt;kind&gt;<\/code>, including inside Elementor page data and snapshot payloads. People in wp-admin, public visitors and Aura's own system calls see the real values.<\/li>\n<li>An agent write that carries a redacted placeholder is refused (<code>aura_redacted_placeholder<\/code>): leave that field out and the stored value is kept.<\/li>\n<li>Aura's own page snapshots stay complete: a signed header proves the read is Aura's (<code>aura_unredacted_grant_invalid<\/code> when it does not verify).<\/li>\n<li><code>audit_rules<\/code> reports <code>redacted_24h<\/code> and <code>placeholder_refused_24h<\/code>; <code>\/status<\/code> reports <code>redaction<\/code>.<\/li>\n<\/ul>\n\n<h4>2.17.5<\/h4>\n\n<ul>\n<li>Plugin updates: on a host where PHP may not write or delete <code>.php<\/code> files (WP Engine is the proven case), every Aura-driven plugin change \u2014 the self-update, a single or batch plugin update, <code>update_plugin_safely<\/code> \u2014 is refused before anything is touched, with <code>code: aura_php_writes_blocked<\/code> (or <code>aura_upgrade_dir_unwritable<\/code> when <code>wp-content\/upgrade<\/code> cannot be written at all). Such a host failed every update at unpack, and the restore that followed deleted the plugin's non-PHP files: SiteAgent's own <code>readme.txt<\/code>, and a plugin's CSS, JS and images. The check creates and removes a temporary file, and runs only when WordPress writes files directly.<\/li>\n<li>Restore: a restore checks that it can finish \u2014 PHP can write <code>.php<\/code> files in the plugins directory, and every directory of the plugin is writable \u2014 before it deletes anything; otherwise it answers <code>stage: preflight<\/code> and leaves the directory untouched. A failed install that changed no file is no longer restored at all (<code>restore_skipped: unchanged<\/code>).<\/li>\n<li><code>\/status<\/code> reports the host's last recorded answer as <code>host: { php_writes, checked_at }<\/code>, so Aura can stop sending updates to such a site.<\/li>\n<li><code>update_plugin_safely<\/code> returns the refusal's code and message (it lost both before).<\/li>\n<\/ul>\n\n<h4>2.17.4<\/h4>\n\n<ul>\n<li>Self-update: a package that carries the version already running is refused before anything changes. The verified download is inspected in place, the plugin is not backed up, and no install runs. Before this, a same-version request backed the plugin up, reinstalled over the live directory, refused and restored \u2014 and on one host lost <code>readme.txt<\/code> on every run. The refusal answers <code>code: aura_self_update_same_version<\/code>. An archive that cannot be read falls through to the previous path.<\/li>\n<li>Self-update: when the post-install version check fails, the message now says what the evidence supports. A verified package with a different version means the upgrader did not replace the plugin directory (<code>aura_self_update_not_replaced<\/code>); without that proof both causes are named (<code>aura_self_update_version_unchanged<\/code>).<\/li>\n<li>Self-update: a restore that cannot finish reports which stage failed (<code>clear<\/code> or <code>extract<\/code>), and the message says what is on disk. A failed clear can remove some files before it stops, so the directory is no longer described as untouched.<\/li>\n<\/ul>\n\n<h4>2.17.3<\/h4>\n\n<ul>\n<li>Snapshots: an <code>overwrite_file()<\/code> record now stores the sha256 of the content it wrote (<code>replaced_with_sha256<\/code>), and restoring that record puts the old bytes back ONLY while the file still holds exactly what the write left there. A file edited since is refused untouched (<code>aura_file_changed_since<\/code>); a file already holding the old bytes answers <code>already<\/code> and is not rewritten; a record taken without that hash \u2014 a direct <code>POST \/aura\/v2\/snapshot<\/code>, or a Power Pack older than 0.2.5 \u2014 is refused as unfenced (<code>aura_snapshot_unfenced<\/code>), because nothing proves what it would be writing over.<\/li>\n<li>Snapshots: every engine write to a path records a <code>write_seq<\/code>, taken under that path's lock and strictly increasing per target, so two writes to one file can be ordered after the fact \u2014 Aura rolls a run back in that order.<\/li>\n<li>Snapshots: a file restore that cannot act says why with a code instead of a bare failure \u2014 <code>aura_file_changed_since<\/code>, <code>aura_snapshot_unfenced<\/code>, <code>aura_snapshot_voided<\/code> (the create's rollback record is gone) and <code>aura_path_locked<\/code> (another write to the path is in progress) \u2014 each answered as an HTTP 409 refusal rather than a 500. Restoring a created file that is already gone answers <code>already<\/code>.<\/li>\n<\/ul>\n\n<h4>2.17.2<\/h4>\n\n<ul>\n<li>Snapshots: every engine write to a path \u2014 <code>create_file()<\/code>, the new <code>overwrite_file()<\/code>, and the restore of a created file \u2014 runs under a per-path lock, so two agent writes racing one new file can no longer interleave in the same inode (a concurrent in-place overwrite passed the inode check). After a publish the target must hash to the recorded content, else the record is voided as <code>interrupted<\/code> and the staged bytes kept.<\/li>\n<li>Snapshots: <code>overwrite_file()<\/code> replaces an existing file the way the engine creates one \u2014 old bytes snapshotted, new content staged beside the target with its own mode, then renamed over it: a reader sees the old file or the new, never a truncated one, and a short write touches nothing. The Power Pack's <code>write_file<\/code> adopts it in 0.2.5.<\/li>\n<li>Snapshots: on a host without <code>flock()<\/code> the record and path locks are <code>mkdir()<\/code> directories (atomic everywhere; a lock older than five minutes is a crashed holder and is broken) instead of running unlocked, which let a stage sweep retire the record of a publish that then completed.<\/li>\n<li>Snapshots: without <code>link()<\/code>, an executable created file that was edited since is verified in place and left where it is on <code>file_changed_since<\/code> \u2014 the claim by rename came first and the put-back refused exec bits, stranding the edited file under its <code>.aura-restore-*<\/code> name.<\/li>\n<li>Snapshots: <code>delete()<\/code> removes the record's lock file with the record.<\/li>\n<\/ul>\n\n<h4>2.17.1<\/h4>\n\n<ul>\n<li>Snapshots: <code>create_file()<\/code> publishes without <code>link()<\/code>. Most managed hosts put <code>link()<\/code> in <code>disable_functions<\/code> for web PHP (Cloudways does), and 2.17.0 refused every new-file create there (<code>unsupported_filesystem<\/code>). Without <code>link()<\/code> the target is now claimed with an exclusive create \u2014 it refuses an existing path and returns an inode this call owns \u2014 and the bytes are written into that handle; ownership is checked by inode after the write, so nothing that took the path meanwhile is ever overwritten. What this mode gives up is the empty-to-complete jump: a reader in the milliseconds of the write can see the file grow. A short write leaves the entry empty, never truncated content. Restoring a created file that was edited since puts it back the same way instead of leaving it aside under its <code>.aura-restore-*<\/code> name. A publish interrupted mid-write is reconciled by the stage sweep: the record is voided and marked <code>interrupted<\/code>, the file is never deleted by a restore. <code>create_file()<\/code> answers <code>published: link | write<\/code>.<\/li>\n<li><code>audit_agent_code<\/code>: <code>power_pack.create_publish<\/code> says how a create lands on this host (<code>link<\/code> or <code>write<\/code>).<\/li>\n<\/ul>\n\n<h4>2.17.0<\/h4>\n\n<ul>\n<li>New read-only tool <code>audit_agent_code<\/code>: executable code an AI agent authored or can author on this site \u2014 Angie code snippets (recorded, agent-authored, and which are live in the environment the loader includes, correlated per snippet directory in both environments), the SiteAgent Power Pack's execute-php \/ file-write \/ wp-cli flags, and third-party exec stores (EMCP Pro sandbox, Atarim exec abilities). Counts and presence only; never file contents, never a verdict. Bounded (200 directory entries per environment, <code>coverage.truncated<\/code>), <code>null<\/code> for anything unreadable, <code>{ error }<\/code> per subtree.<\/li>\n<li>Snapshots: <code>create_file()<\/code> creates a NEW file with the engine owning the whole create \u2014 staged beside the target under a non-PHP name, recorded, then published with <code>link()<\/code> (atomic, no-clobber) \u2014 so no target ever exists without its record and no partial content is ever visible. Restoring the record claims the path with an atomic rename, verifies the claimed file, and removes it only while its bytes still match; a file edited since is put back and refused (<code>file_changed_since<\/code>), never deleted. The Power Pack's <code>write_file<\/code> adopts it in 0.2.4.<\/li>\n<li>Tool metadata lists an empty parameter map as <code>{}<\/code> rather than <code>[]<\/code> (SA#83).<\/li>\n<li>Generic SiteAgent mutations (batch entry, generic single update, generic rollback) renew the self-update lease between phases and heartbeat it from inside the upgrader's own sub-phases, so a slow download, unpack or install is never seized as stale, and a lost lease stops the entry rather than racing its successor (SA#80).<\/li>\n<li>Every Aura-driven mutation of SiteAgent's own files \u2014 self-update, generic single update, the batch entry, the guarded rollback \u2014 refuses on a multisite network with <code>aura_self_update_multisite_unsupported<\/code>: the update lock is per site while the plugin directory is shared (SA#79). Other plugins are unaffected.<\/li>\n<li>A rule-blocked call now says to scope the rule out of the site with <code>sites<\/code>, or release it.<\/li>\n<\/ul>\n\n<h4>2.16.2<\/h4>\n\n<ul>\n<li><code>\/status<\/code>'s door fragment carries <code>observation<\/code>, a per-site door-version witness bumped atomically by every door-state mutation (never by a mere poll) and clock-floored so a restored backup can never reissue a value it already served, so Aura can order overlapping polls by the site's own witness instead of request timestamps; <code>elementor.governor<\/code> reports the current value. The observation witness requires InnoDB for wp_options and 64-bit PHP; without them ordering falls back to Aura's own request order for that site (<code>elementor.governor<\/code> reports why via <code>observation_unsupported<\/code>: <code>engine<\/code> or <code>php32<\/code>).<\/li>\n<li><code>\/status<\/code> also accepts <code>door_observation_seen<\/code> \u2014 Aura's own last accepted observation, echoed back \u2014 so the site can bump its witness forward, clock-floored, when a restore rewound its own copy behind Aura's. Accepted for ANY non-negative integer the site's own witness could ever report (no magnitude ceiling \u2014 the witness only counts up); honoured only within a fixed headroom below the class ceiling, silently ignored (200, no bump, no error) above it.<\/li>\n<li><code>elementor.governor<\/code> reports <code>counters_as_of<\/code> beside the four <code>_30d<\/code> rolling counters, which <code>observation<\/code> does not cover; the counters themselves are <code>int|null<\/code>.<\/li>\n<li><code>interrupted<\/code> \/ <code>running<\/code> \/ <code>held<\/code> are <code>null<\/code>, not <code>[]<\/code>, when their own queue could not be read, and <code>observation<\/code> is withheld entirely whenever any read behind the fragment was unreadable.<\/li>\n<li>New <code>door_write_unsupported<\/code> reason <code>reconnect_guard_unavailable<\/code>: a governed write refuses outright on a <code>$wpdb<\/code> replacement that cannot disable reconnects, rather than risk a mutation replaying on a session this plugin no longer controls.<\/li>\n<li>Door writes report a <code>committed<\/code> tri-state (<code>true<\/code> \/ <code>false<\/code> \/ unknown) and answer a retryable 503 with <code>may_have_run: true<\/code> whenever a commit cannot be proven \u2014 claims, holds, acks, and rotations alike; <code>replay()<\/code> now forwards <code>claim()<\/code>'s own error instead of a generic one.<\/li>\n<li>Hold references and pending log-row reservations are derived from the request's own identity, so a retry after an ambiguous commit finds and reuses its own prior write instead of risking a duplicate.<\/li>\n<\/ul>\n\n<h4>2.16.1<\/h4>\n\n<ul>\n<li><code>\/status<\/code> door fragment and <code>audit_mcp_exposure<\/code>'s <code>elementor.governor<\/code> carry <code>binding<\/code>, the site's current binding generation, so Aura can label a departed client's door-log entries without inferring the generation from the rows.<\/li>\n<\/ul>\n\n<h4>2.16.0<\/h4>\n\n<ul>\n<li>Elementor MCP door governance: every write through Elementor &gt;= 4.3's official MCP server is held for approval in Aura unless an operator <code>allow<\/code> rule covers it; <code>block<\/code> refuses; every write that runs is snapshotted first on the site and recorded in a per-site door log Aura drains. New tools <code>elementor_replay_ability<\/code>, <code>snapshot_get<\/code>; new routes <code>\/aura\/v1\/door\/reject<\/code>, <code>\/aura\/v1\/door\/ack<\/code>; <code>\/status<\/code> carries <code>door<\/code>; <code>audit_mcp_exposure<\/code> carries <code>elementor.governor<\/code>. Rules gain the <code>allow<\/code> effect and the <code>design_system<\/code> \/ <code>page_create<\/code> targets.<\/li>\n<\/ul>\n\n<h4>2.15.0<\/h4>\n\n<ul>\n<li>audit_mcp_exposure reports an <code>elementor<\/code> block: Elementor &gt;= 4.3's official MCP module state, every <code>elementor_mcp_consent<\/code> row, every <code>Elementor MCP\u2026<\/code> Application Password across all users (full detail), and the other Application Passwords of edit_posts users as counts. Every list is bounded (50 \/ 50 \/ 200) with a truncation flag beside it; no usermeta value over 256 KB is decoded; a scan that fails is reported as <code>{ error }<\/code> in its place, never as an empty list. Read-only.<\/li>\n<li>aura_worker_app_password_list() accepts an optional byte bound, enforced in the same statement that returns the value.<\/li>\n<\/ul>\n\n<h4>2.14.0<\/h4>\n\n<ul>\n<li>Feature: <strong>a self-update can now undo itself.<\/strong> Before installing, SiteAgent\narchives its current build \u2014 best-effort: a site without ZipArchive or with\nan unwritable backup directory still updates, and the result says\n  backed_up: false so the caller knows this one had no way back. After\ninstalling, SiteAgent asks the new build to prove it came up. The proof is\nwritten by the build itself: a boot beacon recorded the first time the new\ncode serves a request, and a fatal beacon recorded by\na shutdown guard when the new code dies while loading. A build whose own\nrecords say it broke is rolled back to the archived one, and compiled\ncopies are asked out of the opcode cache (best-effort \u2014 a host that\nrestricts <code>opcache_invalidate()<\/code> may serve the old compiled code until its\ncache revalidates). The result reports exactly what happened (<code>backed_up<\/code>,\n  verified, <code>rolled_back<\/code>). When neither record appears, the update stands\nand says it was not verified, never guessed.<\/li>\n<li>Feature: <strong>one SiteAgent mutation at a time.<\/strong> Every path that can replace\nSiteAgent's own files \u2014 the self-update, the generic plugin update, a batch\nentry, the rollback endpoint \u2014 runs under a single per-site claim: taken by\na conditional insert, seizable only after its holder has gone silent for ten\nminutes (a request that dies never releases), and released only by its\nowner. The self-update additionally renews the claim between its phases,\nso a slow step costs the lease nothing once the next phase begins. An\noverlapping request is answered \"in progress\" and touches nothing.<\/li>\n<li>Hardening: backups follow a symlink only while its target stays inside the\nplugin folder \u2014 a link pointing out of the tree makes the backup report\nitself incomplete instead of copying unrelated files into an archive.\nRestores never delete through a symlink, root or child, and refuse to run at\nall when a link cannot be removed. A package carrying the version already\nrunning is refused, because its boot records could not be told apart from\nthe old build's. Backup filenames carry a per-operation suffix, so two\nbackups started in the same second never overwrite each other.<\/li>\n<\/ul>\n\n<h4>2.13.0<\/h4>\n\n<ul>\n<li>Feature: <strong>Aura can now disconnect a site in two phases, and the site\nrefuses changes the moment it is told to.<\/strong> When Aura sends a disconnect,\nSiteAgent records it and every mutation on the site \u2014 SiteAgent's own write\nendpoints, MCP write tools, grant-signed calls, and any WordPress REST write\nmade with the Application Password or site token of the departing connection\n\u2014 is answered <code>403 aura_site_unbound<\/code> until the site is reconnected. Reads\nkeep working, <code>\/status<\/code> keeps answering, and Aura's own ruleset endpoint\nstays reachable so the disconnect can be finished or retried.<\/li>\n<li>Feature: <strong>a departing connection's Application Password stops working\neverywhere, not just on the REST API.<\/strong> WordPress authenticates Application\nPasswords on XML-RPC as well, so a credential the disconnect could not revoke\nis now refused where WordPress decides whether it authenticates at all. Your\nown Application Passwords are untouched, and so is admin login.<\/li>\n<li>Feature: <strong>the cleanup is proven, not assumed.<\/strong> SiteAgent revokes the\nApplication Password(s) Aura minted, clears the stored ruleset and the\ngateway public key, forgets the connect bookkeeping, and deletes the site\ntoken LAST \u2014 and only when Aura says the disconnect is final. Every step is\nidempotent, runs in one fixed order, and continues past a step that failed.\nAn interrupted disconnect finishes itself on the site's next page load\n(throttled, at most once every five minutes), so a site Aura can no longer\nreach still converges.<\/li>\n<li>Feature: the answer to a disconnect now names what is still owed:\n  leftovers lists the credentials or stores this site could not prove it had\nreleased (<code>app_passwords<\/code>, <code>options<\/code>, <code>ruleset<\/code>, <code>grant_pubkey<\/code>), alongside\n  cleanup_complete. An empty list means only the shared site token was still\noutstanding; a non-empty one means Aura must keep waiting.<\/li>\n<li>Feature: <strong>the settings screen tells you when Aura disconnected the site<\/strong>\n(\"Disconnected by Aura at \u2026\") and offers <strong>Remove remaining Aura data<\/strong> \u2014 an\nadmin-initiated teardown that runs the same proven cleanup, and only clears\nthe disconnect record once everything it names, the site token included, is\ngone.<\/li>\n<li>Feature: manually connected sites (those with no gateway public key, which\ntherefore cannot verify a signed document) can be disconnected with a bare\n  { \"unbind\": true, \u2026 } body on <code>POST \/wp-json\/aura\/v2\/rules<\/code>, authenticated\nby the site token alone. A site that DOES hold a gateway key refuses the\nbare form and requires the signed envelope.<\/li>\n<li>Safety: every ruleset push now runs under the site-wide claim the connect\nflow already used, so a push, a disconnect and a reconnect can no longer\ninterleave. A site already busy answers <code>503 aura_site_busy<\/code>, which is\nretryable. A claim left behind by a killed request is taken over after two\nminutes rather than blocking the site indefinitely.<\/li>\n<li>Safety: reconnecting (magic link) and <strong>Regenerate Token<\/strong> now settle the\nprevious connection's outstanding cleanup BEFORE installing a new token, and\nrelease the disconnect record only after the replacement connection is\ninstalled and read back \u2014 so a reconnect that fails halfway leaves the site\nstill refusing the departed connection instead of quietly reviving it.<\/li>\n<li>Fix: a disconnect record damaged in the database is now REPAIRED \u2014 rebuilt\nfrom the site's own state, under the claim \u2014 and then torn down through the\nordinary path. Previously a damaged record was a permanent dead end: the\nsite refused every change and no control could clear it.<\/li>\n<li>Diagnostics: <code>\/status<\/code> reports <code>unbound: { at, site_ref }<\/code> while a\ndisconnect is outstanding, and <code>app_password_probe_unproven:\n{ count, at, owner }<\/code> when the site cannot prove an Application Password was\nrevoked \u2014 the usual reason a disconnect never finishes. Both are bounded and\ncontain no secrets.<\/li>\n<li>New error codes: <code>aura_site_unbound<\/code> (403 \u2014 the site is disconnected),\n  aura_site_busy (503 \u2014 retry), <code>aura_unbind_incomplete<\/code> (409 \u2014 something is\nstill owed; the answer lists it), <code>aura_unbind_unreadable<\/code> (409 \u2014 the\ndisconnect record could not be read, which is NOT the same as an incomplete\ncleanup), <code>aura_unbind_unrepairable<\/code> (409), <code>aura_unbind_marker_stuck<\/code>\n(500 \u2014 everything was removed but the record itself would not delete),\n  aura_unbind_marker_malformed (500), <code>aura_unbind_store_failed<\/code> (500) and\n  aura_ruleset_client_mismatch (409 \u2014 a disconnect addressed to a different\nAura client), which the bare unkeyed form now checks too.<\/li>\n<li>Compatibility: a 2.13 site that is never sent a disconnect behaves exactly\nas 2.12 did. Nothing on the site changes until Aura asks for one.<\/li>\n<\/ul>\n\n<h4>2.12.0<\/h4>\n\n<ul>\n<li>Feature: <strong>a rule can now apply to some of a client's sites instead of all\nof them.<\/strong> Aura's signed ruleset names the site each document was issued\nfor, SiteAgent stores that identity, and a rule that lists the sites it\napplies to is enforced only where it belongs. Rules that name no sites are\nclient-wide exactly as before.<\/li>\n<li>Safety: a site that cannot prove its own identity \u2014 an older record, a\ndocument issued before this field existed \u2014 enforces EVERY rule rather than\nskipping the ones it cannot place. Scoping only ever narrows on proof.<\/li>\n<li>Upgrade: the identity is recovered offline from the ruleset already stored,\nby re-verifying its signature locally. No new network traffic, and a site\nwhose ruleset has not changed since the upgrade is repaired on its next\nrequest rather than waiting for the next push.<\/li>\n<\/ul>\n\n<h4>2.11.0 and earlier<\/h4>\n\n<ul>\n<li>WordPress.org truncates a Changelog over 5,000 words, and this plugin's history is longer than that.\nThe entries for 2.11.0 and every release before it were moved out of this file verbatim and are kept in full at:\nhttps:\/\/github.com\/Digitizers\/SiteAgent\/blob\/main\/docs\/changelog-archive.md<\/li>\n<\/ul>","raw_excerpt":"Let AI update and maintain WordPress with Aura&#039;s guardrails: optional per-action approval, an audit trail, and auto-rollback on safe batch updates.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/pe.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/291225","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/pe.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/pe.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/pe.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=291225"}],"author":[{"embeddable":true,"href":"https:\/\/pe.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/benkalsky"}],"wp:attachment":[{"href":"https:\/\/pe.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=291225"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/pe.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=291225"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/pe.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=291225"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/pe.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=291225"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/pe.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=291225"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/pe.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=291225"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}