Running the fork against a live WordPress found a real hole in registration, and it is the kind that only shows up when you actually try it. ECDSA public-key recovery always succeeds. Given any well-formed signature and any digest it returns a key — just not the signer's, unless the digest is the one that was signed. The auth flow leaned on that as if a wrong message would fail. It does not; it quietly yields a stranger's address. At sign-in this was harmless, because the wrong address matches no account and the attempt fails. Registration and wallet-linking were another matter: both took the recovered address and bound it to an account, so a signature over slightly different text — a challenge copied without its blank line, a wallet that rewrote the text, a login signature replayed at the registration form — created an account keyed to an address nobody could sign for. The person would see "success" and discover the truth the next time they tried to get in. Wallet-linking was worse still: it would move an existing account onto a dead address and lock its owner out of their own site. Both paths now require the address the signer claims and compare it to the recovered one, which is what verification actually means. Sign-in accepts the claim when the page sends it and uses it to turn "no account uses that wallet" into the more useful "that signature is not over the text we asked for". Also from running it: URL rewriting mangled every link on a site whose URL carries a port. The protocol-relative pass matched inside absolute URLs and gave each one a second scheme, and matching the host without its port left the port stranded as `//host:8760:8760/`. Local and staging installs would have exported a site of broken links. Plain permalinks silently collapse an entire site onto one exported file, because every post's URL is `/?p=N` and its path is `/`. The queue looks healthy the whole time. The Publishing screen now says so. Translations loaded on `plugins_loaded`, which WordPress 6.7 warns about on every request — the kind of noise that trains people to stop reading logs. And one deletion: an `is_email()` filter written on the assumption that WordPress rejects `.invalid` addresses. It does not — `is_email()` validates syntax, not whether a domain could exist — so the filter never fired. A filter that appears to relax a rule but does not is worse than no filter, because someone later reasons from it. The documentation made the same claim and has been corrected. Verification added rather than asserted: tests/live.mjs drives a real instance over HTTP (40 checks), and tests/mock-gateway.mjs answers uploads with the signature check transcribed from the gateway's own source, so the publishing path can be exercised without a registered name.
107 lines
6.1 KiB
Markdown
107 lines
6.1 KiB
Markdown
# Plugin compatibility
|
|
|
|
The short answer: plugins work. Sirius Press changes how people sign in, not
|
|
how WordPress works. Roles, capabilities, nonces, the options API, the REST
|
|
API, the hook system and `wp_mail()` are all untouched, so a plugin that does
|
|
not ask "what is this user's email address?" cannot tell the difference.
|
|
|
|
The rest of this page is the long answer, for the plugins that do ask.
|
|
|
|
---
|
|
|
|
## The mechanism
|
|
|
|
Every account has a `user_email`. It is a placeholder under a domain ending in
|
|
`.invalid` — a top-level domain [RFC 2606](https://www.rfc-editor.org/rfc/rfc2606)
|
|
reserves precisely so that it can never resolve anywhere, ever.
|
|
|
|
This matters more than it sounds. `$user->user_email` is read in thousands of
|
|
places across the ecosystem, usually without a null check. Leaving it empty
|
|
would produce warnings, blank "From" headers and outright fatals in other
|
|
people's code. Filling it with something permanently undeliverable means every
|
|
one of those reads returns a string and behaves normally.
|
|
|
|
Mail addressed to one of those placeholders never reaches a mail transport. It
|
|
is caught by `pre_wp_mail` and delivered to the **Sirius Press inbox**, where
|
|
the recipient reads it while signed in. That is where password-reset mails,
|
|
comment-moderation notices, update nags and "your plugin needs attention"
|
|
messages end up.
|
|
|
|
Mail addressed to a real domain is passed straight through. If you configured
|
|
SMTP, it sends. If you did not, it fails exactly as stock WordPress fails.
|
|
|
|
`is_email()` is left completely alone, and it is worth saying why, because
|
|
the obvious guess is wrong. WordPress validates an address's *syntax*, not
|
|
whether its domain could ever exist — so `.invalid` addresses already pass
|
|
`is_email()` unchanged. No shim is needed to make the placeholders acceptable
|
|
to plugins that validate, and none is shipped. (Verified against WordPress
|
|
7.1.1; an earlier draft of this fork carried a filter based on the wrong
|
|
assumption, and it never fired.)
|
|
|
|
The flip side is that `is_email()` will also accept `someone@example.invalid`
|
|
typed into a contact form by a visitor. That is stock WordPress behaviour, not
|
|
something this fork introduced, and it is the contact form's business to care
|
|
about.
|
|
|
|
---
|
|
|
|
## The top ten
|
|
|
|
Three of these were installed and activated on a Sirius Press 0.1.0 instance
|
|
running WordPress 7.1.1, with all four fork plugins active, and the whole
|
|
wallet sign-in suite re-run with them loaded. Those rows say **tested**. The
|
|
rest are reasoned from each plugin's setup and activation path and say
|
|
**expected** — treat them as claims awaiting a test, and report anything that
|
|
behaves differently.
|
|
|
|
| Plugin | Status | Notes |
|
|
|---|---|---|
|
|
| **Yoast SEO** 28.6 | tested — activates cleanly | No errors on any admin screen with it loaded. Reads `admin_email` for schema output and gets the placeholder. Its XML sitemaps export to your name along with everything else. |
|
|
| **Contact Form 7** 6.1.7 | tested — activates cleanly | Forms build and save. The default recipient is the placeholder, so submissions land in the inbox — fine for a small site, not what you want for a real contact form. Set a real address and configure SMTP. |
|
|
| **WooCommerce** 11.1.1 | tested — activates cleanly | Store and admin work. Order emails to customers use the address the customer typed, so they send once SMTP is configured. One behaviour worth knowing: WooCommerce redirects subscriber-role accounts away from wp-admin, so a newly registered reader lands on the shop rather than their profile. That is WooCommerce's own setting, not this fork's. |
|
|
| **Elementor** | expected to work | No email dependency. Pages built with it export normally. |
|
|
| **Wordfence** | expected to work | Alert emails go to the inbox unless you set a real address. Its login-security features overlap with wallet auth; two-factor on top of a signature is redundant, and its "email me a code" option cannot work. |
|
|
| **WP Super Cache / W3 Total Cache** | expected to work | Compatible, but consider whether you need them: the static export already serves cached HTML from the name, which is the harder-working cache. |
|
|
| **Akismet** | expected to work | Needs an API key, obtained on akismet.com with a real address of yours. Nothing to do with site accounts. |
|
|
| **Jetpack** | expected to work partly | Connection requires a WordPress.com account. The modules built around subscriber email lists cannot do anything useful here. Not recommended. |
|
|
| **UpdraftPlus** | expected to work | Backups work. Report emails go to the inbox. Back up `wp-config.php` too, or you lose `SIRIUS_PRESS_KEY` and with it the stored publishing phrase. |
|
|
| **Advanced Custom Fields** | expected to work | No email dependency at all. |
|
|
|
|
"Activates cleanly" means the plugin activated without a fatal or a WP_Error,
|
|
and every Sirius Press admin screen plus Users, Profile and Plugins rendered
|
|
with no PHP diagnostic while it was loaded.
|
|
|
|
---
|
|
|
|
## What genuinely cannot work
|
|
|
|
Not because of a shim we did not write, but because the feature is the mailbox:
|
|
|
|
- **Email-based two-factor.** There is no mailbox to send a code to.
|
|
- **Newsletter plugins managing your site's own accounts.** A newsletter to a
|
|
list of real addresses people typed into a form works fine. A newsletter to
|
|
"all subscribers" does not, because subscribers have wallets, not mailboxes.
|
|
- **"Email me when someone comments" for account holders.** Goes to the inbox
|
|
instead, which the person sees next time they sign in.
|
|
- **Password reset by email.** Deliberately removed. See
|
|
[accounts.md](accounts.md).
|
|
|
|
---
|
|
|
|
## Making a plugin behave
|
|
|
|
If a plugin refuses to finish its setup because of a missing address, the usual
|
|
fix is one filter:
|
|
|
|
```php
|
|
add_filter( 'pre_option_their_email_setting', function ( $value ) {
|
|
return $value ?: 'admin@' . SP_Settings::stub_email_domain();
|
|
} );
|
|
```
|
|
|
|
`sirius-press-compat` is a short file of exactly these. If you write one for a
|
|
plugin not listed here, it is worth sending — the whole file is under a hundred
|
|
lines of real code and it grows one plugin at a time.
|
|
|
|
If a plugin sends mail to a real address and it does not arrive, that is an
|
|
SMTP problem rather than a Sirius Press one: see [smtp.md](smtp.md).
|