
Bulwarden is the site agent, installed on each managed WordPress site. It
exposes a signed REST API (jgm/v1) that a Bulwarden hub calls to read
health and security data, run checks and trigger updates. The hub is a separate
application — it is not part of this plugin — and this plugin talks to no hub
at all until you pair it with one. On its own it sends nothing anywhere.
It is built for the person who is responsible for a site they did not build:
an agency looking after forty of them, or an owner who wants one place that
answers “is this thing all right?” without logging into it.
What it reports
- Site health — WordPress, PHP and plugin versions, pending updates, disk
usage, and WordPress’ own Site Health tests, collected in one snapshot. - A full plugin and theme inventory, with each item’s version, whether it
is active, whether an update is waiting, and whether it came from
WordPress.org at all — which is what lets a hub match it against a CVE feed. - Must-use plugins, which WordPress runs on every single request with
nothing activating them and no way to switch one off from the admin. Nothing
else in wp-admin lists them properly, and dropping a file there is the tidiest
way to install a backdoor. - Configuration-level security checks with a posture score — file editing,
directory listing, XML-RPC, debug output, and the rest of the settings that
quietly decide how exposed a site is. - Unfinished updates. An update that already replaced a site’s files can
still fail to finish, and neither state clears itself: a pending database
schema upgrade that every administrator is sent to and the public site hides
completely, or a leftover.maintenancelock from an updater that died
mid-copy. Both are reported, and both can be finished from the hub by running
exactly what WordPress itself would have run. - Email delivery. WordPress keeps no record of the mail a site sends: when
a contact form stops delivering or an order confirmation never arrives, the
error is raised and thrown away. This counts what was attempted, what failed
and why, and reports which transport is doing it — PHP’smail(), an SMTP
server, or the plugin that configured one. No part of any message is stored:
not the recipient, not the subject, not the body.
What it finds
- A malware scan in three layers. Every core and WordPress.org file is
verified against its published checksums, so a modified file is found
whether or not anybody has written a signature for what was put in it; then
every file is read for the code patterns webshells and injected backdoors
actually use. Findings carry the file, the line and a short excerpt. - Backdoors that arrive dressed as an ordinary plugin — a forged
administrator login, a privileged action gated on a key baked into the file,
an account created from hard-coded credentials, users hidden from the users
list. A plugin absent from WordPress.org has no published files to check it
against, so without this a hostile one surfaced only as unverifiable. - A plugin hiding itself. Malware that conceals a plugin does it by
answering the question before WordPress can — and the hidden plugin is then
also the one plugin whose files are never checked. The scan counts what is on
disk itself and compares. - Server-configuration files.
.htaccess,php.iniand.user.iniare
the one part of a WordPress install that changes what the server does
rather than what WordPress does, and nothing in wp-admin shows them. A file
set to run before every request, a rule that makes uploaded images execute as
PHP, a redirect that only fires for search engines: all reported, with the
line. Nothing is ever changed for you — a good share of those directives are
written deliberately by a host or a security plugin, and the point is that
you get to decide which. - Leftover files. An interrupted backup, a finished restore and a migration
plugin all leave things behind and none of them clean up: a
wp-config.php.bak the web server hands out as plain text, adatabase.sql
in the web root, an installer that overwrites the site with no login. Found,
measured, and removable from the hub — and the site decides what may be
deleted, so a stale list can only ever be refused. - Signatures that keep up. The scan’s pattern list can be updated by your
provider without updating this plugin, so a new detection reaches the site in
hours rather than waiting for a release to travel a whole fleet. A site that
has never been paired scans with the list this plugin ships.
What it does
- Remote core, plugin and theme updates, wrapped in maintenance mode so
visitors never see a half-updated site — and taken off the host’s PHP time
limit, so a core update on shared hosting finishes instead of being killed
part-way. - Zero-knowledge encrypted backups. The archive is built and encrypted on
this server, under a passphrase that is never transmitted anywhere;
only the ciphertext is uploaded. Nobody — including whoever runs the hub —
can read a backup without that passphrase. Restores work the same way in
reverse, and can put back the database alone if that is all that broke. - Page tracing. Your provider can ask this site to render one of its own
public pages and report which file produced each line of the HTML — core, a
plugin, the theme, a must-use plugin. It answers the question nothing in
WordPress answers: which plugin adds the Google Fonts tag, or the tracking
script, or the markup nobody can find a setting for. The page is never
altered to do it. - WP-Cron on a schedule that actually runs. A site with no visitors runs no
cron, which is how scheduled posts, order emails and licence checks quietly
stop. - A support card on the dashboard naming whoever maintains the site, how to
reach them, and a Request help button that files a ticket with them
directly — attached to the right site, without the owner having to explain
which one.
Pairing
On activation the plugin provisions an API key + secret. Copy the connect
string from the Bulwarden entry in the WordPress admin menu and paste
it into the hub’s “Connect a site” form. Your provider’s support details are
then pushed to the site automatically by the hub and appear on the dashboard.
Security
Hub client requests are authenticated with the per-site API key and an
HMAC-SHA256 signature over the method, route, timestamp, and body. Timestamps
outside a 5-minute window are rejected to limit replay. The secret never
travels on the wire — it lives only in this plugin and in the hub webapp.
Privacy
Bulwarden is a client agent: on its own it sends data nowhere. Pairing this
site with a hub (yours, or your maintenance provider’s) is an explicit,
opt-in action — you choose when to copy the connect string from the Bulwarden
screen in the WordPress admin menu and hand it to that hub. Nothing is
transmitted before that.
Once paired, data only moves in these cases:
- Health, inventory, and security checks. The paired hub reads these from
this site’s signed REST API when it polls or when a check is requested —
WordPress/plugin/theme versions and update status, a full plugin & theme
inventory, disk usage, and the configuration-level security posture score. - Backups, only when triggered (the Settings screen’s “Back up now”
button, or a request from the paired hub): the full site is archived and
encrypted on this server with a passphrase that is never transmitted
anywhere, then the ciphertext is uploaded to the hub. Only someone who has
the passphrase can decrypt a backup — the hub operator cannot read its
contents. - Malware scan results, only when a scan is triggered (admin- or
hub-initiated): findings (type, severity, file path, line, and a short
excerpt) are sent to the hub that requested the scan. - Finishing an interrupted update, when the paired hub asks. Nothing is
sent in that exchange; the site runs WordPress’ own database upgrade, or
deletes an expired.maintenancelock file, and reports the outcome. - Server-configuration files, when the paired hub asks: the contents of
this site’s.htaccess,php.iniand.user.inifiles, so the hub can show
what is in them and when they last changed. These are configuration, not
content — no post, page, media file or user data is read. - Email counters, as part of the health snapshot: how many messages this
site attempted and how many failed over the last seven days, the error text
the mail server returned, and which transport is sending. No part of any
message is recorded — not the recipient, not the subject, not the body. - A traced page, only when the hub asks for one: the HTML of a single
public page of this site, exactly as an anonymous visitor would receive it,
plus the file paths and line numbers that produced it. No file contents are
read and no logged-in view is ever rendered.
Independently of any hub pairing, this plugin verifies WordPress core and
wordpress.org-hosted plugin files by comparing local file hashes against the
public checksum APIs at api.wordpress.org and downloads.wordpress.org.
Those requests carry only what identifies the release to look up — the
WordPress version and locale, and each plugin’s slug and version. No file
contents, hashes, URLs or user data are sent: the reference checksums come
back and the comparison happens here, on this server.
Bulwarden does not call home on activation, does not collect analytics, and
stores data on no server this site hasn’t been explicitly connected to. The
hub itself is a separate service outside this plugin’s control; consult your
provider for how it handles the data described above.
External services
This plugin connects to the services below. Nothing here runs on activation.
Your Bulwarden hub
Bulwarden is the site-side agent for a hub — the management application an
agency or site owner runs to watch and maintain a fleet of WordPress sites. The
hub is a separate product and is not part of this plugin.
The default hosted hub is operated by Bulwarden at https://bulwarden.net/ —
terms of use: https://bulwarden.net/terms/ , privacy policy:
https://bulwarden.net/privacy/ . A hub can also be self-hosted, or run by the
maintenance provider you buy from, in which case this plugin talks to that
provider’s server instead and their terms and privacy policy apply.
No connection to any hub exists until you make one. You pair a site by
copying the connect string from the Bulwarden screen in the WordPress admin
menu and pasting it into the hub; before that the plugin contacts no hub at
all, and it never discovers or chooses one on its own. Once paired, data moves only in these cases:
- When the hub asks for status. It reads this site’s signed REST API and
receives WordPress/plugin/theme versions and update status, the full plugin
and theme inventory, disk usage, the results of WordPress’ own Site Health
tests, and the configuration-level security posture score. - When a backup runs (you press “Back up now”, or the hub requests one).
The site is archived and encrypted here, with a passphrase that is never
transmitted, and only the ciphertext is uploaded. The hub operator cannot
read a backup’s contents without that passphrase. - When a malware scan runs (started by you or by the hub). The findings —
type, severity, file path, line number and a short excerpt — are sent to the
hub that asked for the scan. - When the hub pushes your provider’s support details — their name, logo,
support address and accent colour — on connect and whenever they change
them. Nothing is sent from the site in that exchange.
Every one of those requests is authenticated with this site’s own API key and
an HMAC-SHA256 signature; the shared secret never travels over the wire.
api.wordpress.org and downloads.wordpress.org
The malware scan verifies WordPress core and wordpress.org-hosted plugins
against their published checksums, using WordPress.org’s own public APIs at
https://api.wordpress.org/core/checksums/1.0/ and
https://downloads.wordpress.org/plugin-checksums/. This happens whenever a
scan runs, whether or not the site is paired with a hub.
These requests identify only the release being looked up: the WordPress version
and locale, and each installed plugin’s slug and version. No file contents,
hashes, site URL or user data are sent — the reference checksums are returned
and compared locally.
These are WordPress.org services, covered by the WordPress.org privacy policy:
https://wordpress.org/about/privacy/
Screenshots

The Bulwarden screen at a glance: whether the site is connected, whether backups are encrypted, and when the last backup and scan ran.

Connecting the site: the connect string you paste into your hub, and the API credentials behind it.

Backups: a run in progress showing the phase it has reached and its log, above the encryption passphrase and the folders left out of the archive.

The malware scan: what the last one found, grouped by kind, and how much of the site could be checked against the files WordPress.org published.

The activity log: an append-only record of credential changes, backups, restores, scans and repairs, including the ones the hub triggered.

The dashboard support card — who maintains this site and how to reach them, pushed by the hub.