Repository navigation
Signed updates
SafeHouse installs an update only when the release manifest carries a valid Ed25519 signature and the downloaded package matches the checksum in that manifest. This page explains where updates come from, how a release is built and signed, what the site checks, and what a valid signature does and does not prove.
SafeHouse is not in the WordPress.org plugin directory. Its plugin header names its own update host (Update URI: https://updates.designhouse.me/shouse/), so WordPress asks only that host about SafeHouse updates and never looks up the shouse slug on WordPress.org. A plugin with a lookalike name there cannot replace it.
The update host, updates.designhouse.me, is a read-only Cloudflare Worker. It serves a fixed list of files from a private storage bucket: the release manifest, its signature, the release ZIPs and the vulnerability data. Every other path answers 404.
- When WordPress checks for plugin updates, SafeHouse downloads
manifest.jsonandmanifest.json.sigfrom the update host. - It verifies the Ed25519 signature over the exact bytes of the manifest, with the public key built into the installed plugin. If the signature is invalid, no update is offered.
- It checks that the manifest is for SafeHouse and that its fields are well formed: a version number, a 64-character SHA-256 checksum and an HTTPS download address.
- Only then does WordPress show the update.
- When WordPress downloads the package, SafeHouse checks that the download address is the one in the signed manifest, downloads the ZIP itself and compares its SHA-256 checksum with the manifest. On any mismatch it deletes the file and cancels the update.
The verified manifest is cached for 3 hours, and a failed check for 1 hour.
Each step is written to the activity log: update_verified (info) when a package passes, and update_rejected (critical) when something is refused.
| What went wrong | What you see |
|---|---|
| The manifest signature is invalid | No update is offered. The activity log shows "SafeHouse update manifest has an invalid signature", and wp shouse update-check reports "Manifest signature is invalid." |
| The package address is not the signed one | The update fails with "This SafeHouse package is not listed in the signed release manifest. Update cancelled." |
| The package checksum does not match | The update fails with "The downloaded SafeHouse package does not match the signed checksum. Update cancelled." |
An invalid manifest signature shows no message on the Plugins or Updates screens; the site sees no update. If you expect a release and do not see it, look at the activity log or run wp shouse update-check.
SafeHouse switches on WordPress's background updates for itself, so a signed security fix arrives without anyone clicking. To leave the decision to WordPress's own auto-update setting, add this to wp-config.php:
define( 'SHOUSE_AUTO_UPDATE', false );To stop update checks entirely, for example on sites deployed from git, use SHOUSE_DISABLE_UPDATES. See Deploy with git or CI.
- A maintainer runs
./dev/release.sh x.y.zon a cleanmainbranch. It requires a## x.y.zsection inCHANGELOG.md, sets the version, runs the linters, commits "Release x.y.z" and creates the tagvx.y.z. - Pushing the tag starts the Release workflow in GitHub Actions. Its first job runs the linters and a test build with a throwaway key, without access to any secret.
- The second job runs only after a maintainer approves the protected
releaseenvironment, which holds the signing key. - The build takes the
plugin/folder from the tag, adds the license, and packs the files in a fixed order with fixed timestamps, so the same tag always gives the same ZIP. It writes the manifest with the ZIP's SHA-256 checksum, signs it, verifies the signature, and refuses to continue if the key is not one the tag's own code trusts. - The same three files,
shouse-x.y.z.zip,manifest.jsonandmanifest.json.sig, go to the update host and to a GitHub release, where anyone can check them: Verify a download.
The private key exists only in the protected GitHub environment and in an offline backup.
SafeHouse 0.2.0 was released shortly before this workflow existed. It was built and signed with the earlier version of the same build script, which did not yet add the license file to the ZIP. Verify a download shows how to rebuild it byte for byte.
The public key built into SafeHouse 0.2.0, added on 2026-10-06:
SOrVjhZX5PC6q78ohxcoO9zqcll7YarqDyF6hQ6knBg=
The key list lives in PUBLIC_KEYS in plugin/src/Core/Updater.php. When the key changes, one release trusts both the old and the new key, and later releases trust only the new one. Install updates as they come: a site that skips the release that introduced a new key cannot verify later releases and needs one update by hand.
Vulnerability alerts data is signed with a separate key. The updater never trusts that key, so the vulnerability data pipeline cannot sign a plugin update.
A valid signature and a matching checksum prove that the package is the one signed with the SafeHouse release key, and that nobody changed it afterwards: not the update host, not a CDN, not anyone on the network. Someone who takes over the update host still cannot ship code, because they do not have the private key.
They do not prove:
- that the code is free of bugs or vulnerabilities
- anything about a ZIP you upload by hand on the Plugins screen: SafeHouse does not check those. Verify it yourself first.
- anything if the private key itself is stolen; that is why it is kept only in a protected environment that needs a maintainer's approval
- anything about a copy deployed from git: SafeHouse does not update a git working copy at all
These pages describe SafeHouse 0.2.0 and were last reviewed on 2026-10-07. · Repository · Releases · Changelog · Report a problem with these pages
Search every page and heading in the Pages box.
Start here
Modules
- All modules
- Security: Hardening · Login limits · Bot protection · Install lockdown
- Alerts: Change alerts · Plugin health · Vulnerability alerts
- Performance: LiteSpeed page cache · Cloudflare cache · Redis object cache
- WooCommerce: Omnibus price history
- Site tools: SMTP mail · Header and footer scripts · Duplicate posts and pages · Maintenance mode · Tweaks
Guides
- Behind Cloudflare or a proxy
- Running with Wordfence
- Configure in wp-config
- Deploy with git or CI
- Replacing other plugins
- Upgrade from WPHouse
Reference
Trust
Help