{"id":355276,"date":"2026-08-23T12:19:18","date_gmt":"2026-08-23T12:19:18","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/bulwarden\/"},"modified":"2026-08-23T12:18:57","modified_gmt":"2026-08-23T12:18:57","slug":"bulwarden","status":"publish","type":"plugin","link":"https:\/\/ar.wordpress.org\/plugins\/bulwarden\/","author":23550935,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.13.7","stable_tag":"1.13.7","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-08-23 12:18:57","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":0,"downloads":50,"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"}},"upgrade_notice":{"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"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3661811,"resolution":"1","location":"assets","locale":"","width":2080,"height":434},"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":3661811,"resolution":"3","location":"assets","locale":"","width":2080,"height":2480},"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":3661811,"resolution":"6","location":"assets","locale":"","width":1120,"height":460}},"screenshots":{"1":"The Bulwarden screen at a glance: whether the site is connected, whether\nbackups are encrypted, and when the last backup and scan ran.","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 and its log,\nabove the encryption passphrase and the folders left out of the 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 and how to reach\nthem, pushed by the hub."}},"plugin_section":[262246],"plugin_tags":[732,2156,600,151481,2550],"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-maintenance","plugin_tags-management","plugin_tags-security","plugin_tags-site-health","plugin_tags-updates","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=3661811","caption":"The Bulwarden screen at a glance: whether the site is connected, whether\nbackups are encrypted, and when the last backup and scan ran."},{"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=3661811","caption":"Backups: a run in progress showing the phase it has reached and its log,\nabove the encryption passphrase and the folders left out of the 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=3661811","caption":"The dashboard support card &mdash; who maintains this site and how to reach\nthem, pushed by the hub."}],"raw_content":"<!--section=description-->\n<p>Bulwarden is the <strong>site agent<\/strong>, installed on each managed\nWordPress site. It exposes a signed REST API (<code>jgm\/v1<\/code>) that a <strong>Bulwarden\nhub<\/strong> calls to read health and security data and to trigger updates. The hub\nis a separate application \u2014 it is not part of this plugin \u2014 and this plugin\ntalks to no hub at all until you pair it with one.<\/p>\n\n<h4>Features<\/h4>\n\n<ul>\n<li>Site health snapshots (versions, pending updates, disk, WordPress Site Health tests)<\/li>\n<li>Full plugin &amp; theme inventory (version, active status, available update) per site<\/li>\n<li>Configuration-level security checks with a posture score<\/li>\n<li>Remote core \/ plugin \/ theme updates, wrapped in maintenance mode<\/li>\n<li>Zero-knowledge encrypted backups: the passphrase stays on your server and is never transmitted<\/li>\n<li>A dashboard card showing who maintains this site and how to reach them<\/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, and body. Timestamps\noutside a 5-minute window are rejected to limit replay. 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<\/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=\"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.13.7<\/h4>\n\n<ul>\n<li>No change to the plugin. Like 1.13.6 it exists for the release itself: publishing to the plugin directory failed on the very first attempt, in the step that tells the directory which files a version has dropped &mdash; there was no previous version for this one to have dropped any, and the step could not cope with having nothing to do.<\/li>\n<\/ul>\n\n<h4>1.13.6<\/h4>\n\n<ul>\n<li>No change to the plugin itself &mdash; it is 1.13.5 under a new number. The screenshots on the plugin directory name the version they were taken from, so publishing them alongside a matching release is what keeps the listing and the download describing the same thing.<\/li>\n<\/ul>\n\n<h4>1.13.5<\/h4>\n\n<ul>\n<li>Fix: opening a running backup's log squeezed it into the half of the panel beside the Cancel button, leaving the button stranded in the middle of a column of log lines. The log now gets the full width of the panel.<\/li>\n<li>Fix: the progress bar for the encryption and upload phases sat empty at 0% for a few seconds after each page load, before the first update reached it &mdash; directly under a line already saying how much had been transferred. It now appears only once there is progress to draw.<\/li>\n<li>Change: the encryption passphrase field no longer stretches the whole width of the panel.<\/li>\n<\/ul>\n\n<h4>1.13.4<\/h4>\n\n<ul>\n<li>Change: the Bulwarden screen has been rebuilt around the question it never answered &mdash; is this site actually protected right now. It opens with the connection, the encryption passphrase, the last backup and the last scan, each with its own state, and a site that has not been set up yet is walked through the three steps that finish it instead of being shown a disabled button and told to look elsewhere.<\/li>\n<li>Add: a running backup now shows which phase it is in &mdash; database, files, encryption, upload &mdash; and its run log, which the site has been recording all along and never displayed. A failed or cancelled run stays on screen until something replaces it.<\/li>\n<li>Add: the last scan is summarised with what it actually found, grouped by kind, and says plainly how much of the site could be checked against published originals and how much publishes none.<\/li>\n<li>Add: a panel for what this server can do &mdash; PHP, the encryption extension, the archive backend, and whether WP-Cron is running &mdash; because a backup that never starts on one site is otherwise invisible from the admin.<\/li>\n<li>Add: your provider's support details, as pushed by the hub, are now visible on the screen rather than only on the dashboard.<\/li>\n<li>Add: the connect string and API key can be copied with a button, and the secret is masked until you ask to see it.<\/li>\n<li>Fix: the progress bar for the encryption and upload phases never appeared, so a long upload looked identical to a stalled one.<\/li>\n<li>Fix: the button that removes a backup exclusion did nothing when the click landed on its icon.<\/li>\n<\/ul>\n\n<h4>1.13.3<\/h4>\n\n<ul>\n<li>Change: Bulwarden now has its own entry in the WordPress admin menu instead of sitting under Settings. It is a screen you come back to &mdash; to pair the site, set the backup passphrase, start a backup or read the activity log &mdash; not something configured once and forgotten.<\/li>\n<li>Add: a <strong>Settings<\/strong> link on the plugin's row on the Plugins screen, so the way to it is where you look right after activating.<\/li>\n<\/ul>\n\n<h4>1.13.2<\/h4>\n\n<ul>\n<li>Fix: an untouched WordPress could be reported as having a modified core file. WordPress publishes a separate package per language, and they differ in exactly one file it checks: <code>wp-includes\/version.php<\/code>. The package a site installed is not the language it is displayed in &mdash; switching the admin language installs translation files and rewrites no core file at all &mdash; so a site whose two disagree (an English install later switched to German, or a German one switched to \"Deutsch (Sie)\") had that one file compared against a package it never installed. It was reported as a critical modification on every scan, and nothing an operator could do would clear it. The package on disk now decides which checksums are fetched, and if WordPress.org publishes none for it, that one file is left unchecked rather than judged against another package's.<\/li>\n<li>Fix: core findings now tell the hub which package they were measured against, so the diff view can say why WordPress.org's copy of <code>version.php<\/code> differs from a localized site's rather than presenting it as an unexplained mismatch.<\/li>\n<\/ul>\n\n<h4>1.13.1<\/h4>\n\n<ul>\n<li>Fix: some untouched plugins were reported as modified. WordPress.org publishes more than one accepted copy of a file whenever a release's repository copy and its downloadable zip drift apart &mdash; both are genuine, and a site can legitimately have installed either. Every published copy is now accepted, so a file matching any of them is clean. Affected files were reported as critical findings on plugins nothing had touched.<\/li>\n<li>Fix: findings on those files now tell the hub every hash the file was measured against, so it can show you the diff instead of reporting that WordPress.org has no copy of the file.<\/li>\n<\/ul>\n\n<h4>1.13.0<\/h4>\n\n<ul>\n<li>New: your hub can now see &mdash; and finish &mdash; an update that changed this site's files but never completed. The two that strand a site are a pending WordPress database upgrade (the \"Database Update Required\" screen every administrator is sent to, which the public site hides completely) and a leftover <code>.maintenance<\/code> lock from an update that died half-way. Both are reported to the hub, and both can be finished from it: the repair runs exactly what WordPress itself would have run, and nothing else.<\/li>\n<li>New: a plugin waiting on its own database migration (WooCommerce, most commonly) is reported too. It is reported only &mdash; that migration belongs to its author and runs from their own screen.<\/li>\n<li>New: a restore can now put back <strong>only the database<\/strong>, leaving every file on disk untouched. A bad content edit or a plugin that mangled its own tables is a database problem, and restoring the files alongside it would also undo every plugin, theme and core update made since the backup &mdash; including the security ones. Restores made by an older hub are unaffected and still restore everything.<\/li>\n<li>Add: the health report now includes this plugin's own version, so your hub can tell which sites are new enough for a feature before offering it rather than after failing.<\/li>\n<\/ul>\n\n<h4>1.12.0<\/h4>\n\n<ul>\n<li>New: when a file no longer matches the version published on WordPress.org, your hub can now show you the actual diff \u2014 the published file beside the one on your site, with the changed lines marked. A checksum mismatch on its own can't tell a caching plugin rewriting an asset apart from a backdoor appended to a core file; this is what tells them apart. Findings now carry the release they were compared against and the hash they should have had, so the hub can prove the copy it fetched really is the published one.<\/li>\n<li>New: <code>POST \/jgm\/v1\/file<\/code> returns the current contents of a file, and <strong>only<\/strong> of a file the site's own last scan flagged. It is not a general file read: anything not named by a finding is refused, as are wp-config.php, symlinks, paths outside the site, and files over 1 MB.<\/li>\n<\/ul>\n\n<h4>1.11.1<\/h4>\n\n<ul>\n<li>Fix: the malware scan called a site infected over files that cannot run. A stylesheet, image or font inside a plugin or theme that no longer matches its published release is now reported as a warning rather than as a critical finding \u2014 the usual author of a rewritten <code>style.min.css<\/code> is a caching or optimisation plugin, not an attacker, and the file cannot execute either way. It is still checked, still reported, and still scanned for signatures; code files (PHP, JavaScript, HTML, SVG) and WordPress core are unaffected.<\/li>\n<li>Change: findings now say what was actually observed \u2014 \"no longer matches the version published on WordPress.org\" \u2014 instead of implying a verdict. A checksum mismatch means a file changed after it was installed, which is a fact about the file, not a diagnosis of the site.<\/li>\n<\/ul>\n\n<h4>1.11.0<\/h4>\n\n<ul>\n<li>Fix: sites intermittently reported themselves as unreachable to their hub, clearing again on the next check. Every signed request now carries a single-use nonce, so two legitimate requests that arrive in the same second are no longer mistaken for one being replayed and refused.<\/li>\n<li><strong>This release must be installed alongside the matching hub update<\/strong> \u2014 the two sign requests to each other, and the signing form changed on both sides at once.<\/li>\n<\/ul>\n\n<h4>1.10.4<\/h4>\n\n<ul>\n<li>Fix: the malware scan flagged a plugin's readme file as \"does not match the released version\". wordpress.org generates a plugin's checksums once, at release, but authors keep editing the tagged readme afterwards (a \"Tested up to\" bump, a changelog typo), so this fired on most healthy sites. Readme, changelog and licence files inside a plugin or theme are now reported as informational instead of as a modified plugin \u2014 they are still checked, and code files are unaffected.<\/li>\n<li>Fix: when file-integrity findings from several plugins were listed together, the section was titled after whichever plugin happened to sort first, so it named one plugin above a list that included others. Findings are now titled by what they are, with the affected plugin shown on each file.<\/li>\n<\/ul>\n\n<h4>1.10.3<\/h4>\n\n<ul>\n<li>Fix: a missing or broken component at boot (e.g. an interrupted plugin update that leaves the site with a mix of old and new files) no longer takes down every page on the site. Each boot component now initializes in isolation, the same way a single failing Site Health test already can't break the rest of the health report.<\/li>\n<\/ul>\n\n<h4>1.10.2<\/h4>\n\n<ul>\n<li>Fix: the uploads-folder hidden-PHP check still had false positives after 1.10.1 on images whose embedded color profile or gamma curve happened to be readable-looking binary (a narrow, slowly-drifting run of bytes that lands in printable ASCII without being text \u2014 and, being copied verbatim into every thumbnail WordPress generates, showed up identically across a whole set of derivative image sizes). It now also requires the readable run to contain actual letters, which genuine injected PHP always has (a function name, a keyword, at least a superglobal like <code>$_GET<\/code>) and this kind of structured binary noise essentially never does.<\/li>\n<\/ul>\n\n<h4>1.10.1<\/h4>\n\n<ul>\n<li>Fix: the malware scan's uploads-folder check for hidden PHP could flag a legitimate image as a false positive \u2014 JPEG\/PNG data is effectively random binary, and a large enough image has a real chance of containing a bare <code>&lt;?=<\/code> by pure coincidence. It now also requires readable text to follow the tag, which genuine injected PHP always has but random image data essentially never does.<\/li>\n<\/ul>\n\n<h4>1.10.0<\/h4>\n\n<ul>\n<li>Add: the malware scan's file-integrity check now also covers your <strong>active theme<\/strong>, the same byte-for-byte verification core files and wordpress.org-hosted plugins already get. A custom or premium theme with no published release to compare against is reported as unverifiable rather than treated as a finding, exactly like a premium plugin.<\/li>\n<li>Fix: the malware scan's file-integrity check could wrongly report <code>wp-includes\/version.php<\/code> as modified on a completely untouched WordPress install, on hosts where PHP's own cached view of that file can briefly disagree with what's actually on disk (e.g. after a core auto-update completes). The check now reads the version straight off disk instead of trusting that cache, and any other checksum mismatch is double-checked before being reported, in case it was caught mid-write.<\/li>\n<\/ul>\n\n<h4>1.9.0<\/h4>\n\n<ul>\n<li>Remove: <strong>white-labelling is gone.<\/strong> The plugin always appears as \"Bulwarden\" with its real author in the plugins list, and can no longer be renamed, re-attributed or hidden from users who don't administer the site. Your provider's name, logo and support address still appear on the dashboard support card \u2014 that part is unchanged.<\/li>\n<li>Change: all of this plugin's stored data moved to a <code>bulw_<\/code> prefix, as the Plugin Directory guidelines require. <strong>There is no automatic migration<\/strong>: a site updated from an earlier release finds no stored settings, and has to be re-paired with its hub (Settings \u2192 Bulwarden \u2192 Regenerate credentials, then paste the new connect string into the hub). Note the backup passphrase has to be re-entered too, and existing backups still need the <em>old<\/em> passphrase to decrypt \u2014 keep it before you change it.<\/li>\n<li>Change: the settings screen's styles and scripts are now loaded as proper enqueued files instead of being written inline into the page, and two unnecessary loads of WordPress core files were removed.<\/li>\n<li>Add: readme now documents every external service the plugin contacts, and what is sent to each.<\/li>\n<\/ul>\n\n<h4>1.8.0<\/h4>\n\n<ul>\n<li>Add: when your provider restores a backup to this site, it can now choose whether to also rewrite every URL\/domain found in the database to this site's own \u2014 on by default (unchanged), with the option to leave the restored content exactly as backed up, which matters when a backup is restored onto a different domain than the one it came from.<\/li>\n<\/ul>\n\n<h4>1.6.0<\/h4>\n\n<ul>\n<li>Add: every backup keeps a detailed <strong>run log<\/strong> \u2014 each phase with timings, per-table row counts, anything skipped or pruned and why, and the exact operation that failed. It shows live on the Settings screen while a backup runs, travels to your provider with every finished backup, and is attached to failure reports, so a failed backup can be diagnosed remotely without server access.<\/li>\n<li>Add: <strong>chunked, resumable uploads<\/strong>. Large backups are now sent in small, individually retried pieces that resume mid-file after a network error, instead of one huge request that a proxy body-size limit or dropped connection could doom outright. Falls back automatically when the hub doesn't support it yet. The old hard one-hour upload limit is gone \u2014 a genuinely stalled transfer still aborts within about 2 minutes, but a slow, healthy one is never cut off again.<\/li>\n<li>Add: <strong>end-to-end integrity checks<\/strong>. The finished archive is verified on disk before it is encrypted (exact size, plus a full read-back of the archive index), and a SHA-256 of the encrypted blob travels with the upload so the hub can prove it stored exactly the bytes this site produced.<\/li>\n<li>Add: a <strong>disk-space preflight<\/strong> refuses to start a backup that clearly cannot fit (based on the previous backup's size), failing in seconds with a clear message instead of after an hour of work \u2014 or filling the disk under the live site.<\/li>\n<li>Add: a new streaming archive writer builds the backup in a single pass with no rewrites \u2014 dramatically less disk I\/O on large sites (a 400 MB test tree dropped from ~24 GB of rewrite traffic to zero) \u2014 and already-compressed media (images, video, archives) is stored without recompression, cutting CPU time on media-heavy sites. Requires only zlib; ZipArchive remains as a fallback.<\/li>\n<li>Fix: several ways a database dump could silently come out incomplete are now hard failures instead \u2014 a dropped database connection mid-dump no longer masquerades as \"table finished\", and a dump missing the options table is refused outright. Dumps are also taken as a single point-in-time snapshot, binary data is written in a corruption-proof form, database views are handled as views (previously their rows were duplicated as table data), and large rows can no longer exhaust memory.<\/li>\n<li>Fix: an unreadable directory anywhere in the site (a root-owned folder, a mounted volume's lost+found) no longer aborts the entire backup \u2014 it is skipped, recorded, and reported.<\/li>\n<li>Fix: sites whose plugins or themes are installed as symlinks now get an explicit record that those directories were not archived, instead of silently missing data.<\/li>\n<li>Fix: a backup run killed by the host no longer leaves its temporary working directory \u2014 including an unencrypted copy of the site \u2014 behind forever; leftovers are cleaned up at the start of the next run.<\/li>\n<li>Fix: a restored backup no longer carries the \"backup in progress\" state of the run that produced it, which could block new backups on the restored site for up to an hour.<\/li>\n<\/ul>\n\n<h4>1.4.1<\/h4>\n\n<ul>\n<li>Fix: two <code>phpcs:ignore<\/code> comments in the 1.4.0 working-directory cleanup referenced sniff codes that don't exist, so they didn't actually suppress anything \u2014 the WordPress Plugin Check tool correctly flagged direct <code>rmdir()<\/code>\/<code>scandir()<\/code> use. No functional change.<\/li>\n<\/ul>\n\n<h4>1.4.0<\/h4>\n\n<ul>\n<li>Add: a \"Cancel backup\" button on the Settings screen, shown while a backup is running. It works even if the backup process is wedged, not just when it's responsive: it cleans up the working directory and notifies your provider immediately, rather than waiting for the process to notice.<\/li>\n<li>Fix: a symlinked directory anywhere in the site (pointing at an ancestor, another site sharing storage, or a loop between two directories) could send the archiving step into unbounded recursion with no error \u2014 indistinguishable from a hang. Symlinked directories are no longer followed.<\/li>\n<li>Fix: a stray Unix socket or FIFO under the site (e.g. a cache\/queue backend's socket file) could make the archiver hang indefinitely trying to read it. Only regular files are ever archived now.<\/li>\n<li>Fix: an upload that stalls completely (a proxy or load balancer silently dropping an idle transfer) now fails within about 2 minutes with a clear error, instead of hanging for up to an hour.<\/li>\n<li>Fix: every backup's temporary working directory is now fully removed when it finishes \u2014 previously an empty folder was left behind after every single run, successful or not.<\/li>\n<\/ul>\n\n<h4>1.3.0<\/h4>\n\n<ul>\n<li>Add: live backup status \u2014 the Settings screen now shows which step a running backup is on (dumping the database, archiving, encrypting, uploading), its size, and upload speed, refreshed automatically while it runs. Your provider can now also query this status directly, so a backup that crashes without reporting back no longer leaves its record stuck at \"running\" forever.<\/li>\n<li>Add: only one backup can run at a time; starting a second one while another is in progress is refused with a clear reason instead of the two colliding.<\/li>\n<\/ul>\n\n<h4>1.2.1<\/h4>\n\n<ul>\n<li>Fix: a phpcs:ignore comment for the backup-exclusions form field no longer covered the line it was meant to (Settings.php), and the repository now ships a <code>.gitattributes<\/code> pinning source files to LF line endings \u2014 no functional change.<\/li>\n<\/ul>\n\n<h4>1.2.0<\/h4>\n\n<ul>\n<li>Add: backup exclusions are now a plain editable list on the Settings screen (add\/remove a directory row) instead of a freeform textarea, pre-filled with every exclusion currently in effect \u2014 including the defaults, which used to be invisible and hardcoded.<\/li>\n<li>Add: new default exclusions for the folders the most common alternative backup plugins (UpdraftPlus, All-in-One WP Migration, Duplicator, BackupBuddy, Total Upkeep, WP Clone) use for their own local archives, so they no longer bloat every backup by default.<\/li>\n<li>Add: a <code>.git<\/code> directory is now excluded wherever it occurs in the site tree (not just at the site root), and is pruned from the walk entirely rather than iterated and discarded \u2014 much faster on a site with a large repository history.<\/li>\n<li>Fix: sites that already had a custom backup-exclusions list keep the protection that used to be hardcoded (the cache\/upgrade exclusion) via a one-time migration, so upgrading can't silently shrink what gets excluded.<\/li>\n<\/ul>\n\n<h4>1.1.1<\/h4>\n\n<ul>\n<li>Fix: the plugin's own pre-branding default (Settings menu label, admin plugins-list name) is now <code>Bulwarden<\/code> instead of the generic <code>Site Maintenance<\/code> placeholder left over from before the rebrand \u2014 matches the hub's own agency branding default, which already shipped as <code>Bulwarden<\/code>. An agency's white-label branding still overrides it after a site is paired.<\/li>\n<\/ul>\n\n<h4>1.1.0<\/h4>\n\n<ul>\n<li>Add: the malware scan's signature walk (Layer B) now skips any file already proven byte-identical to its published release by the checksum verifier (Layer A) \u2014 a pattern match inside code the vendor actually shipped isn't evidence of compromise on this site, so it no longer gets reported.<\/li>\n<li>Fix: the scan no longer flags its own installation folder. It isn't hosted on wordpress.org so it was never checksum-verified, and its own pattern documentation quotes the very strings the signature scanner looks for \u2014 which the scanner was matching against itself.<\/li>\n<\/ul>\n\n<h4>1.0.2<\/h4>\n\n<ul>\n<li>Fix: text domain corrected to <code>bulwarden<\/code> (header and every translatable string). The 1.0.0 revert to <code>jg-maintenance<\/code> was itself a mistake: it matched this plugin's source-repo folder name rather than its actual installed slug, which has shipped as <code>bulwarden<\/code> since 0.3.2.<\/li>\n<li>Fix: the restore process's self-protection check (skip this plugin's own folder mid-restore, so it can't overwrite the code that's running it) was still checking <code>wp-content\/plugins\/jg-maintenance<\/code> and so silently failed to protect the real <code>wp-content\/plugins\/bulwarden<\/code> install.<\/li>\n<\/ul>\n\n<h4>1.0.1<\/h4>\n\n<ul>\n<li>Fix: resolved the remaining WordPress Plugin Check findings \u2014 the header\/translatable-string text domain regression from 1.0.0 (see below), a stale <code>Tested up to<\/code>, and several false-positive warnings (direct file I\/O in the backup\/restore subsystem, direct <code>$wpdb<\/code> calls, <code>set_time_limit()<\/code> in background jobs, and core-filter lookups) now carry documented justification so they no longer flag.<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>Fix: text domain reverted to <code>jg-maintenance<\/code> everywhere (header and every translatable string). It must match the plugin's slug\/folder name, not the <code>Bulwarden<\/code> brand \u2014 the earlier switch to <code>bulwarden<\/code> broke WordPress.org's text-domain convention despite matching the new name.<\/li>\n<li>Add: a Privacy section documenting exactly what data is sent to a paired hub, and when.<\/li>\n<li>Harden: encrypted backup uploads now honor a site's configured HTTP proxy and verify TLS certificates the way the WordPress HTTP API does, rather than relying on raw curl defaults.<\/li>\n<\/ul>\n\n<h4>0.3.3<\/h4>\n\n<ul>\n<li>No plugin changes. Version bump only, to re-cut a release after a suite CI fix (release:maintenance\/deploy:plugin were still looking up the pre-rebrand plugin slug).<\/li>\n<\/ul>\n\n<h4>0.3.2<\/h4>\n\n<ul>\n<li>Fix: main plugin file renamed to bulwarden.php to match the Bulwarden rebrand (the CI build looks up the file by plugin slug).<\/li>\n<\/ul>\n\n<h4>0.3.1<\/h4>\n\n<ul>\n<li>Rebrand: the plugin is now Bulwarden (user-facing strings, plugin header, and default branding).<\/li>\n<\/ul>\n\n<h4>0.3.0<\/h4>\n\n<ul>\n<li>Rebrand: the plugin is now Bulwark, the site agent for Bulwarden (user-facing strings and plugin header).<\/li>\n<\/ul>\n\n<h4>0.2.3<\/h4>\n\n<ul>\n<li>Backups: the archiver now tolerates files that are deleted or replaced mid-backup (e.g. another backup plugin rotating its own files) \u2014 the affected files are skipped and logged instead of aborting the whole backup.<\/li>\n<\/ul>\n\n<h4>0.2.2<\/h4>\n\n<ul>\n<li>Backups: admin-configurable exclusion paths (Settings \u2192 JG Maintenance \u2192 Backup exclusions) so directories the plugin can't know about on its own \u2014 e.g. a legacy backup plugin's folder \u2014 can be left out of the archive instead of bloating every future backup.<\/li>\n<\/ul>\n\n<h4>0.1.0<\/h4>\n\n<ul>\n<li>Initial scaffold: client agent with signed REST API, health + security collectors, remote updates, white-label settings. (Hub is a separate webapp.)<\/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:\/\/ar.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/355276","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ar.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/ar.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/ar.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=355276"}],"author":[{"embeddable":true,"href":"https:\/\/ar.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/bulwarden"}],"wp:attachment":[{"href":"https:\/\/ar.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=355276"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/ar.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=355276"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/ar.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=355276"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/ar.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=355276"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/ar.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=355276"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/ar.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=355276"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}