{"id":355276,"date":"2026-08-23T12:19:18","date_gmt":"2026-08-23T12:19:18","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/bulwarden\/"},"modified":"2026-09-17T14:06:42","modified_gmt":"2026-09-17T14:06:42","slug":"bulwarden","status":"publish","type":"plugin","link":"https:\/\/en-ca.wordpress.org\/plugins\/bulwarden\/","author":23550935,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.29.1","stable_tag":"1.29.1","tested":"7.1","requires":"6.0","requires_php":"7.4","requires_plugins":null,"header_name":"Bulwarden","header_author":"Justin Gloeckl","header_description":"Site agent for remote health monitoring, malware scanning, zero-knowledge encrypted backups, and remote core\/plugin\/theme updates, driven by a Bulwarden hub you connect it to.","assets_banners_color":"665fca","last_updated":"2026-09-17 14:06:42","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/bulwarden.net","header_author_uri":"","rating":0,"author_block_rating":0,"active_installs":10,"downloads":617,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.13.7":{"tag":"1.13.7","author":"bulwarden","date":"2026-08-23 12:18:57","revision":3661809},"1.14.0":{"tag":"1.14.0","author":"bulwarden","date":"2026-08-26 13:12:02","revision":3667070},"1.15.0":{"tag":"1.15.0","author":"bulwarden","date":"2026-08-26 19:29:26","revision":3667716},"1.16.0":{"tag":"1.16.0","author":"bulwarden","date":"2026-08-27 13:52:58","revision":3668981},"1.17.0":{"tag":"1.17.0","author":"bulwarden","date":"2026-08-31 14:04:24","revision":3674412},"1.17.1":{"tag":"1.17.1","author":"bulwarden","date":"2026-08-31 15:28:58","revision":3674563},"1.18.0":{"tag":"1.18.0","author":"bulwarden","date":"2026-08-31 17:01:58","revision":3674665},"1.19.0":{"tag":"1.19.0","author":"bulwarden","date":"2026-09-01 18:49:39","revision":3676675},"1.22.2":{"tag":"1.22.2","author":"bulwarden","date":"2026-09-12 19:10:42","revision":3693045},"1.23.0":{"tag":"1.23.0","author":"bulwarden","date":"2026-09-14 07:41:01","revision":3694704},"1.25.0":{"tag":"1.25.0","author":"bulwarden","date":"2026-09-14 14:08:03","revision":3695357},"1.26.0":{"tag":"1.26.0","author":"bulwarden","date":"2026-09-14 18:41:29","revision":3695780},"1.27.0":{"tag":"1.27.0","author":"bulwarden","date":"2026-09-15 13:14:22","revision":3697027},"1.27.1":{"tag":"1.27.1","author":"bulwarden","date":"2026-09-15 14:11:46","revision":3697107},"1.27.2":{"tag":"1.27.2","author":"bulwarden","date":"2026-09-15 16:18:35","revision":3697350},"1.27.3":{"tag":"1.27.3","author":"bulwarden","date":"2026-09-15 16:46:03","revision":3697402},"1.28.0":{"tag":"1.28.0","author":"bulwarden","date":"2026-09-15 19:57:13","revision":3697644},"1.28.2":{"tag":"1.28.2","author":"bulwarden","date":"2026-09-16 19:25:25","revision":3699174},"1.29.1":{"tag":"1.29.1","author":"bulwarden","date":"2026-09-17 14:06:42","revision":3700416}},"upgrade_notice":{"1.23.0":"<p>The malware scan gains eighteen checks written against real backdoors, and eight\nexisting checks stop reporting honest code. If your provider&#039;s hub had refused an\nupdate as infected, it will go through after this.<\/p>","1.22.2":"<p>Nothing on your site changes. This release exists to correct what the plugin&#039;s\nown listing says about it, which had fallen behind several releases.<\/p>","1.22.1":"<p>Fixes backups of sites larger than 4 GB, which failed while verifying the\nfinished archive. If you have such a site, run a backup after updating.<\/p>","1.22.0":"<p>A restore that cannot finish no longer leaves your site half-restored, and a\nrestore that ran into trouble now says so instead of reporting success. Nothing\nyou had set up changes; no action needed.<\/p>","1.21.0":"<p>The activity log now records the updates your provider installs on this site,\nand three other things it was missing. Nothing else changes; no action needed.<\/p>","1.20.0":"<p>A plugin that appears on your site without WordPress.org publishing it now\nstarts a malware scan by itself, within seconds, instead of waiting for the next\nscheduled one. Nothing is blocked or deleted; no action needed.<\/p>","1.19.0":"<p>Adds German, server-configuration checks, email delivery monitoring and a\ncancel button for a stuck scan, and stops a slow update on shared hosting being\nkilled part-way. Nothing you had set up changes; no action needed.<\/p>","1.18.0":"<p>The malware scan now recognises backdoors that arrive dressed as an ordinary\nplugin, not just obfuscated webshells. Nothing you had set up changes; no action\nneeded.<\/p>","1.17.1":"<p>Fixes false &quot;a WordPress core file is missing&quot; and &quot;no longer matches the\nofficial release&quot; findings on the readme and licence files a non-English\nWordPress installs under a translated name.<\/p>","1.16.0":"<p>The support card on your dashboard can now show your provider&#039;s telephone\nnumber, if they have set one in their hub. Nothing you had set up changes; no\naction needed.<\/p>","1.15.0":"<p>The malware scan now counts the plugins on disk itself instead of taking\nWordPress&#039; word for what is installed, so a plugin that has hidden itself from\nthe plugin list is reported &mdash; and checked against WordPress.org like every\nother one. Must-use plugins are now listed too: they run on every request and\nthe admin gives you no way to switch one off, so the scan reports what is there.\nNothing you had set up changes; no action needed.<\/p>","1.14.0":"<p>Adds a &quot;Request help&quot; button to the dashboard support card, and lets your hub\nfind and clear the files old backups, restores and migrations left behind.\nNothing you had set up changes; no action needed.<\/p>","1.13.7":"<p>Identical to 1.13.5 and 1.13.6 in behaviour. Nothing on your site changes.<\/p>","1.13.6":"<p>Identical to 1.13.5 in behaviour. It exists so the plugin directory&#039;s listing\nand its screenshots describe the same version; nothing on your site changes.<\/p>","1.13.5":"<p>Corrections to the Bulwarden screen rebuilt in 1.13.4. Nothing changes in how\nthe plugin works; no action needed.<\/p>","1.13.4":"<p>The Bulwarden screen has been rebuilt around what you actually came to it for.\nNothing you had set up changes; no action needed.<\/p>","1.13.3":"<p>Bulwarden moves out of the Settings submenu into its own entry in the WordPress\nadmin menu. Nothing else changes; no action needed.<\/p>","1.13.2":"<p>Fixes a false &quot;a WordPress core file no longer matches the official release&quot;\nfinding on <code>wp-includes\/version.php<\/code> &mdash; raised on any site whose display\nlanguage differs from the WordPress package it was installed from.<\/p>","1.13.1":"<p>Fixes a false &quot;a plugin file no longer matches the version published on\nWordPress.org&quot; finding on plugins WordPress.org publishes more than one copy of.<\/p>","1.13.0":"<p>Your hub can now see and finish an update that changed this site&#039;s files but\nnever completed &mdash; a pending database upgrade or a leftover maintenance\nlock. Restores can also put back only the database, leaving files untouched.<\/p>","1.11.0":"<p>Must be installed alongside the matching hub update: the two sign requests to\neach other and the signing form changed on both sides at once.<\/p>","1.9.0":"<p>Stored data moved to a new prefix and there is no automatic migration &mdash;\nre-pair the site with your hub after updating, and re-enter the backup\npassphrase. Existing backups still need the old passphrase to decrypt.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3661811,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3661811,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3661811,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3661811,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.13.7","1.14.0","1.15.0","1.16.0","1.17.0","1.17.1","1.18.0","1.19.0","1.22.2","1.23.0","1.25.0","1.26.0","1.27.0","1.27.1","1.27.2","1.27.3","1.28.0","1.28.2","1.29.1"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3693044,"resolution":"1","location":"assets","locale":"","width":2080,"height":628},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3661811,"resolution":"2","location":"assets","locale":"","width":1120,"height":1468},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3693044,"resolution":"3","location":"assets","locale":"","width":2080,"height":2560},"screenshot-4.png":{"filename":"screenshot-4.png","revision":3661811,"resolution":"4","location":"assets","locale":"","width":2080,"height":1308},"screenshot-5.png":{"filename":"screenshot-5.png","revision":3661811,"resolution":"5","location":"assets","locale":"","width":2080,"height":1150},"screenshot-6.png":{"filename":"screenshot-6.png","revision":3693044,"resolution":"6","location":"assets","locale":"","width":1120,"height":778}},"screenshots":{"1":"The Bulwarden screen at a glance: whether the site is connected, whether\nbackups are encrypted, when the last backup and scan ran, and how current\nthe scanner's signature list is.","2":"Connecting the site: the connect string you paste into your hub, and the\nAPI credentials behind it.","3":"Backups: a run in progress showing the phase it has reached, its log and a\ncancel button, above the encryption passphrase and the folders left out of\nthe archive.","4":"The malware scan: what the last one found, grouped by kind, and how much of\nthe site could be checked against the files WordPress.org published.","5":"The activity log: an append-only record of credential changes, backups,\nrestores, scans and repairs, including the ones the hub triggered.","6":"The dashboard support card &mdash; who maintains this site, how to reach\nthem, and the <em>Request help<\/em> button that files a ticket with them directly."}},"plugin_section":[262246],"plugin_tags":[151,732,1184,5603,600],"plugin_category":[52,54],"plugin_contributors":[277095],"plugin_business_model":[],"class_list":["post-355276","plugin","type-plugin","status-publish","hentry","plugin_section-dashboard-widgets","plugin_tags-backup","plugin_tags-maintenance","plugin_tags-malware","plugin_tags-monitoring","plugin_tags-security","plugin_category-performance","plugin_category-security-and-spam-protection","plugin_contributors-bulwarden","plugin_committers-bulwarden"],"banners":{"banner":"https:\/\/ps.w.org\/bulwarden\/assets\/banner-772x250.png?rev=3661811","banner_2x":"https:\/\/ps.w.org\/bulwarden\/assets\/banner-1544x500.png?rev=3661811","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/bulwarden\/assets\/icon-128x128.png?rev=3661811","icon_2x":"https:\/\/ps.w.org\/bulwarden\/assets\/icon-256x256.png?rev=3661811","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/bulwarden\/assets\/screenshot-1.png?rev=3693044","caption":"The Bulwarden screen at a glance: whether the site is connected, whether\nbackups are encrypted, when the last backup and scan ran, and how current\nthe scanner's signature list is."},{"src":"https:\/\/ps.w.org\/bulwarden\/assets\/screenshot-2.png?rev=3661811","caption":"Connecting the site: the connect string you paste into your hub, and the\nAPI credentials behind it."},{"src":"https:\/\/ps.w.org\/bulwarden\/assets\/screenshot-3.png?rev=3693044","caption":"Backups: a run in progress showing the phase it has reached, its log and a\ncancel button, above the encryption passphrase and the folders left out of\nthe archive."},{"src":"https:\/\/ps.w.org\/bulwarden\/assets\/screenshot-4.png?rev=3661811","caption":"The malware scan: what the last one found, grouped by kind, and how much of\nthe site could be checked against the files WordPress.org published."},{"src":"https:\/\/ps.w.org\/bulwarden\/assets\/screenshot-5.png?rev=3661811","caption":"The activity log: an append-only record of credential changes, backups,\nrestores, scans and repairs, including the ones the hub triggered."},{"src":"https:\/\/ps.w.org\/bulwarden\/assets\/screenshot-6.png?rev=3693044","caption":"The dashboard support card &mdash; who maintains this site, how to reach\nthem, and the <em>Request help<\/em> button that files a ticket with them directly."}],"raw_content":"<!--section=description-->\n<p>Bulwarden is the <strong>site agent<\/strong>, installed on each managed WordPress site. It\nexposes a signed REST API (<code>jgm\/v1<\/code>) that a <strong>Bulwarden hub<\/strong> calls to read\nhealth and security data, run checks and trigger updates. The hub is a separate\napplication \u2014 it is not part of this plugin \u2014 and <strong>this plugin talks to no hub\nat all until you pair it with one<\/strong>. On its own it sends nothing anywhere.<\/p>\n\n<p>It is built for the person who is responsible for a site they did not build:\nan agency looking after forty of them, or an owner who wants one place that\nanswers \"is this thing all right?\" without logging into it.<\/p>\n\n<h4>What it reports<\/h4>\n\n<ul>\n<li><strong>Site health<\/strong> \u2014 WordPress, PHP and plugin versions, pending updates, disk\nusage, and WordPress' own Site Health tests, collected in one snapshot.<\/li>\n<li><strong>A full plugin and theme inventory<\/strong>, with each item's version, whether it\nis active, whether an update is waiting, and whether it came from\nWordPress.org at all \u2014 which is what lets a hub match it against a CVE feed.<\/li>\n<li><strong>Must-use plugins<\/strong>, which WordPress runs on every single request with\nnothing activating them and no way to switch one off from the admin. Nothing\nelse in wp-admin lists them properly, and dropping a file there is the tidiest\nway to install a backdoor.<\/li>\n<li><strong>Configuration-level security checks<\/strong> with a posture score \u2014 file editing,\ndirectory listing, XML-RPC, debug output, and the rest of the settings that\nquietly decide how exposed a site is.<\/li>\n<li><strong>Unfinished updates.<\/strong> An update that already replaced a site's files can\nstill fail to finish, and neither state clears itself: a pending database\nschema upgrade that every administrator is sent to and the public site hides\ncompletely, or a leftover <code>.maintenance<\/code> lock from an updater that died\nmid-copy. Both are reported, and both can be finished from the hub by running\nexactly what WordPress itself would have run.<\/li>\n<li><strong>Email delivery.<\/strong> WordPress keeps no record of the mail a site sends: when\na contact form stops delivering or an order confirmation never arrives, the\nerror is raised and thrown away. This counts what was attempted, what failed\nand why, and reports which transport is doing it \u2014 PHP's <code>mail()<\/code>, an SMTP\nserver, or the plugin that configured one. No part of any message is stored:\nnot the recipient, not the subject, not the body.<\/li>\n<\/ul>\n\n<h4>What it finds<\/h4>\n\n<ul>\n<li><strong>A malware scan in three layers.<\/strong> Every core and WordPress.org file is\nverified against its <em>published<\/em> checksums, so a modified file is found\nwhether or not anybody has written a signature for what was put in it; then\nevery file is read for the code patterns webshells and injected backdoors\nactually use. Findings carry the file, the line and a short excerpt.<\/li>\n<li><strong>Backdoors that arrive dressed as an ordinary plugin<\/strong> \u2014 a forged\nadministrator login, a privileged action gated on a key baked into the file,\nan account created from hard-coded credentials, users hidden from the users\nlist. A plugin absent from WordPress.org has no published files to check it\nagainst, so without this a hostile one surfaced only as <em>unverifiable<\/em>.<\/li>\n<li><strong>A plugin hiding itself.<\/strong> Malware that conceals a plugin does it by\nanswering the question before WordPress can \u2014 and the hidden plugin is then\nalso the one plugin whose files are never checked. The scan counts what is on\ndisk itself and compares.<\/li>\n<li><strong>Server-configuration files.<\/strong> <code>.htaccess<\/code>, <code>php.ini<\/code> and <code>.user.ini<\/code> are\nthe one part of a WordPress install that changes what the <em>server<\/em> does\nrather than what WordPress does, and nothing in wp-admin shows them. A file\nset to run before every request, a rule that makes uploaded images execute as\nPHP, a redirect that only fires for search engines: all reported, with the\nline. Nothing is ever changed for you \u2014 a good share of those directives are\nwritten deliberately by a host or a security plugin, and the point is that\nyou get to decide which.<\/li>\n<li><strong>Leftover files.<\/strong> An interrupted backup, a finished restore and a migration\nplugin all leave things behind and none of them clean up: a\n  wp-config.php.bak the web server hands out as plain text, a <code>database.sql<\/code>\nin the web root, an installer that overwrites the site with no login. Found,\nmeasured, and removable from the hub \u2014 and the <em>site<\/em> decides what may be\ndeleted, so a stale list can only ever be refused.<\/li>\n<li><strong>Signatures that keep up.<\/strong> The scan's pattern list can be updated by your\nprovider without updating this plugin, so a new detection reaches the site in\nhours rather than waiting for a release to travel a whole fleet. A site that\nhas never been paired scans with the list this plugin ships.<\/li>\n<li><strong>A scan that starts itself.<\/strong> When a plugin WordPress.org does not publish\nappears in the plugins or must-use folder, the site starts a scan within\nseconds and tells your provider what set it off, instead of waiting for the\nnext scheduled one. Fake plugins are how most WordPress break-ins arrive, and\na scan is a cheap thing to be wrong about: nothing is blocked, quarantined or\ndeleted \u2014 the site looks, and your provider decides.<\/li>\n<\/ul>\n\n<h4>What it does<\/h4>\n\n<ul>\n<li><strong>Remote core, plugin and theme updates<\/strong>, wrapped in maintenance mode so\nvisitors never see a half-updated site \u2014 and taken off the host's PHP time\nlimit, so a core update on shared hosting finishes instead of being killed\npart-way.<\/li>\n<li><strong>Zero-knowledge encrypted backups.<\/strong> The archive is built <em>and encrypted on\nthis server<\/em>, under a passphrase that is never transmitted anywhere;\nonly the ciphertext is uploaded. Nobody \u2014 including whoever runs the hub \u2014\ncan read a backup without that passphrase.<\/li>\n<li><strong>Restores that fail safely.<\/strong> The same thing in reverse, and it can put back\nthe database alone if that is all that broke. Every table is rebuilt beside\nthe live one and swapped in only once it is complete, so a restore stopped by\na timeout or a server limit leaves the database exactly as it was rather than\nhalf-replaced \u2014 and picks itself back up where it stopped. What could not be\ndone is counted and reported rather than discarded, so a restore that ran into\ntrouble says so instead of reporting success.<\/li>\n<li><strong>Page tracing.<\/strong> Your provider can ask this site to render one of its own\npublic pages and report which file produced each line of the HTML \u2014 core, a\nplugin, the theme, a must-use plugin. It answers the question nothing in\nWordPress answers: <em>which<\/em> plugin adds the Google Fonts tag, or the tracking\nscript, or the markup nobody can find a setting for. The page is never\naltered to do it.<\/li>\n<li><strong>WP-Cron on a schedule that actually runs.<\/strong> A site with no visitors runs no\ncron, which is how scheduled posts, order emails and licence checks quietly\nstop.<\/li>\n<li><strong>A support card on the dashboard<\/strong> naming whoever maintains the site, how to\nreach them, and a <em>Request help<\/em> button that files a ticket with them\ndirectly \u2014 attached to the right site, without the owner having to explain\nwhich one.<\/li>\n<li><strong>An activity log on the site itself<\/strong>, append-only, recording everything that\nchanged this site or sent part of it somewhere \u2014 and which side asked. Updates\ninstalled by your provider and anything that failed with them, backups,\nrestores, scans, repairs, deleted leftovers, a file read off the server, a\ntraced page, and every change to the credentials, the passphrase or the\nprovider details. WordPress records what changed and never who asked, and the\nanswer belongs on your own server rather than only in somebody else's console.<\/li>\n<\/ul>\n\n<h4>Pairing<\/h4>\n\n<p>On activation the plugin provisions an API key + secret. Copy the <strong>connect\nstring<\/strong> from the <strong>Bulwarden<\/strong> entry in the WordPress admin menu and paste\nit into the hub's \"Connect a site\" form. Your provider's support details are\nthen pushed to the site automatically by the hub and appear on the dashboard.<\/p>\n\n<h4>Security<\/h4>\n\n<p>Hub \u2192 client requests are authenticated with the per-site API key and an\nHMAC-SHA256 signature over the method, route, timestamp, a single-use nonce and\nthe body. The nonce is what makes a signature good exactly once: it is recorded\nwhen the request arrives and refused if it comes back, so a captured request\ncannot be replayed. Timestamps outside a 5-minute window are rejected outright,\nwhich is also how long a nonce has to be remembered for. The secret never\ntravels on the wire \u2014 it lives only in this plugin and in the hub webapp.<\/p>\n\n<h3>Privacy<\/h3>\n\n<p>Bulwarden is a client agent: on its own it sends data nowhere. Pairing this\nsite with a hub (yours, or your maintenance provider's) is an explicit,\nopt-in action \u2014 you choose when to copy the connect string from the Bulwarden\nscreen in the WordPress admin menu and hand it to that hub. Nothing is\ntransmitted before that.<\/p>\n\n<p>Once paired, data only moves in these cases:<\/p>\n\n<ul>\n<li><strong>Health, inventory, and security checks.<\/strong> The paired hub reads these from\nthis site's signed REST API when it polls or when a check is requested \u2014\nWordPress\/plugin\/theme versions and update status, a full plugin &amp; theme\ninventory, disk usage, and the configuration-level security posture score.<\/li>\n<li><strong>Backups<\/strong>, only when triggered (the Settings screen's \"Back up now\"\nbutton, or a request from the paired hub): the full site is archived and\nencrypted on this server with a passphrase that is never transmitted\nanywhere, then the ciphertext is uploaded to the hub. Only someone who has\nthe passphrase can decrypt a backup \u2014 the hub operator cannot read its\ncontents.<\/li>\n<li><strong>Malware scan results<\/strong>, only when a scan is triggered (admin- or\nhub-initiated): findings (type, severity, file path, line, and a short\nexcerpt) are sent to the hub that requested the scan.<\/li>\n<li><strong>Finishing an interrupted update<\/strong>, when the paired hub asks. Nothing is\nsent in that exchange; the site runs WordPress' own database upgrade, or\ndeletes an expired <code>.maintenance<\/code> lock file, and reports the outcome.<\/li>\n<li><strong>Server-configuration files<\/strong>, when the paired hub asks: the contents of\nthis site's <code>.htaccess<\/code>, <code>php.ini<\/code> and <code>.user.ini<\/code> files, so the hub can show\nwhat is in them and when they last changed. These are configuration, not\ncontent \u2014 no post, page, media file or user data is read.<\/li>\n<li><strong>Email counters<\/strong>, as part of the health snapshot: how many messages this\nsite attempted and how many failed over the last seven days, the error text\nthe mail server returned, and which transport is sending. <strong>No part of any\nmessage is recorded<\/strong> \u2014 not the recipient, not the subject, not the body.<\/li>\n<li><strong>A traced page<\/strong>, only when the hub asks for one: the HTML of a single\npublic page of this site, exactly as an anonymous visitor would receive it,\nplus the file paths and line numbers that produced it. No file contents are\nread and no logged-in view is ever rendered.<\/li>\n<\/ul>\n\n<p>Independently of any hub pairing, this plugin verifies WordPress core and\nwordpress.org-hosted plugin files by comparing local file hashes against the\npublic checksum APIs at <code>api.wordpress.org<\/code> and <code>downloads.wordpress.org<\/code>.\nThose requests carry only what identifies the release to look up \u2014 the\nWordPress version and locale, and each plugin's slug and version. No file\ncontents, hashes, URLs or user data are sent: the reference checksums come\nback and the comparison happens here, on this server.<\/p>\n\n<p>Bulwarden does not call home on activation, does not collect analytics, and\nstores data on no server this site hasn't been explicitly connected to. The\nhub itself is a separate service outside this plugin's control; consult your\nprovider for how it handles the data described above.<\/p>\n\n<h3>External services<\/h3>\n\n<p>This plugin connects to the services below. Nothing here runs on activation.<\/p>\n\n<h4>Your Bulwarden hub<\/h4>\n\n<p>Bulwarden is the site-side agent for a <strong>hub<\/strong> \u2014 the management application an\nagency or site owner runs to watch and maintain a fleet of WordPress sites. The\nhub is a separate product and is not part of this plugin.<\/p>\n\n<p>The default hosted hub is operated by Bulwarden at https:\/\/bulwarden.net\/ \u2014\nterms of use: https:\/\/bulwarden.net\/terms\/ , privacy policy:\nhttps:\/\/bulwarden.net\/privacy\/ . A hub can also be self-hosted, or run by the\nmaintenance provider you buy from, in which case this plugin talks to that\nprovider's server instead and their terms and privacy policy apply.<\/p>\n\n<p><strong>No connection to any hub exists until you make one.<\/strong> You pair a site by\ncopying the connect string from the Bulwarden screen in the WordPress admin\nmenu and pasting it into the hub; before that the plugin contacts no hub at\nall, and it never discovers or chooses one on its own. Once paired, data moves only in these cases:<\/p>\n\n<ul>\n<li><strong>When the hub asks for status.<\/strong> It reads this site's signed REST API and\nreceives WordPress\/plugin\/theme versions and update status, the full plugin\nand theme inventory, disk usage, the results of WordPress' own Site Health\ntests, and the configuration-level security posture score.<\/li>\n<li><strong>When a backup runs<\/strong> (you press \"Back up now\", or the hub requests one).\nThe site is archived and encrypted here, with a passphrase that is never\ntransmitted, and only the ciphertext is uploaded. The hub operator cannot\nread a backup's contents without that passphrase.<\/li>\n<li><strong>When a malware scan runs<\/strong> (started by you or by the hub). The findings \u2014\ntype, severity, file path, line number and a short excerpt \u2014 are sent to the\nhub that asked for the scan.<\/li>\n<li><strong>When the hub pushes your provider's support details<\/strong> \u2014 their name, logo,\nsupport address and accent colour \u2014 on connect and whenever they change\nthem. Nothing is sent from the site in that exchange.<\/li>\n<\/ul>\n\n<p>Every one of those requests is authenticated with this site's own API key and\nan HMAC-SHA256 signature; the shared secret never travels over the wire.<\/p>\n\n<h4>api.wordpress.org and downloads.wordpress.org<\/h4>\n\n<p>The malware scan verifies WordPress core and wordpress.org-hosted plugins\nagainst their published checksums, using WordPress.org's own public APIs at\n    https:\/\/api.wordpress.org\/core\/checksums\/1.0\/ and\n    https:\/\/downloads.wordpress.org\/plugin-checksums\/. This happens whenever a\nscan runs, whether or not the site is paired with a hub.<\/p>\n\n<p>These requests identify only the release being looked up: the WordPress version\nand locale, and each installed plugin's slug and version. No file contents,\nhashes, site URL or user data are sent \u2014 the reference checksums are returned\nand compared locally.<\/p>\n\n<p>These are WordPress.org services, covered by the WordPress.org privacy policy:\nhttps:\/\/wordpress.org\/about\/privacy\/<\/p>\n\n<!--section=installation-->\n<p>Bulwarden is one half of a pair: this plugin runs on the site, and a <strong>hub<\/strong>\nruns somewhere else and manages it. Installing the plugin on its own does\nnothing until you pair it with a hub.<\/p>\n\n<ol>\n<li>Install and activate the plugin as you would any other &mdash; upload it under\nPlugins &rarr; Add New &rarr; Upload Plugin, or install it from the directory.<\/li>\n<li>Go to <strong>Bulwarden<\/strong> in the WordPress admin menu. The plugin generated an\nAPI key and secret for this site on activation; the screen shows a single\n<strong>connect string<\/strong> that carries the site name, URL and both credentials.<\/li>\n<li>Copy that string and paste it into your hub's \"Connect a site\" form. That is\nthe whole pairing step &mdash; there is nothing to type on this side, and no\nhub address to configure here.<\/li>\n<li>If you want encrypted backups, set a <strong>backup passphrase<\/strong> on the same\nscreen. It never leaves this server, and it is required to restore. Keep it\nsomewhere you will still have it after the disaster you are backing up\nagainst &mdash; nobody, including your hub operator, can recover a backup\nwithout it.<\/li>\n<\/ol>\n\n<h4>Do I need a hub?<\/h4>\n\n<p>Yes, for anything beyond installing it. The hub is separate software: use the\nhosted one, run your own, or use the one your maintenance provider runs. Until\nthe site is paired, this plugin contacts nothing and does nothing.<\/p>\n\n<h4>Upgrading<\/h4>\n\n<p>Updates install like any other plugin. Your credentials, passphrase and settings\nsurvive an update; nothing needs re-pairing.<\/p>\n\n<!--section=faq-->\n<dl>\n<dt id=\"does%20this%20plugin%20send%20my%20site%27s%20data%20anywhere%20on%20its%20own%3F\"><h3>Does this plugin send my site's data anywhere on its own?<\/h3><\/dt>\n<dd><p>No. Before you pair it with a hub it contacts nothing at all, and it never\ndiscovers or chooses a hub by itself. After pairing, data moves only for the\nthings listed under Privacy below &mdash; health checks, backups, malware scan\nfindings &mdash; and only to the hub you paired with.<\/p><\/dd>\n<dt id=\"can%20the%20hub%20operator%20read%20my%20backups%3F\"><h3>Can the hub operator read my backups?<\/h3><\/dt>\n<dd><p>No, provided you set a passphrase. The archive is encrypted <strong>on this server<\/strong>\nwith a key derived from your passphrase, and only the ciphertext is uploaded.\nThe passphrase is never transmitted, so a hub holds bytes it cannot decrypt.\nThe same property is why nobody can recover a lost passphrase for you.<\/p><\/dd>\n<dt id=\"what%20happens%20if%20i%20deactivate%20or%20delete%20the%20plugin%3F\"><h3>What happens if I deactivate or delete the plugin?<\/h3><\/dt>\n<dd><p>The site stops answering its hub, which the hub will notice and report as the\nagent going unreachable. Deleting the plugin removes its settings, its audit-log\ntable and its working directories (see <code>uninstall.php<\/code>); backups already stored\non a hub are unaffected.<\/p><\/dd>\n<dt id=\"is%20the%20plugin%20available%20in%20german%3F\"><h3>Is the plugin available in German?<\/h3><\/dt>\n<dd><p>Yes. Everything the plugin puts on screen &mdash; the settings page, the support\ncard on the dashboard, every status message and every scan finding &mdash; is\ntranslated, and WordPress picks it up from your site's own language setting with\nnothing to configure. Both German locales are covered: <em>Deutsch<\/em> and <em>Deutsch\n(Sie)<\/em>. The wording is formal throughout, because these screens are read by the\nowner of the site rather than by the agency that maintains it.<\/p><\/dd>\n<dt id=\"does%20it%20work%20on%20multisite%3F\"><h3>Does it work on multisite?<\/h3><\/dt>\n<dd><p>The plugin runs per site. A network-wide database upgrade is reported but not\nperformed &mdash; core's own network upgrade screen walks every site in the\nnetwork, and that is where it belongs.<\/p><\/dd>\n<dt id=\"why%20does%20the%20malware%20scan%20flag%20a%20file%20i%20know%20is%20fine%3F\"><h3>Why does the malware scan flag a file I know is fine?<\/h3><\/dt>\n<dd><p>The file-integrity check compares every core and WordPress.org-hosted file\nagainst the checksums WordPress.org published for that exact release. A caching\nor optimisation plugin that rewrites a stylesheet produces the same mismatch a\nbackdoor does, which is why \"this file no longer matches what was published\" is\nreported as a fact rather than a verdict, and why non-executable files are\nwarnings rather than critical findings. Your hub can show you the actual diff.<\/p><\/dd>\n<dt id=\"what%20does%20it%20do%20to%20my%20site%27s%20performance%3F\"><h3>What does it do to my site's performance?<\/h3><\/dt>\n<dd><p>Nothing on page loads: there is no front-end code path. Backups and malware\nscans are real work and run in the background across WordPress cron ticks, to a\ntime budget, so they do not block visitors. Health checks are answered on demand\nwhen the hub asks.<\/p><\/dd>\n<dt id=\"which%20php%20and%20wordpress%20versions%20are%20supported%3F\"><h3>Which PHP and WordPress versions are supported?<\/h3><\/dt>\n<dd><p>PHP 7.4 or newer and WordPress 6.0 or newer. Backups additionally need the\n    sodium extension (encryption) and either <code>zlib<\/code> or the <code>zip<\/code> extension\n(archiving); the Settings screen tells you if either is missing.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.29.1<\/h4>\n\n<ul>\n<li>Maintenance: coding-standard clean-up in the one-click sign-in code and\nthe settings defaults. No behaviour changes.<\/li>\n<\/ul>\n\n<h4>1.29.0<\/h4>\n\n<ul>\n<li>New: <strong>one-click sign-in from your maintenance provider.<\/strong> Your provider can\nopen this site's admin as an administrator without a password, over the same\nsigned connection it already uses to update and back up the site. Each\nsign-in uses a link that works exactly once, for one minute, for one named\nadministrator, and is written to the activity log with the account it used.\nA switch on the Bulwarden screen, beside the credentials, turns it off \u2014 and\nturning it off closes the route, not only the button.<\/li>\n<\/ul>\n\n<h4>1.28.2<\/h4>\n\n<ul>\n<li>Fix: <strong>the progress of a file restore is now reported correctly when it\nfinishes.<\/strong> The step that writes a backup's files back ended by reporting\nthat it had written none of them &mdash; a completed restore showed\n\"0 of 93,680 files, 0%\" beside a count of the files it had just written.\nThe two figures were also counted differently: the total included the\nbackup's database dump and its description alongside the files, so it could\nnever be reached however many files were restored. It now counts the\nbackup's files, and every one of them is reported either as written or as\ndeliberately left alone &mdash; this site's own wp-config.php, Bulwarden\nitself, and WordPress' drop-ins are never overwritten, and your hub now says\nso instead of leaving the difference unexplained.<\/li>\n<\/ul>\n\n<h4>1.28.1<\/h4>\n\n<ul>\n<li>Fix: <strong>a restore no longer spends most of its time waiting to be asked to\ncarry on.<\/strong> A restore runs in rounds, and at the end of each one the plugin\nasked WordPress' scheduler to come straight back for the next \u2014 except that\nthe request was made from inside the scheduler, which refuses it. So the work\nonly continued when a visitor happened to arrive, or after a three-minute\nsafety timer, and on a quiet site \u2014 which is most sites being restored \u2014 a\nrestore took hours of waiting for minutes of work. On sites where WordPress'\nown scheduler is switched off in favour of a real cron job, which is the\nusual advice for a site that matters, nothing continued the restore at all\nbetween those cron runs. Each round now asks for the next one directly.<\/li>\n<\/ul>\n\n<h4>1.28.0<\/h4>\n\n<ul>\n<li>New: <strong>a banner on a site that has the plugin but is not connected to\nanyone.<\/strong> Until a site is paired with a maintenance hub the plugin does\nnothing at all \u2014 no backups, no scans, nothing watched \u2014 and from the\ndashboard that is indistinguishable from a plugin quietly working. Anyone who\ncan install plugins now sees a notice saying so, with the way to connect the\nsite one click away, and it can be put off for a month at a time. The support\nwidget on the dashboard no longer says Bulwarden maintains a site that nobody\nhas agreed to maintain.<\/li>\n<li>New: <strong>a way to ask Bulwarden for help when there is no maintenance provider\nat all.<\/strong> The Request help button on the dashboard writes to whoever looks\nafter the site, which is no use to somebody who installed this and has nobody.\nThe plugin's own settings screen now carries a short form that reaches\nBulwarden directly, and says exactly what it sends: the site's address, its\nWordPress and PHP versions and the reply address typed into it. Nothing else,\nand no keys.<\/li>\n<li>New: <strong>the hub is told whether a backup encryption passphrase has been set<\/strong> \u2014\nthe fact that there is one, never the passphrase itself, which stays on this\nsite and is never sent anywhere. Without one every backup stops before it\nstarts, and an agency previously found that out from a failed run at three in\nthe morning rather than from the setup nobody finished.<\/li>\n<li>New: <strong>five more malware detections<\/strong>, from a study of 400,000 production\nwebservers (Kasturi et al., <em>Mistrust Plugins You Must<\/em>): code that redefines\na built-in PHP function instead of loading the real one, a function name\nassembled from fragments and then called, a file written to disk, made\nexecutable and run, code that deletes the file it ran from, and the branding\n\"nulled\" plugin marketplaces stamp into the paid plugins they give away with\nsomething added.<\/li>\n<\/ul>\n\n<h4>1.27.3<\/h4>\n\n<ul>\n<li>Fixed: <strong>a restore of a large backup no longer leaves the site stuck showing\n\"Briefly unavailable for scheduled maintenance\".<\/strong> Downloading and decrypting\na backup take as long as they take, and a round of work that had spent its\nwhole time doing that went on to start restoring the database anyway \u2014 which\nbegins by switching the site into maintenance mode. It then managed a single\ninstruction before stopping for the round, so the maintenance mode bought\nnothing; and because WordPress checks for maintenance mode before it loads\nany plugin, a round that did not live long enough to switch it back off took\nthe site's own timer down with it \u2014 the timer being the only thing that could\nstart the next round. The site sat unavailable, and the restore sat still,\nuntil WordPress itself gave up on the maintenance flag ten minutes later. A\nround that is out of time now stops cleanly between phases, and maintenance\nmode is switched off the moment the database work pauses rather than at the\nvery end of the round.<\/li>\n<\/ul>\n\n<h4>1.27.2<\/h4>\n\n<ul>\n<li>Fixed: <strong>restoring a backup onto a different site now rewrites its addresses\nto <em>this<\/em> site's.<\/strong> With \"rewrite addresses\" ticked, the restore replaced the\nbackup's web address with the address it read from the site \u2014 but it read it\nafter the backup's database was already in place, so it was reading the\nbackup's own address back. A backup moved to a new domain either had nothing\nrewritten at all, leaving the restored site pointing at the domain it was\ncopied from, or had every address in the database rewritten from <code>http:\/\/<\/code> to\n  https:\/\/ on that old domain. The restore reported success in both cases.\nThis site's addresses are now recorded before the restore starts and used as\nthe target; if they could not be recorded, the addresses are left alone and\nthe restore says so instead of guessing.<\/li>\n<li>Fixed: a backup now records the site's own stored web address rather than the\naddress of the internal request that happened to start it, so the restore\nsearches for the address that is really in the backup.<\/li>\n<\/ul>\n\n<h4>1.27.1<\/h4>\n\n<ul>\n<li>Fixed: <strong>a long restore of a backup that predates this plugin now finishes.<\/strong>\nRestoring the database replaces the list of active plugins with the backup's,\nand a backup taken before this plugin was installed does not list it. On a\nsite big enough to need several rounds of work, the round after the swap\nloaded WordPress without the plugin, so nothing was left to carry on and the\nrestore sat half-done until the hub gave up. The plugin now puts itself back\nthe moment the table is swapped in, in the same round.<\/li>\n<li>Changed: <strong>a restore never writes WordPress' drop-in files<\/strong>\n(<code>object-cache.php<\/code>, <code>db.php<\/code>, <code>advanced-cache.php<\/code> and the rest of that\nfamily in <code>wp-content<\/code>). They are loaded before any plugin and are tied to\nthe server they were written on \u2014 a cache server address, database hosts,\nabsolute paths \u2014 so a backup's copy restored onto another server took the\nwhole site down before anything could repair it. The copy already on the site\nis kept, and the restore log says which files were left alone.<\/li>\n<\/ul>\n\n<h4>1.27.0<\/h4>\n\n<ul>\n<li>Fixed: <strong>a large restore no longer stops part-way through the database.<\/strong>\nRestoring the database replaces the WordPress options table, and the\nrestore's own progress record and its \"carry on\" timer were rows in that\ntable \u2014 so on a site big enough to need several rounds of work, the round\nthat swapped the table in was the last one that ever ran. The site sat\nhalf-restored, the dashboard showed the last progress it had heard of, and\nif the backup came from another site, the hub could no longer reach this one\nat all. The restore now puts its own state and this site's own credentials\nback the moment the table is swapped in, and carries on.<\/li>\n<li>Fixed: each round of a long restore now starts the next one immediately\ninstead of waiting for a timer, which roughly halves the wall-clock time of a\nrestore that spans many rounds \u2014 and no longer depends on a visitor arriving\nto trigger it.<\/li>\n<li>Improved: <strong>restoring a large database is many times faster.<\/strong> Backups now\nwrite rows in batches rather than one statement per row, and the restore\nreplays them inside transactions, so a database that used to trickle in at\nabout a thousand rows a second now goes back at the speed of the disk.\nBackups made by earlier versions restore exactly as before.<\/li>\n<li>Improved: a restore interrupted by the hosting provider (a killed process, a\ndropped database connection) now resumes exactly where its last saved\nprogress left off. Previously the rows written since the last save were\nreplayed on top of themselves, which on a bad day was enough errors to\nabandon the restore.<\/li>\n<li>Improved: a restore that resumes after a pause keeps the database session\nsettings the backup asked for (character set, foreign-key checks, the rule\nthat keeps a row with id 0 from being renumbered).<\/li>\n<li>Improved: two rounds of one restore can no longer run at the same time.<\/li>\n<li>Improved: the hub's restore page now says when it could not ask the site for\nprogress, and why, rather than showing the last answer as if it were live.<\/li>\n<li>Developer note: <code>BULW_RESTORE_TICK_BUDGET<\/code> (seconds) shortens a restore's\nper-round budget so that a multi-round restore can be reproduced on a small\ntest site.<\/li>\n<\/ul>\n\n<h4>1.26.0<\/h4>\n\n<ul>\n<li>New: <strong>the scan now reports program files stored on your site.<\/strong> A malware scan\nreads code \u2014 PHP, JavaScript, page templates \u2014 so an installer or a program\nsitting in your media library was invisible to it. That is the commonest thing\na compromised WordPress site is actually used for: not defacing the site, but\nquietly hosting a file that is mailed out to other people, until the domain is\nflagged by Google Safe Browsing or the host suspends the account. These are now\nlisted on the security tab.<\/li>\n<li>New: a program stored under a name that says otherwise \u2014 a Windows or Linux\nprogram saved as <code>.jpg<\/code>, <code>.pdf<\/code> or <code>.mp4<\/code> in the uploads folder \u2014 is reported as\na critical finding. There is no innocent reason for one, and it is exactly how\nsuch a file is kept out of sight.<\/li>\n<li>New: a program file found inside <code>wp-admin<\/code> or <code>wp-includes<\/code> is reported as\ncritical. WordPress itself ships none, in any language.<\/li>\n<li>New: every one of these findings carries a SHA-256 checksum of the file, shown\nnext to it in your provider's dashboard. That is the part your provider can act\non: it identifies the file itself, so it can be checked against a malware\ndatabase and recognised if the same file turns up on another site.<\/li>\n<li>Note: an ordinary program file elsewhere on the site is reported for\ninformation only. It never marks the site as infected and never triggers an\nalert \u2014 a business that distributes its own software has every right to store\none, and the scan says so rather than crying wolf.<\/li>\n<\/ul>\n\n<h4>1.25.1<\/h4>\n\n<ul>\n<li>Fix: <strong>a restore no longer hands this site another site's identity.<\/strong> WordPress\nkeeps this plugin's settings in the database, so restoring a backup made on a\n<em>different<\/em> site replaced this site's connection credentials with that site's \u2014\nand from that moment your provider's hub could not reach this site at all: no\nhealth checks, no cron, no scans, no backups. Putting it right meant logging in\nand pairing the site again by hand. The credentials, the hub address and the\nbackup passphrase are now carried across a restore and put back the instant the\ndatabase is in place. Everything else in the backup's settings is restored as\nbefore.<\/li>\n<li>Fix: the backup passphrase was replaced the same way, so the next backup was\nencrypted with a passphrase recorded for a different site \u2014 something nobody\nwould discover until the day they needed it.<\/li>\n<li>Fix: a backup no longer carries its own \"restore in progress\" marker, which\ncould drop one run's state onto a site part-way through another.<\/li>\n<\/ul>\n\n<h4>1.25.0<\/h4>\n\n<ul>\n<li>New: <strong>a backup can now be restored onto a site whose database tables are named\ndifferently.<\/strong> WordPress puts a short prefix in front of every one of its table\nnames, and it is rarely the same on two installs &mdash; so restoring a backup onto\na staging copy, or onto a site being rebuilt after a break-in, used to be refused\noutright. Your provider can now ask for the tables to be renamed as they are\nrestored, which leaves the site reading its own names and needs no change to\nwp-config.php at all. Everyone's role and personal settings are moved across with\nthem, so nobody loses their login.<\/li>\n<li>Change: <strong>the message shown when the names do not match no longer recommends\nsomething harmful.<\/strong> It used to suggest editing wp-config.php to match the backup.\nOn a site that is up, that stops WordPress finding its own database, takes the\nsite down, and stops this plugin running &mdash; which removes the only thing able to\nperform the restore. The message now explains the two safe ways forward instead.<\/li>\n<\/ul>\n\n<h4>1.24.1<\/h4>\n\n<ul>\n<li>Fix: <strong>a restore that stopped explained itself in punctuation codes.<\/strong> The messages\nthis site sends your provider when a restore cannot go ahead &mdash; the reason it\nstopped, and the database errors behind it &mdash; were encoded for a web page here\nbefore being sent, and then encoded a second time by the page that displayed them.\nEvery apostrophe and quotation mark in the one line meant to explain the failure\narrived as a run of digits and semicolons, including the table names in a database\nerror, which left the explanation unreadable exactly when it was needed. The\nmessages are now sent as written.<\/li>\n<\/ul>\n\n<h4>1.24.0<\/h4>\n\n<ul>\n<li>New: <strong>a restore can now be watched while it runs.<\/strong> Putting a site back can\ntake an hour on a large one, and until now your provider's hub learned nothing\nat all until it had finished &mdash; a spinner, and then an answer. This site now\nreports every step of a restore as it goes: checking, downloading, decrypting,\nthe database, the files, the addresses, and the finish, each with how far\nthrough it is and how long it has taken, alongside the run log as it is\nwritten.<\/li>\n<li>Change: a step that will not run says so from the start, and says why &mdash; a\ndatabase-only restore is not quietly \"about to\" restore your files.<\/li>\n<\/ul>\n\n<h4>1.23.0<\/h4>\n\n<ul>\n<li>New: <strong>the malware scan knows eighteen more kinds of backdoor.<\/strong> They were\nwritten against real ones: the plugins compromised on wordpress.org in June\n2024 and April 2026, the two backdoored plugins found there in 2026, and the\nfake plugins that plant a hidden administrator, rewrite <code>wp-cron.php<\/code>, take a\nrole from the request, or unpack code fetched from a server nobody published.\nEach check pairs the dangerous action with the thing that makes it\nillegitimate, so an honest plugin doing the same work is not reported.<\/li>\n<li>Fix: <strong>eight existing checks reported honest code as malware.<\/strong> A code comment\nmentioning a shell command, a full-width video frame, a compressed setting, an\nembedded font, a temporary-login plugin creating its account from variables,\nand a plugin's own test files were each being flagged. Measured against the\n250 most popular plugins and WordPress core, those checks now report nothing\nthere. Your provider's hub refuses to install an update it considers infected,\nso this also unblocks updates it was wrongly refusing.<\/li>\n<\/ul>\n\n<h4>1.22.2<\/h4>\n\n<ul>\n<li>Change: <strong>nothing on your site changes in this release.<\/strong> It exists because the\nplugin's description here had fallen behind what the plugin actually does, and\nthe directory shows that text from the released version rather than from the\nlatest one &mdash; so correcting it takes a release.<\/li>\n<li>Change: three features that shipped in 1.20.0, 1.21.0 and 1.22.0 were described\nonly in this changelog and not in the description anybody reads first: the\nactivity log on your own site, the scan the site starts for itself when a plugin\nnobody published appears, and what a restore now does when it cannot finish.<\/li>\n<li>Change: the security section described how requests between this site and your\nprovider are signed, and had been describing the form used before 1.11.0. The\nsignature has covered a single-use value per request since then, which is what\nmakes a captured request useless rather than merely short-lived.<\/li>\n<li>Change: the screenshots in the directory listing were taken nine releases ago,\nbefore the support card had a <em>Request help<\/em> button or a telephone number. They\nare re-taken from this version.<\/li>\n<\/ul>\n\n<h4>1.22.1<\/h4>\n\n<ul>\n<li>Fix: <strong>backups of sites larger than 4 GB could not be completed.<\/strong> Past that size the\narchive was written in a form no unpacker can read, so the check that runs on every\nfinished archive rejected it and the backup was reported as failed with \"the finished zip\ncould not be re-opened (code 21)\". Smaller sites were never affected. On a site whose\nhosting is missing PHP's <code>zip<\/code> extension that check cannot run, so an affected backup may\nhave been stored instead of failing &mdash; those cannot be restored, and should be\nreplaced with a fresh backup after updating.<\/li>\n<\/ul>\n\n<h4>1.22.0<\/h4>\n\n<ul>\n<li>Improve: <strong>a restore that cannot finish no longer leaves your site half-restored.<\/strong> Each\ntable is now rebuilt alongside the live one and swapped in only once it is complete, so a\nrestore interrupted by a timeout, a server limit or an error leaves your database exactly\nas it was instead of partly replaced. Long restores also pick themselves back up where\nthey stopped rather than starting over, and your site is put into maintenance mode only\nwhile the database is actually being written.<\/li>\n<li>Improve: <strong>a restore now says when something went wrong.<\/strong> Failed database statements are\ncounted and reported instead of being discarded, and a restore that could not create or\nreplace your tables stops immediately and explains why rather than finishing and\nreporting success. Your provider's dashboard shows the difference.<\/li>\n<li>Improve: <strong>checks before a restore starts, so it fails before it can do damage.<\/strong> Free\ndisk space, the database permissions the restore needs, whether the downloaded backup\nmatches what was stored, and whether the backup's tables match this site's are all\nverified first. An incomplete download is now reported as an incomplete download rather\nthan as a wrong passphrase.<\/li>\n<li>Improve: <strong>restores can optionally remove files the backup does not contain<\/strong>, for\nrecovering from a hack rather than from a mistake. Off unless your provider asks for it,\nand never applied to your uploads.<\/li>\n<li>Improve: <strong>after a restore, the site is put back in working order<\/strong>: caches are cleared\n(so you see the restored site and not the old one), plugins and themes the restored\ndatabase names but that are no longer installed are deactivated rather than breaking the\nsite, and a pending WordPress database upgrade is completed.<\/li>\n<li>Improve: backups and restores can no longer run at the same time. A backup started while\na restore was in progress could previously store a copy of a half-restored site.<\/li>\n<li>Improve: web addresses are rewritten more completely on a restore &mdash; the WordPress\naddress and the site's folder path as well as the home address &mdash; and the rewrite can\nno longer skip rows on large tables or be stopped by unusual stored data.<\/li>\n<li>Improve: database triggers and stored routines are now included in backups and restored\nwith them.<\/li>\n<li>Fix: the number of files a restore wrote is shown again on your provider's dashboard.<\/li>\n<li>Fix: files whose names contain two dots in a row (<code>photo..2019.jpg<\/code>) are no longer\nskipped by a restore.<\/li>\n<\/ul>\n\n<h4>1.21.0<\/h4>\n\n<ul>\n<li>Improve: <strong>the Activity log now records the things it was missing.<\/strong> Updates installed on\nthis site by your provider are written down &mdash; what was updated, and anything that\nfailed &mdash; and so are a file being read off this server, a change to the provider\ndetails shown on your dashboard, and a change to the address this site reports to.\nBetween them those were the actions with the biggest effect on the site and the least\ntrace of who asked for them. Nothing about how the plugin works changes; the log simply\nanswers more of the questions people bring to it.<\/li>\n<\/ul>\n\n<h4>1.20.0<\/h4>\n\n<ul>\n<li>Add: <strong>a plugin that appears out of nowhere is now scanned immediately.<\/strong> When a plugin\nturns up on your site that WordPress.org does not publish &mdash; a premium plugin you\ninstalled by hand, or something nobody installed at all &mdash; the site starts a malware\nscan itself, within seconds, and tells your provider what set it off. Fake plugins are\nhow most WordPress break-ins arrive, and until now the first look at one waited for the\nnext scheduled scan. Nothing is blocked, quarantined or deleted: the site looks, and your\nprovider decides. Anything appearing in the <em>must-use plugins<\/em> folder counts too, since a\nfile put there runs on every request with nothing to activate it and no way to switch it\noff.<\/li>\n<\/ul>\n\n<h4>1.19.0<\/h4>\n\n<ul>\n<li>Add: <strong>German<\/strong>. Every screen this plugin adds &mdash; the settings page, the dashboard\nsupport card, every status message, every scan finding and every error &mdash; is\ntranslated, in both <em>Deutsch<\/em> and <em>Deutsch (Sie)<\/em>. Nothing to configure: WordPress uses\nyour site's own language setting. The wording is formal throughout, because these\nscreens are read by the person whose site it is rather than by the agency maintaining\nit.<\/li>\n<li>Add: a page trace now lists the page's resource hints &mdash; the\n   and <code>&lt;link rel=\"dns-prefetch\"&gt;<\/code> tags &mdash; and says which\nplugin asked for each one. That tag is the reason a page reaches out to a third-party\nserver before it has drawn anything, it is what a webfont privacy check flags, and it\nwas the one thing on a traced page the trace could not name: nothing enqueues it, and\nWordPress prints it itself, so tracing the bytes only ever answered \"WordPress\". Hints\nadded through WordPress' own filter now carry the callback that added them; hints\nprinted straight into a theme template are found in the page and labelled with the file\nthat printed them.<\/li>\n<li>Add: <strong>server-configuration checks<\/strong>. The <code>.htaccess<\/code>, <code>php.ini<\/code> and <code>.user.ini<\/code> files\ninside your WordPress folder are now read and reported to your provider, with a history\nof every time one of them changed. These are the one part of a WordPress install that\nchanges what the <em>server<\/em> does rather than what WordPress does &mdash; nothing in the\nWordPress admin shows them, and a single line in one can outlive every other trace of a\nbreak-in. Flagged: a file set to run before every request, a rule that makes uploaded\nimages execute as PHP, a redirect that only fires for search engines or for visitors\narriving from one, and a <code>php.ini<\/code> sitting in the media library, which has no legitimate\nreason to exist.<\/li>\n<li>Note: nothing is ever changed on your site. A good share of these directives are written\ndeliberately by a host or a security plugin, so the point is that you get to see them and\ndecide &mdash; not that they are removed for you. A folder correctly set to refuse PHP is\nreported too, as good news rather than as a problem.<\/li>\n<li>Add: the scan's signature list can now be updated by your provider without updating this\nplugin. A new detection reaches your site within hours instead of waiting for a plugin\nrelease to travel a whole fleet &mdash; and a signature that turns out to fire on a\nlegitimate plugin can be switched off just as quickly. A site that has never been paired,\nor whose provider is unreachable, keeps scanning with the list this plugin ships.<\/li>\n<li>Add: <strong>email delivery is now monitored<\/strong>. WordPress keeps no record of the mail a site\nsends: when a contact form stops delivering, or an order confirmation never arrives, the\nerror is raised and thrown away and the site goes on looking perfectly healthy. This now\ncounts what the site tries to send and what fails, keeps the reason the mail server gave,\nand reports which transport is doing it &mdash; PHP's <code>mail()<\/code>, an SMTP server, or the\nplugin that configured one. Your provider sees it alongside everything else.<\/li>\n<li>Note: no part of any message is recorded. Not the recipient, not the subject, not the\nbody &mdash; only how many were attempted, how many failed, and why. And \"this site has\nsent no email\" is reported as exactly that rather than as a fault: a site with no forms\nand no shop legitimately sends nothing for months.<\/li>\n<li>Add: a malware scan can now be <strong>cancelled<\/strong> from your provider's hub. A full scan reads\nevery file on the site across several scheduled runs, so on a large or slow one it can\nsit half-finished for a long time &mdash; and until now nothing could stop it, including\nthe next scan, which was refused because a run was already going. Cancelling stops the\nwalk on the site and lets a new scan start straight away.<\/li>\n<li>Add: the backup passphrase field now shows how strong the passphrase is as you type.\nNothing is enforced &mdash; a backup passphrase is not a login, and nobody, including\nyour provider, can recover a backup without it, so being locked out by a rule you did\nnot expect would be far worse than a weak one. The meter simply says what helps:\n<strong>length beats punctuation<\/strong>, and four unrelated words reach the top of the scale where\na short password with symbols in it does not.<\/li>\n<li>Fix: a remote update could be killed part-way through by the host's PHP time limit. A\ncore update or a batch of plugin updates is downloaded, unpacked and installed inside a\nsingle request, and thirty seconds &mdash; the default on a great many shared hosts\n&mdash; is not long enough. The update is now taken off that clock and detached from the\nconnection that asked for it, so it always runs to completion even if the hub stops\nwaiting; the hub, for its part, now re-checks the site rather than reporting a slow\nupdate as a failed one.<\/li>\n<\/ul>\n\n<h4>1.18.0<\/h4>\n\n<ul>\n<li>Add: the malware scan now recognises the backdoors that arrive dressed as an ordinary plugin, not just the classic obfuscated webshells. A plugin absent from WordPress.org has no published files to check it against, so until now one like this surfaced only as <em>unverifiable<\/em> &mdash; never as malware &mdash; however plainly hostile its code. The scan now flags, as critical, a plugin that forges a login as an administrator it picked for the caller, that gates a privileged action on a secret key baked into the file, that creates an administrator from hard-coded credentials, that hides accounts from the users list, or that runs request data through a dynamic function call.<\/li>\n<li>Note: each of these is matched by the thing that makes it illegitimate, not by an API a healthy plugin also uses &mdash; a login plugin that sets a session after checking a password is untouched, and WordPress' own installer, which creates the first administrator on every site, is not mistaken for a planted one. The false positives it must not raise are pinned by a test alongside the detections themselves.<\/li>\n<\/ul>\n\n<h4>1.17.1<\/h4>\n\n<ul>\n<li>Fix: an untouched WordPress could be reported as having a missing or modified core file. A non-English WordPress installs the readme and licence in its own language too &mdash; <code>liesmich.html<\/code> on a German site, <code>licenza.html<\/code> on an Italian one, eight more names across the languages &mdash; and only the English <code>readme.html<\/code> and <code>license.txt<\/code> were treated as the documentation they are. So deleting them, which most hardening guides tell you to do and a good many hosts already have, was reported as a missing core file, and a copy edited to hide the version number or rewritten in transit was reported as a critical modification. Neither could be cleared: the file's own language package stopped shipping it years ago.<\/li>\n<li>Change: the release's documentation is now recognised by what it is &mdash; a readme or licence sitting in the site root &mdash; rather than by a list of the English names, so the same is true of every language and of any name a future one uses. Files inside <code>wp-admin<\/code> and <code>wp-includes<\/code> are unaffected: nothing legitimately rewrites one of those, whatever its extension, and a modified one is still a critical finding. The skipped files are still pattern-scanned for malware exactly as before.<\/li>\n<\/ul>\n\n<h4>1.17.0<\/h4>\n\n<ul>\n<li>Add: <strong>page tracing<\/strong>. Your provider can now ask this site, from their hub, to render one of its own public pages and report which file produced each line of the HTML that came back &mdash; core, a plugin, the theme, a must-use plugin, a drop-in. The question it exists to answer is the one nothing in WordPress answers: <em>which<\/em> plugin adds the Google Fonts tag, or the tracking script, or the markup nobody can find a setting for. Styles and scripts are reported separately with the callback that enqueued each one, because those tags are printed by WordPress itself and naming WordPress helps nobody.<\/li>\n<li>Note: the trace never alters the page. Nothing is injected into the output, no marker and no comment; the attribution travels beside the HTML as byte offsets, so what is measured is exactly the page a visitor  &hellip;<\/li>\n<\/ul>","raw_excerpt":"Site agent for health monitoring, malware scanning, encrypted backups and remote updates, driven by a Bulwarden hub you connect it to.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/en-ca.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/355276","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/en-ca.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/en-ca.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/en-ca.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=355276"}],"author":[{"embeddable":true,"href":"https:\/\/en-ca.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/bulwarden"}],"wp:attachment":[{"href":"https:\/\/en-ca.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=355276"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/en-ca.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=355276"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/en-ca.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=355276"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/en-ca.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=355276"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/en-ca.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=355276"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/en-ca.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=355276"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}