{"id":337142,"date":"2026-07-09T11:38:16","date_gmt":"2026-07-09T11:38:16","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/audiscale-connector\/"},"modified":"2026-09-30T17:35:26","modified_gmt":"2026-09-30T17:35:26","slug":"audiscale-connector","status":"publish","type":"plugin","link":"https:\/\/lmo.wordpress.org\/plugins\/audiscale-connector\/","author":23528889,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"6.3.0","stable_tag":"6.3.0","tested":"7.1.2","requires":"6.0","requires_php":"7.4","requires_plugins":null,"header_name":"AudiScale Connector","header_author":"AudiScale","header_description":"Server-side companion for AudiScale in WordPress: SEO output in the <head> (meta description, canonical, robots, Open Graph, JSON-LD), redirects, and an enumerated catalog of operations driven by AudiScale over an HMAC-signed channel. No arbitrary code execution.","assets_banners_color":"416e71","last_updated":"2026-09-30 17:35:26","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"","header_author_uri":"https:\/\/audiscale.com","rating":0,"author_block_rating":0,"active_installs":0,"downloads":669,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"5.1.1":{"tag":"5.1.1","author":"platinumwp","date":"2026-07-09 11:38:04","revision":3601417},"5.2.0":{"tag":"5.2.0","author":"platinumwp","date":"2026-08-03 20:59:53","revision":3633458},"5.3.0":{"tag":"5.3.0","author":"platinumwp","date":"2026-08-14 18:01:57","revision":3647797},"5.3.1":{"tag":"5.3.1","author":"platinumwp","date":"2026-08-16 12:38:42","revision":3649708},"5.4.0":{"tag":"5.4.0","author":"platinumwp","date":"2026-08-25 12:34:08","revision":3665321},"5.5.0":{"tag":"5.5.0","author":"platinumwp","date":"2026-08-25 16:12:22","revision":3665663},"5.6.0":{"tag":"5.6.0","author":"platinumwp","date":"2026-08-26 07:30:34","revision":3666434},"5.7.0":{"tag":"5.7.0","author":"platinumwp","date":"2026-08-26 10:19:51","revision":3666721},"5.7.1":{"tag":"5.7.1","author":"platinumwp","date":"2026-08-28 08:05:04","revision":3669999},"5.8.0":{"tag":"5.8.0","author":"platinumwp","date":"2026-08-29 20:39:36","revision":3671874},"5.9.0":{"tag":"5.9.0","author":"platinumwp","date":"2026-09-03 19:07:07","revision":3680266},"6.0.0":{"tag":"6.0.0","author":"platinumwp","date":"2026-09-28 15:29:54","revision":3717486},"6.1.0":{"tag":"6.1.0","author":"platinumwp","date":"2026-09-30 09:47:18","revision":3720853},"6.2.0":{"tag":"6.2.0","author":"platinumwp","date":"2026-09-30 16:25:15","revision":3721669},"6.3.0":{"tag":"6.3.0","author":"platinumwp","date":"2026-09-30 17:35:26","revision":3721759}},"upgrade_notice":{"6.3.0":"<p>Keeps AudiScale Connector up to date automatically (this plugin only) and adds optional remote user management, off until enabled in AudiScale and approved change by change. Passwords are never seen or set.<\/p>","6.2.0":"<p>Reads the theme&#039;s design system (presets, patterns) so AudiScale writes contents in your theme&#039;s own style, and previews AudiScale drafts in your real theme through a short-lived signed link. Nothing is published.\nCrawl log of search and AI crawlers (no IP address, no visitor data, 30 days at most, can be switched off). Consider mentioning it in your privacy policy: a suggested paragraph is in Settings \u2192 Privacy.\nIndexNow support (key file and optional automatic ping on publish, off by default) and robots \/ canonical directives now written into Yoast SEO, Rank Math or SEOPress when one is active. Nothing changes until AudiScale applies a setting.<\/p>","6.1.0":"<p>Theme updates, performance settings with preview and one-click switch-off, page-builder editing, plugin rollback, read-only file integrity, malware signature, administrator account and backup checks, and an incident lockdown (sign everyone out, force an administrator password reset). On a paired site, the installed version of a plugin is archived before each plugin update so it can be restored, and each administrator&#039;s last login date is recorded; nothing else changes until AudiScale applies a setting.<\/p>","5.1.1":"<p>First public WordPress.org release.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3633458,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3633458,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3601417,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3601417,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["5.1.1","5.2.0","5.3.0","5.3.1","5.4.0","5.5.0","5.6.0","5.7.0","5.7.1","5.8.0","5.9.0","6.0.0","6.1.0","6.2.0","6.3.0"],"block_files":[],"assets_screenshots":[],"screenshots":[]},"plugin_section":[],"plugin_tags":[5777,6487,726,1117,186],"plugin_category":[55],"plugin_contributors":[270706],"plugin_business_model":[],"class_list":["post-337142","plugin","type-plugin","status-publish","hentry","plugin_tags-meta-description","plugin_tags-open-graph","plugin_tags-redirects","plugin_tags-schema","plugin_tags-seo","plugin_category-seo-and-marketing","plugin_contributors-platinumwp","plugin_committers-platinumwp"],"banners":{"banner":"https:\/\/ps.w.org\/audiscale-connector\/assets\/banner-772x250.png?rev=3601417","banner_2x":"https:\/\/ps.w.org\/audiscale-connector\/assets\/banner-1544x500.png?rev=3601417","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/audiscale-connector\/assets\/icon-128x128.png?rev=3633458","icon_2x":"https:\/\/ps.w.org\/audiscale-connector\/assets\/icon-256x256.png?rev=3633458","generated":false},"screenshots":[],"raw_content":"<!--section=description-->\n<p>AudiScale Connector is the companion component for the hosted AudiScale service (https:\/\/audiscale.com). It unlocks the SEO actions that WordPress core and the standard REST API do not allow \u2014 most notably printing a <code>&lt;meta name=\"description\"&gt;<\/code> tag in the <code>&lt;head&gt;<\/code> without forcing you to install another SEO plugin.<\/p>\n\n<h4>Service disclosure (SaaS)<\/h4>\n\n<p>This plugin communicates with the third-party AudiScale service. <strong>No data is\nsent until you have explicitly paired<\/strong> your site from your AudiScale dashboard.\nOnce paired, AudiScale can remotely apply a <strong>fixed catalog of operations<\/strong>\n(listed below), each of which is:<\/p>\n\n<ul>\n<li>signed with HMAC-SHA256 using a per-site secret (rotatable and revocable);<\/li>\n<li>subject to a WordPress capability check (<code>manage_options<\/code>);<\/li>\n<li>logged (who, what, when, before\/after value);<\/li>\n<li>reversible where possible (draft\/revision, dry-run).<\/li>\n<\/ul>\n\n<p>The plugin <strong>never executes arbitrary code<\/strong>: there is no code-evaluation\nendpoint and no remote code download. \"Almost everything\" means an enumerated,\nhand-coded, audited catalog.<\/p>\n\n<ul>\n<li>Terms of service: https:\/\/audiscale.com\/en\/terms<\/li>\n<li>Privacy policy: https:\/\/audiscale.com\/en\/privacy<\/li>\n<\/ul>\n\n<h4>Operation catalog<\/h4>\n\n<ul>\n<li>SEO \/ <code>&lt;head&gt;<\/code>: meta description, SEO title, canonical, robots, Open Graph, Twitter cards, native sitemap exclusion<\/li>\n<li>Redirects: create \/ update \/ delete (301, 302, 410)<\/li>\n<li>Structured data: per-content JSON-LD<\/li>\n<li>Content &amp; media: field updates (via revision), draft creation, alternative text, status change (publish\/draft only), move to trash and restore from it, listing of trashed contents<\/li>\n<li>Taxonomies: read the taxonomies and terms a post type declares, assign existing terms to a content (never creates a term)<\/li>\n<li>Site (read-only): robots.txt, public settings, content inventory, theme design system (global styles presets, theme supports, block patterns)<\/li>\n<li>Draft preview: short-lived signed link rendering one draft in the real theme (never published, noindex)<\/li>\n<li>Site (footer): marked HTML block printed on <code>wp_footer<\/code> (badge install \/ removal)<\/li>\n<li>Plugins: inventory with update availability, forced update check, update of an installed plugin (the installed version is archived first), rollback to the archived version<\/li>\n<li>Performance: preconnect \/ font preload hints, Google Fonts <code>display=swap<\/code> and self-hosting, removal of emojis \/ oEmbed \/ jQuery Migrate \/ Dashicons for visitors, script defer \/ async \/ delay until interaction, per-page removal of unused scripts and styles, scripts and styles inventory, server environment report \u2014 each one reversible, previewable before it goes live, and switched off at once from the Connection tab<\/li>\n<li>Page builders: read and update a page in the builder it is edited with (Elementor, Divi, WPBakery, Beaver Builder), and create a new page directly in that builder<\/li>\n<li>Crawl log (read-only): which pages search and AI crawlers request, with the HTTP status<\/li>\n<li>Users (optional, see below): list the site's users, change a role, sign a user out, revoke application passwords, send WordPress's password-reset email, require a new password at next login, disable \/ re-enable login, delete with post reassignment<\/li>\n<li>Connector: status, pairing, audit log<\/li>\n<\/ul>\n\n<p>The plugin detects Yoast, Rank Math and SEOPress and <strong>stands down<\/strong>\nautomatically if one of them already manages the <code>&lt;head&gt;<\/code>, to avoid duplicate\ntags.<\/p>\n\n<h4>Files downloaded to your site (performance features)<\/h4>\n\n<p>Only when AudiScale asks for it through a signed operation, never while a visitor\nloads a page:<\/p>\n\n<ul>\n<li><strong>Google Fonts self-hosting<\/strong> (<code>perf.fonts.set<\/code> with <code>selfHost<\/code>): the plugin\ndownloads the Google Fonts stylesheets the site already uses from\n  fonts.googleapis.com and the font files they reference from\n  fonts.gstatic.com, into <code>wp-content\/uploads\/audiscale-fonts\/<\/code>. Visitors then\nload the fonts from your own domain, so their IP address is no longer sent to\nGoogle on each page view. No other host is contacted, and no request carries\nvisitor data.<\/li>\n<li><strong>Plugin archives<\/strong>: once the site is paired, before any plugin update (from\nAudiScale, from wp-admin or a WordPress auto-update), the installed version is\nzipped into a private folder under <code>wp-content\/uploads\/<\/code> (random folder and file\nnames, an <code>index.php<\/code> in every folder, closed to web access on Apache) so it can\nbe restored. One archive per plugin, deleted after 90 days; its SHA-256 checksum\nis verified before any restore. Nothing is archived on a site that is not paired.<\/li>\n<\/ul>\n\n<h4>Crawl log<\/h4>\n\n<p>On a paired site, the plugin records the requests of known search and AI\ncrawlers (Googlebot, Bingbot, Applebot, Amazonbot, GPTBot, OAI-SearchBot,\nChatGPT-User, PerplexityBot, ClaudeBot, CCBot, Bytespider, meta-externalagent):\nthe crawler name, whether its identity was verified, the requested path\nwithout its query string, the HTTP status and the time. <strong>No IP address, no\nUser-Agent string and nothing about human visitors is stored<\/strong>; a request that\ndoes not come from a known crawler is never written.<\/p>\n\n<p>Googlebot, Bingbot, Applebot and Amazonbot are verified the way their operators\ndocument it (reverse DNS, then forward confirmation), cached one day under a\nhashed key; a request that claims one of them and fails verification is dropped.\nThe other crawlers publish no such domain and are recorded as declared, not\nverified. The log keeps 30 days and 30,000 rows at most, can be switched off\n(and emptied) from \"AudiScale \u2192 Connection\", and AudiScale reads it once a day\nthrough the signed <code>crawl.log.read<\/code> operation. A suggested paragraph is added\nto the WordPress privacy policy guide (Settings \u2192 Privacy).<\/p>\n\n<p>The plugin never downloads or executes code: archives are only ever reinstalled\nthrough the WordPress upgrader, from files the site itself produced.<\/p>\n\n<h4>Remote user management (optional, off by default)<\/h4>\n\n<p>Nothing here happens until the site owner explicitly turns on \"user management\"\nfor this site in AudiScale, and AudiScale then asks the owner to approve each\nchange before it sends it. What AudiScale can then do, on the current site only:<\/p>\n\n<ul>\n<li>list the users: id, login, display name, email address, roles, registration\ndate, last login, two-factor status (Two Factor, Solid Security, WP 2FA or\nWordfence Login Security, when one of them is active), number of application\npasswords, and whether the account is disabled or must choose a new password\n(<code>users.list<\/code>, <code>list_users<\/code>);<\/li>\n<li>change a user's role (<code>users.role.set<\/code>, <code>promote_users<\/code>, limited to the roles\nthe connected account may itself assign);<\/li>\n<li>sign a user out everywhere (<code>users.sessions.destroy<\/code>, <code>edit_users<\/code>);<\/li>\n<li>revoke all of a user's application passwords (<code>users.app_passwords.revoke<\/code>,\n  edit_users);<\/li>\n<li>send WordPress's own password-reset email to the user\n(<code>users.password_reset.send<\/code>, <code>edit_users<\/code>);<\/li>\n<li>require a new password at the next login, and sign the user out\n(<code>users.password_reset.force<\/code>, <code>edit_users<\/code>) \u2014 lifted once the user resets\ntheir password through \"Lost your password?\";<\/li>\n<li>disable and re-enable login (<code>users.disable<\/code>, <code>users.enable<\/code>, <code>edit_users<\/code>):\na disabled account can log in neither with its password nor with an\napplication password; its content is left untouched;<\/li>\n<li>delete a user (<code>users.delete<\/code>, <code>delete_users<\/code>; on multisite, <code>remove_users<\/code>\nand the user is only removed from this site), always reassigning their posts\nto another active user of the site. A deletion cannot be undone.<\/li>\n<\/ul>\n\n<p><strong>AudiScale never sees, sends or sets a password<\/strong>: a reset always goes through\nWordPress's own email and the user's own choice. User lists are fetched on\ndemand and not stored by AudiScale.<\/p>\n\n<p>Each change is logged locally in the plugin's audit log: the operation, the\ntarget user id, the roles or flags before and after (for a deletion, the login,\nits roles and the user receiving the posts), the WordPress user AudiScale acts\nas, and the time. No email address and no password is ever logged.<\/p>\n\n<p>Safeguards, enforced by the plugin whatever AudiScale asks:<\/p>\n\n<ul>\n<li>the last administrator able to log in can be neither demoted, disabled nor\ndeleted;<\/li>\n<li>the account AudiScale is connected with cannot have its role changed, be\ndisabled, be deleted or lose its application passwords;<\/li>\n<li>on multisite, only a super admin can act on a super admin, and WordPress's own\nper-user capability checks always apply.<\/li>\n<\/ul>\n\n<p>The login block and the forced reset live in user meta\n(<code>audiscale_login_disabled<\/code>, <code>audiscale_password_reset_required<\/code>); deactivating\nor uninstalling the plugin lifts them.<\/p>\n\n<h3>External services<\/h3>\n\n<p>This plugin connects to two external services.<\/p>\n\n<h4>AudiScale<\/h4>\n\n<p>The hosted AudiScale service (https:\/\/audiscale.com), which this plugin is the\ncompanion of. Nothing is sent until an administrator pairs the site. Once paired,\nAudiScale sends signed operation requests to the site, and after an update the\nplugin sends the installed plugin version and the site URL back to AudiScale\n(one request per version).<\/p>\n\n<ul>\n<li>Terms of service: https:\/\/audiscale.com\/en\/terms<\/li>\n<li>Privacy policy: https:\/\/audiscale.com\/en\/privacy<\/li>\n<\/ul>\n\n<h4>Google Fonts<\/h4>\n\n<p>Used only when AudiScale turns on Google Fonts self-hosting (<code>perf.fonts.set<\/code> with\n    selfHost), and only during that operation \u2014 never while a visitor loads a page.\nThe site's server requests, from <code>fonts.googleapis.com<\/code>, the Google Fonts\nstylesheets the site already loads, then, from <code>fonts.gstatic.com<\/code>, the font files\nthose stylesheets reference. The requests carry the server's IP address and a\nbrowser User-Agent string (Google only serves WOFF2 files to a browser it\nrecognizes); they carry no visitor data and no site data beyond the stylesheet URLs.\nAfterwards visitors load the fonts from the site itself, so their IP address is no\nlonger sent to Google.<\/p>\n\n<ul>\n<li>Google Terms of Service: https:\/\/policies.google.com\/terms<\/li>\n<li>Google Privacy Policy: https:\/\/policies.google.com\/privacy<\/li>\n<li>Google Fonts FAQ (privacy): https:\/\/developers.google.com\/fonts\/faq\/privacy<\/li>\n<\/ul>\n\n<!--section=installation-->\n<ol>\n<li>Install and activate the plugin (from wordpress.org or by uploading the ZIP).<\/li>\n<li>From your AudiScale dashboard, start pairing: AudiScale calls <code>POST \/wp-json\/audiscale\/v1\/pair<\/code> with a secret generated on the AudiScale side.<\/li>\n<li>That's it \u2014 subsequent operations are signed with that secret.<\/li>\n<\/ol>\n\n<p>To revoke access at any time: \"Settings \u2192 AudiScale \u2192 Disconnect\", or disconnect\nthe site from AudiScale.<\/p>\n\n<!--section=faq-->\n<dl>\n<dt id=\"does%20the%20plugin%20send%20data%20without%20my%20consent%3F\"><h3>Does the plugin send data without my consent?<\/h3><\/dt>\n<dd><p>No. No communication happens until the site is paired, and pairing requires an\nadministrator (<code>manage_options<\/code>).<\/p><\/dd>\n<dt id=\"what%20happens%20if%20i%20already%20have%20an%20seo%20plugin%3F\"><h3>What happens if I already have an SEO plugin?<\/h3><\/dt>\n<dd><p>AudiScale Connector detects Yoast \/ Rank Math \/ SEOPress and prints nothing in\nthe <code>&lt;head&gt;<\/code> to avoid duplicates.<\/p><\/dd>\n<dt id=\"how%20do%20i%20revoke%20access%3F\"><h3>How do I revoke access?<\/h3><\/dt>\n<dd><p>Unpairing erases the pairing secret: the command channel becomes inert\nimmediately. It also switches off every performance setting AudiScale applied.<\/p><\/dd>\n<dt id=\"how%20do%20i%20switch%20off%20the%20performance%20optimizations%3F\"><h3>How do I switch off the performance optimizations?<\/h3><\/dt>\n<dd><p>\"AudiScale \u2192 Connection \u2192 Switch off AudiScale optimizations\" removes them at\nonce, without AudiScale. Deactivating the plugin removes them too: they are\napplied while pages load, nothing is written into your theme or plugin files.<\/p><\/dd>\n<dt id=\"my%20server%20runs%20nginx%3A%20are%20the%20plugin%20archives%20protected%3F\"><h3>My server runs nginx: are the plugin archives protected?<\/h3><\/dt>\n<dd><p>The archives live in a folder with a random name and random file names, with an\n    index.php in every folder, and an <code>.htaccess<\/code> that Apache honors. nginx ignores\n    .htaccess, so add this rule to the site's server block:<\/p>\n\n<pre><code>location ~* \/wp-content\/uploads\/audiscale-backup- { deny all; }\n<\/code><\/pre><\/dd>\n<dt id=\"can%20audiscale%20see%20or%20change%20my%20users%27%20passwords%3F\"><h3>Can AudiScale see or change my users' passwords?<\/h3><\/dt>\n<dd><p>No. Remote user management is off until you enable it for the site in AudiScale,\nand every change needs your approval there. Even then AudiScale can only send\nWordPress's own password-reset email or require a new password at the next\nlogin: the password is always chosen by the user, and is never read, sent or\nlogged.<\/p><\/dd>\n<dt id=\"what%20does%20uninstalling%20remove%3F\"><h3>What does uninstalling remove?<\/h3><\/dt>\n<dd><p>The plugin's options, its pending proposals, its audit and crawl log tables, the self-hosted\nGoogle Fonts and the plugin archives, and any login block or forced password reset it set\non user accounts. Your content, media, users and plugins are left as they are.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>6.3.0<\/h4>\n\n<ul>\n<li>New <code>connector.self_auto_update.set<\/code> operation (<code>update_plugins<\/code>): turns\nWordPress's own auto-update on or off for AudiScale Connector only, through\nthe standard per-plugin setting. The auto-update setting of your other plugins\nis never read for anything else nor changed. A constant or an\n  auto_update_plugin filter set on the site keeps the last word.<\/li>\n<li>New <code>connector.self_update.run<\/code> operation (<code>update_plugins<\/code>): updates AudiScale\nConnector itself to the latest release now, exactly like a plugin update\n(previous version archived first, reactivated after the update). Does nothing\nwhen it is already up to date.<\/li>\n<li><code>\/status<\/code> now reports whether AudiScale Connector is auto-updated\n(<code>selfAutoUpdate<\/code>).<\/li>\n<li>Remote user management on the signed channel, used only once the site owner\nenables it in AudiScale and approves each change: <code>users.list<\/code> (<code>list_users<\/code>),\n  users.role.set (<code>promote_users<\/code>), <code>users.sessions.destroy<\/code>,\n  users.app_passwords.revoke, <code>users.password_reset.send<\/code>,\n  users.password_reset.force, <code>users.disable<\/code>, <code>users.enable<\/code> (<code>edit_users<\/code>)\nand <code>users.delete<\/code> (<code>delete_users<\/code>; <code>remove_users<\/code> on multisite, where the user\nis only removed from the current site). Each also checks WordPress's per-user\ncapability, and each change is logged with roles, flags and counts only \u2014\nnever an email address or a password.<\/li>\n<li>A disabled account can log in neither with its password nor with an\napplication password. The refusal only shows after a correct password, so it\ndoes not reveal which accounts are disabled.<\/li>\n<li>Safeguards: the last administrator able to log in cannot be demoted, disabled\nor deleted; the connected account cannot have its role changed, be disabled,\nbe deleted or lose its application passwords; a deletion always reassigns the\nposts to another active user of the site; on multisite only a super admin can\nact on a super admin.<\/li>\n<li>AudiScale never sees or sets a password: resets go through WordPress's own\nemail or a new password required at the next login.<\/li>\n<\/ul>\n\n<h4>6.2.0<\/h4>\n\n<ul>\n<li>New read-only <code>site.design_system.get<\/code> operation (<code>edit_posts<\/code>): the active\ntheme's global styles presets (color palette, font sizes, font families,\nspacing sizes, content and wide widths), its theme support flags, its block\npatterns (core patterns excluded) and the synced and unsynced patterns saved\non the site. AudiScale uses them to write contents with the theme's own preset\nclasses and components instead of fixed values. Nothing is written.<\/li>\n<li>New <code>content.preview_link<\/code> operation (<code>edit_posts<\/code>, plus the right to edit the\ncontent): a signed front URL, valid 30 minutes at most, that renders one draft\nor pending content in the real theme \u2014 uncached and noindex. Without a valid\ntoken the draft stays invisible to visitors; private and scheduled contents are\nnever revealed. Nothing is published.<\/li>\n<li>Crawl log: on a paired site, requests from known search and AI crawlers are\nrecorded (crawler, verified or declared, path without query string, HTTP\nstatus, time \u2014 never an IP address or a User-Agent string). Googlebot,\nBingbot, Applebot and Amazonbot are verified by reverse then forward DNS;\nimpostors are dropped. Bounded to 30 days and 30,000 rows, switched off and\nemptied from the Connection screen, removed on uninstall, read by AudiScale\nthrough the signed <code>crawl.log.read<\/code> operation (cursor-paged, <code>manage_options<\/code>).<\/li>\n<li><code>site.robots_txt.get<\/code> now also says whether a physical robots.txt file shadows\nthe value the plugin manages.<\/li>\n<li>Suggested privacy policy text for the crawl log (Settings \u2192 Privacy).<\/li>\n<li>IndexNow: four operations on the signed channel (<code>indexnow.key.set<\/code>,\n  indexnow.state.get, <code>indexnow.auto_ping.set<\/code>, <code>indexnow.key.clear<\/code>, all\n  manage_options). The key is served as plain text at <code>\/&lt;key&gt;.txt<\/code> on your\nsite; nothing else is served. When automatic pings are on, publishing a\npublic content, or updating a published one, sends its URL to api.indexnow.org\n(Bing, Yandex, Seznam, Naver) in the background, at most once a minute per\nURL. Password-protected contents, revisions and non-public types are never\nsent. Off by default, and removed on uninstall.<\/li>\n<li><code>seo.robots.set<\/code> and <code>seo.canonical.set<\/code> now write into the active SEO\nplugin's own fields (Yoast SEO, Rank Math, SEOPress) so the directive really\nappears on the page. A change proposed in draft mode is still not applied\nuntil approved.<\/li>\n<\/ul>\n\n<h4>6.1.0<\/h4>\n\n<ul>\n<li>Read-only inside security checks, both on the signed channel with <code>manage_options<\/code>:\n  security.files.scan walks plugins, themes, mu-plugins and uploads in short\nresumable slices (about 3 seconds, 400 files at most per call, resumed from a\ncursor) and reports each code file's md5, size and date, plus malware\nsignature matches with a short excerpt. Images and other media are never read.\n  security.inside.audit reports administrator accounts (login, last login,\napplication password dates \u2014 never a password or its hash), the active backup\nplugin and its last backup date, and malware signatures in autoloaded options.<\/li>\n<li>The date of each administrator's last login is now stored in the user meta\n  audiscale_last_login (a timestamp, nothing else), so inactive administrator\naccounts can be spotted. Removed on uninstall.<\/li>\n<li>Incident lockdown, on the signed channel with <code>manage_options<\/code>:\n  security.sessions.destroy_all signs every user out (not reversible), and\n  security.admins.password_reset.force signs administrators out and refuses\ntheir next login until they choose a new password through \"Lost your\npassword?\" (<code>security.admins.password_reset.clear<\/code> lifts it). The flag is the\nuser meta <code>audiscale_password_reset_required<\/code>, removed once the password is\nreset and on uninstall. wp-config.php is never written: regenerating the\nauthentication salts stays a manual step.<\/li>\n<li>Themes: <code>themes.list<\/code> (update offered by the site's own index), <code>themes.update<\/code>\n(Theme_Upgrader, logged) and <code>themes.activate<\/code> (returns the previous theme, the\ncall that undoes it). WordPress core REST offers no theme write.<\/li>\n<li>Performance settings (<code>perf.*<\/code> operations, <code>performance<\/code> capability token):\npreconnect and font preload hints, Google Fonts <code>display=swap<\/code> and\nself-hosting, removal of emojis \/ oEmbed \/ jQuery Migrate \/ Dashicons for\nvisitors, script defer \/ async (WordPress script strategies when available) and\ndelay until the first interaction, per-page removal of unused scripts and\nstyles (<code>keepOn<\/code> keeps them on some pages, <code>onlyOn<\/code> removes them only there), scripts and styles inventory, server environment report. All of them\nare applied while pages load and stored in the plugin's own options \u2014 nothing is\nwritten into WordPress content or files, and the stock state hooks nothing.<\/li>\n<li>Every setting supports <code>publishMode<\/code> (draft by default), <code>dryRun<\/code>, returns the\nexact call that undoes it, and is logged. A draft is only rendered for a\nrequest carrying a signed, 30-minute preview token (never cached, never\nindexed, identified by an <code>X-AudiScale-Preview<\/code> header); a visitor never sees\nit. <code>perf.promote<\/code> \/ <code>perf.discard<\/code> \/ <code>perf.reset<\/code> manage the lifecycle.<\/li>\n<li>A preview token opens one page only (the URL it was issued for), lasts five\nminutes by default (30 at most), and never applies to the REST API, feeds or\nadmin-ajax. Optimizations are never applied inside page-builder editors and\npreviews, the customizer, or for logged-in users who can edit content.<\/li>\n<li>The preview also renders pending page-builder documents and pending content\ndrafts, so a change can be checked for breakage before it goes live. A pending\nElementor or Beaver Builder document is rendered with the CSS\/JS generated from\nit \u2014 under preview-only file names and an in-memory copy of the builder caches,\nso the files and cache rows visitors get are never touched \u2014 and the response\nsays in <code>X-AudiScale-Preview-Assets<\/code> whether that generation worked.<\/li>\n<li>New \"Switch off AudiScale optimizations\" button in the Connection tab.\nUnpairing switches them off too.<\/li>\n<li>Elementor and Beaver Builder documents are stored in post meta, which WordPress\nnever filters: they are written only for a user allowed to post unfiltered HTML\n(<code>unfiltered_html<\/code>), as Elementor itself requires \u2014 including when a pending\nchange is approved from the \"Pending\" screen.<\/li>\n<li>Page builders (<code>builder.data.get<\/code>, <code>builder.data.update<\/code>,\n  builder.data.discard): read and update a page in the builder it is edited\nwith \u2014 Elementor, Divi, WPBakery, Beaver Builder \u2014 with each builder's own\nstorage, a draft\/approval cycle, and a refusal when the page changed since it\nwas read. Oxygen and Bricks are detected and refused. Beaver layouts are read\nwithout instantiating any class other than <code>stdClass<\/code>.<\/li>\n<li>WPBakery: writing a page rebuilds <code>_wpb_shortcodes_custom_css<\/code> exactly as\nWPBakery does when a page is saved in its editor, so design options apply\nwithout re-saving the page.<\/li>\n<li>Elementor: <code>builder.data.get<\/code> and <code>perf.environment.get<\/code> report whether the\nFlexbox Container feature is active; a document built with containers is\nrefused while it is not (Elementor would render nothing).<\/li>\n<li><code>content.create_draft<\/code> accepts <code>builder<\/code> + <code>builderData<\/code> to create a new page\ndirectly in the site's builder (<code>content.create_draft.builder<\/code> token).\nUnchanged without them.<\/li>\n<li><code>plugins.update<\/code> archives the installed version first, and so does every\nplugin update WordPress runs (wp-admin, auto-updates). New <code>plugins.rollback<\/code>\noperation reinstalls that archive and reactivates the plugin; a rollback can\nitself be rolled back. Archives live in a private folder, one per plugin,\npurged after 90 days, 1 GB cap.<\/li>\n<li><code>content.update_fields<\/code> in draft mode returns <code>pendingHash<\/code>, the fingerprint\nthe preview echoes.<\/li>\n<\/ul>\n\n<h4>5.9.0<\/h4>\n\n<ul>\n<li>New <code>content.untrash<\/code> operation (<code>edit_posts<\/code> capability): restores a page or a\npost from the WordPress trash. Until now AudiScale could move a content to the\ntrash but never take it back out, so a content it had trashed had to be restored\nby hand in wp-admin. It calls <code>wp_untrash_post()<\/code> and nothing else, and replaying\nit on a content that is not in the trash reports \"nothing to do\" instead of an\nerror, exactly as <code>content.trash<\/code> does on a content already trashed.<\/li>\n<li>The response says which status the content <strong>actually<\/strong> came back with. Since\nWordPress 5.6 a restored content is set to <code>draft<\/code> \u2014 not to the status it had\nbefore it was trashed \u2014 unless the site filters <code>wp_untrash_post_status<\/code>. So the\noutcome cannot be deduced from the WordPress version, and the plugin reads it\nback after the write: <code>after<\/code> is the observed status, <code>previousStatus<\/code> the one\nthe content had when it was trashed, and <code>restoredToPreviousStatus<\/code> says whether\na page that was published is readable again or is now a draft.<\/li>\n<li>New <code>content.trashed.list<\/code> operation (<code>edit_posts<\/code> capability, read-only): lists\nthe trashed contents with their id, title, type, trash date and previous status.\nA trashed content answers no URL any more, so this is the only way to name the\ncontent to restore. Media are out of scope, like everywhere else: WordPress does\nnot trash an attachment, it deletes it and its files for good.<\/li>\n<\/ul>\n\n<h4>5.8.0<\/h4>\n\n<ul>\n<li>New <code>taxonomy.list<\/code> operation (<code>edit_posts<\/code> capability, read-only): lists the\ntaxonomies that apply to a post type \u2014 categories, tags, and any custom one the\nsite declares \u2014 and, on request, one bounded page of their terms. AudiScale\nreads it so it can only ever propose a term that exists.<\/li>\n<li>New <code>content.terms.assign<\/code> operation (<code>manage_options<\/code> capability): files a page\nor a post under existing terms. It <strong>adds<\/strong> by default: assigning one category\nno longer removes the others, and clearing the existing terms requires spelling\nout <code>mode: \"replace\"<\/code>. It <strong>never creates a term<\/strong>: an unknown one is refused and\nreported, so a near-duplicate (\"Actualit\u00e9s\" on a site holding \"Actualit\u00e9\") is\nnever silently created. The complete previous set of terms is written to the\naudit log, and the recorded result is re-read after the write, so it shows the\ndefault category WordPress reassigns to a post left without one.<\/li>\n<li>New <code>content.trash<\/code> operation (<code>delete_posts<\/code> capability): moves a page or a post\nto the WordPress trash, from where you can restore it. Until now a draft created\nfrom AudiScale could not be removed from AudiScale. Nothing is ever deleted\npermanently: if the trash is disabled on your site the operation is refused\ninstead of destroying the content, and media are out of scope \u2014 WordPress does\nnot trash an attachment, it deletes it and its files for good.<\/li>\n<li>The <code>promote<\/code> operation now also approves a page body prepared in draft mode\n(<code>field: \"content\"<\/code>). It previously handled SEO fields only, so a body drafted by\nAudiScale could be approved from WordPress and nowhere else.<\/li>\n<li><code>content.set_page_template<\/code> now honours <code>publishMode<\/code> like every other content\noperation: by default the new template waits on the \"Pending\" screen instead of\nbeing applied to the live site immediately.<\/li>\n<li><code>content.status.set<\/code> and <code>content.trash<\/code> now refuse to unpublish or trash the\npage used as the front page or as the posts page, which would leave the site\nwithout an entry point.<\/li>\n<li>Every content operation now checks that its target is an editable content \u2014 not a\nrevision, a menu item or a theme template \u2014 and that the current user may edit it.\nCustom content types declaring their own capabilities without asking WordPress to map\nthem keep working: for those, the check falls back to the generic content capability\nrather than a primitive capability no role holds.<\/li>\n<li><strong>Behaviour change<\/strong> \u2014 a URL is now resolved to a content deterministically, and\nnever guessed. The plugin reads, in order: the id declared in the URL (<code>?p=<\/code>,\n  ?page_id=, <code>?attachment_id=<\/code> \u2014 the three WordPress itself honours), then the\nsite's own rewrite rules, then the site root, and refuses otherwise with\n  audiscale_url_needs_post_id (404). The slug fallback that used to search the\npath against every content type is <strong>removed<\/strong>: a slug read off a crawled URL\nnames the right content only by luck, and every way it named a wrong one ended in\na success on content nobody aimed at \u2014 on a shop holding both a page and a product\nnamed \"contact\", the write landed on whichever the database returned last. A URL\nwhose path WordPress does not resolve now needs an explicit content id; AudiScale\nresolves the target on its side and sends it.<\/li>\n<li>A URL pointing at another domain is refused on the whole resolution, not on one\nbranch of it: an id declared in the URL does not depend on the domain, so\n\"https:\/\/autre.com\/x\/?p=12\" used to trash the post 12 of YOUR site, and the root\nof any other domain used to serve your home page. Sites whose public domain is not\nthe one WordPress stores (an origin behind a CDN, a migrated domain, one domain\nper language) keep working: the host the request came in on is trusted alongside\n  home_url(), <code>site_url()<\/code> and the <code>audiscale_trusted_hosts<\/code> filter, and\ninternationalized domains, punycode and a trailing dot compare equal.<\/li>\n<li>An id declared in a URL now has to name editorial content in an editable state:\na revision, an auto-draft, a trashed content or a media is refused instead of\nwritten to. <code>post<\/code> and <code>post_id<\/code> are no longer read as content ids \u2014 WordPress\nhonours neither, while plugins use both as ordinary parameters, so a crawled\n\"\/recherche\/?post_id=2\" resolved to the post 2 instead of the search page.<\/li>\n<li>Fix: on a site installed in a subdirectory (\"https:\/\/site.fr\/blog\"), the address\n\"https:\/\/site.fr\/\" was treated as the site root and served the WordPress home\npage, although this WordPress does not answer that address at all.<\/li>\n<li>AudiScale is told, through a dedicated capability token, that this build honours\n  publishMode on <code>content.set_page_template<\/code> \u2014 the operation id alone is the same as\nin 5.7.1, where the template was always applied immediately.<\/li>\n<li>Fix: assigning a term whose slug is a number (\"2024\" on a taxonomy of years) was\nrefused as an unknown term, because a digits-only value was only ever read as a\nterm id.<\/li>\n<li>Fix: a draft write no longer files a WordPress revision of a body it did not touch.<\/li>\n<li>Refusals now carry a stable machine-readable <code>code<\/code> (and the details behind it,\nsuch as the templates the theme really declares) next to the message, so AudiScale\ncan tell one refusal from another and suggest the right fix. The <code>error<\/code> field is\nunchanged.<\/li>\n<\/ul>\n\n<h4>5.7.1<\/h4>\n\n<ul>\n<li>Fix: a URL carrying a content id (<code>?page_id=<\/code>, <code>?p=<\/code>, <code>?post=<\/code>, <code>?post_id=<\/code>) was\nread as the site root, because its path is <code>\/<\/code>. Depending on the site it either\nfailed with <code>audiscale_no_front_page<\/code> or \u2014 with a static front page \u2014 applied the\noperation to the home page instead of the content actually targeted. The declared\nid is now read first, and an id matching no content fails instead of falling back\nto the home page. Affects every operation resolving a URL, SEO ones included.<\/li>\n<\/ul>\n\n<h4>5.7.0<\/h4>\n\n<ul>\n<li>New <code>media.upload<\/code> operation (<code>upload_files<\/code> capability): sideloads a remote\nimage or video into the media library and returns its attachment id, so a page\nAudiScale drafts can reference a real attachment instead of an external URL.\nThe downloaded bytes are type-checked (no SVG), the source URL is validated\nagainst WordPress' own SSRF guard, the transfer is capped at 25 MB (declared\nsize checked before the download, response size capped during it), and nothing\nexisting is overwritten. This operation is why the plugin requires WordPress\n6.0 or later: <code>download_url()<\/code> fetches through <code>wp_safe_remote_get()<\/code>, which\nre-validates every redirect target against the same SSRF guard rather than\nfollowing it blindly.<\/li>\n<li>New <code>blocks.validate<\/code> operation (<code>edit_posts<\/code> capability, read-only): parses\nblock markup the way the editor does and reports content sitting outside any\nblock, unknown block types, or markup that does not survive a parse\/serialize\nround-trip. AudiScale calls it before proposing a draft, so a page never lands\nin the editor showing \"This block contains unexpected or invalid content\".<\/li>\n<li>New <code>site.layout_options<\/code> operation (<code>edit_posts<\/code> capability, read-only):\nlists the page templates the active theme actually declares, and whether the\ntheme renders wide\/full alignments. AudiScale reads it before offering any\nlayout change, so it can only ever propose a layout the theme can render.<\/li>\n<li>New <code>content.set_page_template<\/code> operation (<code>manage_options<\/code> capability):\nchanges the template of an existing page or post, targeted by <code>post_id<\/code> or\n  url. The value must be a template the active theme declares (or <code>default<\/code>);\nanything else is refused rather than written, and the before\/after is recorded\nin the audit log.<\/li>\n<li><code>content.create_draft<\/code> and <code>content.update_fields<\/code> accept an optional\n  page_template, validated the same way. On creation the template is checked\nbefore the post is inserted, so a refused template never leaves an orphan\ndraft behind.<\/li>\n<\/ul>\n\n<h4>5.6.0<\/h4>\n\n<ul>\n<li>New <code>plugins.check_updates<\/code> operation (<code>update_plugins<\/code> capability): purges the\n  update_plugins transient and re-runs WordPress' update check immediately, with\nno staleness guard, then reports how many updates are now visible. WordPress only\nrefreshes that cache about twice a day and its upgrader refuses any plugin the\ncache does not list, so updating a freshly released version from AudiScale failed\nfor hours for no real reason. AudiScale now calls this before retrying, and the\nupdate goes through. <code>plugins.list<\/code> is unchanged.<\/li>\n<\/ul>\n\n<h4>5.5.0<\/h4>\n\n<ul>\n<li>After a plugin update or activation, the connector announces its own version to\nAudiScale over the existing HMAC-signed channel, so the version AudiScale shows\nand gates features on is right immediately instead of at the next manual check.\nOne non-blocking request, only when the site is paired, and nothing is sent\nbeyond the version number and the site URL.<\/li>\n<\/ul>\n\n<h4>5.4.0<\/h4>\n\n<ul>\n<li>New <code>site.footer_snippet.set<\/code> \/ <code>.clear<\/code> \/ <code>.get<\/code> operations: store a marked\nHTML block (a link and an image, no script) and print it on <code>wp_footer<\/code>. Used\nby AudiScale for the one-click \"Verified\" badge install. The block lives in an\noption, not in the theme's <code>footer.php<\/code>, so a theme update cannot wipe it, and\nre-posing the same marker replaces the block instead of stacking a second one.<\/li>\n<\/ul>\n\n<h4>5.3.1<\/h4>\n\n<ul>\n<li><code>plugins.update<\/code> no longer leaves a plugin deactivated after a successful\nupdate: a failed reactivation is now reported (<code>reactivationFailed<\/code>) instead\nof being swallowed, and a main file renamed by the update is re-resolved\nbefore reactivating.<\/li>\n<\/ul>\n\n<h4>5.3.0<\/h4>\n\n<ul>\n<li>New <code>seo.sitemap.exclude<\/code> operation: flags a content so the native\nwp-sitemap.xml skips it (reversible with <code>excluded: false<\/code>; draft\/approve flow\nsupported). No effect while a third-party SEO plugin provides the sitemap.<\/li>\n<li>New <code>content.status.set<\/code> operation: switches a content between \"publish\" and\n\"draft\" only (strict whitelist \u2014 no trash, no private, no scheduling),\n  publish_pages capability, audited before\/after.<\/li>\n<li>Declared compatibility with WordPress 7.1.<\/li>\n<\/ul>\n\n<h4>5.2.0<\/h4>\n\n<ul>\n<li>SEO title and meta description now write into the meta key of the SEO plugin\nthat actually renders the page (Yoast, Rank Math, SEOPress) instead of the\nplugin's own key. While one of those is active this plugin does not print its\nown tags \u2014 so a value you approved was stored but never appeared on the site.\nApproved changes now take effect. Sites with no third-party SEO plugin are\nunaffected.<\/li>\n<li>The approval screen reads the current value from that same key, so the\n\"current\" column no longer shows empty against a field that is set.<\/li>\n<li>The audit log records the key really written.<\/li>\n<\/ul>\n\n<h4>5.1.1<\/h4>\n\n<ul>\n<li>First public WordPress.org release: English readme and service disclosure,\npackaging hygiene, and internationalization (text domain loading).<\/li>\n<li><code>plugins.update<\/code> now keeps the target plugin active after upgrading it (the\nWordPress upgrader deactivates during the file swap and does not restore it on a\nprogrammatic call).<\/li>\n<\/ul>\n\n<h4>5.0.0<\/h4>\n\n<ul>\n<li>Actionable <code>security.*<\/code> operation family: hardenings applied at the PHP\nruntime, without touching wp-config and always reversible in a single call\n(file editor, REST user enumeration, XML-RPC + pingbacks, HTTP headers\nHSTS\/nosniff\/X-Frame-Options\/Referrer-Policy, minor core auto-updates,\nversion masking, <code>?author=N<\/code> scan blocking).<\/li>\n<li>Each op accepts <code>dryRun<\/code> (simulation without writing) and logs the before\/after\nvalue for an undo via the inverse op. <code>security.state.get<\/code> exposes the current\nstate. Disconnecting (<code>unpair<\/code>) resets all hardenings.<\/li>\n<li><code>hsts<\/code> is set only if HTTPS is actually enforced (anti-lockout guard).<\/li>\n<\/ul>\n\n<h4>4.0.0<\/h4>\n\n<ul>\n<li>New <code>security.audit<\/code> operation (read-only, <code>manage_options<\/code> capability):\nfree hardening snapshot (versions, configuration flags, admin accounts, core\nintegrity via wordpress.org checksums). No secret is returned.<\/li>\n<\/ul>\n\n<h4>3.0.0<\/h4>\n\n<ul>\n<li>New plugin-management operations: <code>plugins.list<\/code> (read, <code>activate_plugins<\/code>\ncapability) and <code>plugins.update<\/code> (live, irreversible update, <code>update_plugins<\/code>\ncapability).<\/li>\n<\/ul>\n\n<h4>2.0.0<\/h4>\n\n<ul>\n<li><code>content.update_fields<\/code> op: URL targeting + <code>fields<\/code> object\n(title\/excerpt\/content), respecting <code>publishMode<\/code>.<\/li>\n<li>The body is written faithfully (no destructive filtering on our side,\nGutenberg block delimiters preserved); <code>filtered<\/code> flag if the user lacks the\n  unfiltered_html capability.<\/li>\n<li><code>draft<\/code> mode for content: the proposal is queued and approved from the\n\"AudiScale pending\" screen.<\/li>\n<\/ul>\n\n<h4>1.1.0<\/h4>\n\n<ul>\n<li><code>draft<\/code> mode: proposed values are stored \"pending\" without changing the live\nrender.<\/li>\n<li>Approval surface: \"Pending AudiScale change\" metabox on the edit screen + a\ncentral screen (Settings \u2192 AudiScale pending) to approve\/reject (nonce +\ncapability).<\/li>\n<li><code>promote<\/code> operation to approve a field from AudiScale (consent equivalent to\n  direct).<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Initial version: SEO <code>&lt;head&gt;<\/code> output, redirects, structured data, HMAC-signed\noperation catalog, audit log.<\/li>\n<\/ul>","raw_excerpt":"Server-side companion for AudiScale: SEO  tags, redirects, and an enumerated catalog of operations over an HMAC-signed channel.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/lmo.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/337142","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/lmo.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/lmo.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/lmo.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=337142"}],"author":[{"embeddable":true,"href":"https:\/\/lmo.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/platinumwp"}],"wp:attachment":[{"href":"https:\/\/lmo.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=337142"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/lmo.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=337142"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/lmo.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=337142"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/lmo.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=337142"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/lmo.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=337142"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/lmo.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=337142"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}