=== Synth Antispam – AI Comment Spam Protection ===
Contributors: synthplatform
Tags: AI, antispam, anti-spam, comment spam, spam protection
Requires at least: 6.0
Tested up to: 7.1
Requires PHP: 8.0
Stable tag: 0.1.2
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

AI comment spam protection for WordPress. No CAPTCHA, no keyword lists, no auto-deletion. One-click setup with explainable verdicts.

== Description ==

**Spammers learned to write around keyword filters. Synth reads what a comment is actually trying to promote.**

Keyword lists and blocklists miss polite, well-written spam: the "Great article, very helpful!" comment with a casino link in the author URL, the fake SEO agency, the crypto pitch dressed up as a question. Synth uses an AI model to understand the intent of new comments — so it catches spam that looks human, while legitimate comments continue through your normal WordPress moderation. No CAPTCHA, no puzzles, nothing extra for your visitors.

Synth is an AI spam classifier for WordPress comments. It analyzes what a comment is trying to promote instead of matching it against static keyword lists.

= What it blocks =

* SEO and link-building spam, including links hidden in the author URL field
* Casino, crypto, adult and pharma promotions
* Fake "great post!" comments written to carry a link
* Rewritten and misspelled spam that slips past keyword rules
* Pingback spam and trackback spam, including automated submissions through XML-RPC
* Spam submitted through the REST API

= Why it's better than a rule list =

* **Rewording doesn't help the spammer.** Rules match strings, so spammers change the string. The model reads the message — and the message is the thing being sold.
* **Nothing to maintain.** No keyword lists, blocklists or regex to update.
* **Synth never automatically deletes comments.** Spam goes to the Spam folder (or the moderation queue in strict mode) and can be restored in one click. Synth never moves a comment to Trash.
* **You see why.** A verdict column in the Comments list shows the spam category and confidence for every checked comment.
* **No CAPTCHA, no visitor friction.** No puzzles or extra fields. Submitting a comment waits for the service for up to 2 seconds; the verdict follows in the background. No tracking scripts on your pages.
* **Your decisions come first.** Synth never touches a comment you or another moderation plugin already marked as spam or trash, never changes a comment whose status a moderator has already changed, and never publishes a comment your site held for review, so it runs fine alongside your existing setup. A held comment it finds to be spam still goes to the Spam folder (in strict mode it stays in the queue).
* **Your site keeps accepting comments.** If the service is unreachable, or your included checks run out, AI classification pauses and comments follow the fallback you chose: they continue through your normal WordPress moderation (default) or are held for review.

= Start protecting comments in a few clicks =

1. Install and activate.
2. Open **Settings → Synth Antispam** and press **Get a site key** — no account to create, no API key to copy.
3. Done. Every site starts with an included allowance of AI checks — see [current plans](https://wordpress.synth.locker/pricing).

Until a key is set, the plugin sends nothing except when you press **Get a site key**, and WordPress decides on every comment exactly as before.

= Data minimization =

Synth sends only the data needed to classify a comment: the comment text, author name and URL, the email domain, and a per-site pseudonymous hash of the email. It does not receive the commenter's full email address, IP address, raw User-Agent, cookies or your post content. Commenters who already have an approved comment are skipped entirely. What is sent, when, and how long it is kept is listed under "External services" below.

== External services ==

This plugin needs the Synth Antispam service (`wp-api.synth.locker`, operated by LightApps OÜ, Estonia) to classify comments. Nothing is sent until a site key is set, except the request made when you press **Get a site key**.

Every setting and mode of the plugin works on every plan; only the number of AI checks depends on the plan.

**Requests the plugin makes**

Every request below carries the plugin version and WordPress's own User-Agent header, which names your site address and WordPress version (not the commenter's browser); all except **Get a site key** also carry your site key and, in a separate header, your site address.

* Submit a comment (or pingback/trackback) for a verdict — fields listed below. This request also carries your site key and your site address; the first such request is what ties the key to your site.
* Collect the verdict a moment later — sends only the identifier the service issued for that comment.
* Report a moderator correction — off by default (`synth_wp_send_feedback` filter returns false); when enabled, sends only the identifier and the moderator's decision (spam / not spam).
* Get a site key — only when you press the button; sends only the plugin version and the User-Agent header.
* Test connection — only when you press the button; sends one sample comment ("ping", with no commenter details) and costs one check.
* Check the remaining allowance — only while an administrator has the settings screen open; sends the site key and site address. Costs no check; with a new key it may be the request that ties the key to your site.
* Site Health status — only while an administrator has Tools → Site Health open, at most once every 15 minutes; sends a fixed test identifier and costs no check.
* Start a purchase — only when you press an upgrade button; sends Synth the site key, site address, selected option and the settings-page path to return to, then opens Stripe Checkout. Card data goes directly to Stripe and never reaches your site, this plugin or Synth.

**Fields sent with a comment:** `schema_version`, `plugin_version`, `surface`, `object_type`, comment text (on WordPress 6.7 and later, before WordPress HTML filtering), author name, author URL, email domain, `author_id_hash` (SHA-256 of the email with a secret per-site salt — cannot be linked across sites; the salt is destroyed on uninstall), `author_status` (registered / anonymous), `is_reply`, site locale, `client_ip_status` (whether an IP was present, not the IP), `has_user_agent` (whether a User-Agent was present, not the string).

**Exactly what is sent, field by field.** The same list with the reason for each field; it matches the plugin's request builder field for field:

* `schema_version` — the version of the request format, so the service stays compatible with older and newer plugin versions at once.
* `plugin_version` — the installed plugin version, for the same compatibility reason.
* `surface` — a fixed label saying the request comes from the comment form (`wp_comment` in this version; a hook for future integrations such as forms outside comments).
* `object_type` — `comment`, `pingback`, or `trackback`: the latter two are machine-submitted and the service weighs them differently.
* `content.body` — the comment text. On WordPress 6.7 and later it is taken as submitted, before WordPress's own HTML filtering runs, so a link the filtering would otherwise strip is still visible to the classifier; on earlier versions WordPress filters it first.
* `content.author_name` — the display name the commenter typed into the comment form.
* `content.author_url` — the website URL the commenter typed into the comment form. On the traffic this plugin was built against, this field carries a very strong share of the spam signal — most of it is not visible in the comment text at all.
* `content.author_email_domain` — only the domain part of the commenter's email address (for example `gmail.com`), used to recognise disposable or throwaway email patterns. **The email address itself is never sent** — see "What is never sent" below.
* `content.author_id_hash` — a one-way SHA-256 hash of the commenter's email address combined with a secret value generated for your site alone (your site's own "salt"). It lets the service recognise that two comments on your site came from the same email address without ever receiving that address. Because the salt is unique to your site and known only to it, the same commenter cannot be linked across two sites using this plugin — impossible by construction, not merely disabled. It is a per-site pseudonym, not an anonymised identifier: uninstalling the plugin destroys the salt (see "Retention" below), after which even this site can no longer connect a previously sent hash back to an email address.
* `context.author_status` — either `registered` (a logged-in registered user of your site) or `anonymous` (everyone else). Only these two values are ever sent: a commenter who already has an approved comment on your site is trusted and skipped **before** any request is built, so their comment is never sent at all and no status is transmitted for them.
* `context.is_reply` — whether the comment is a reply to another comment.
* `context.site_locale` — your site's configured language (for example `en_US`), used as a hint for language-specific handling.
* `context.client_ip_status` — `absent` if the request carried no IP address, or `direct` if it did. This tells the service only whether one was present — **the address itself is never sent, in either case** — see "What is never sent" below. The plugin never reconstructs a "real" visitor IP from proxy or CDN forwarding headers: those are only as trustworthy as whoever sent the request, and a site behind a proxy cannot tell the difference.
* `context.has_user_agent` — `true` or `false`, whether the browser sent a User-Agent string at all. **The User-Agent string itself is never sent** — see "What is never sent" below.

**What is never sent:** full email address, IP address, raw User-Agent, post title or body, HTTP referrer, cookies, other form fields.

**Retention:** verdict records expire after 24 hours. Separately, the service keeps copies of each submitted request, its verdict and any later correction to train and improve its models. These copies are stored as sent, have no expiry and cannot currently be turned off. To have your site's copies deleted, email support@synth.locker with your site address. Uninstalling deletes your site's salt but not copies already taken.

* Terms of Use: https://synth.locker/assets/legal/wordpress-terms-of-use.html
* Privacy Policy: https://synth.locker/assets/legal/wordpress-privacy-policy.html
* Pricing: https://wordpress.synth.locker/pricing
* Stripe: https://stripe.com/legal · https://stripe.com/privacy

**Paste-ready paragraph for your privacy policy.** The same list for site owners who write their policy by hand, minus the four fields that describe the request's shape rather than the commenter or the submission (`schema_version`, `plugin_version`, `surface` and `object_type`). It is generated from the same manifest as the field list above, so it cannot fall behind it:

> Synth Antispam sends each comment (and each pingback or trackback) to the Synth Antispam classification service — an external processor — to obtain a spam verdict. What is sent: the comment body; the commenter’s display name; the commenter’s website URL; the domain part (not the full address) of the commenter’s email address; a one-way salted hash derived from that email address; whether the commenter is anonymous or a registered user of this site; whether the comment is a reply; the site’s language; whether an IP address was present at all, never the address itself; whether a User-Agent string was present at all, never the string itself. Comments from people who already have an approved comment on this site are not sent to the service at all. The commenter’s full email address, IP address and raw User-Agent string are never sent, in any case. Verdicts are retained by the service for 24 hours and then expire automatically. Separately from that, the service keeps its own copy of everything listed above, together with the verdict it produced and any correction a moderator of this site later makes to that verdict, and uses those copies to train and improve the Synth spam-classification models. Those copies are kept indefinitely and have no expiry date; the comment text and the commenter details above are kept as sent, not anonymised or aggregated. This applies to every site that uses the service: there is no setting that turns it off. To have this site’s stored copies deleted, write to support@synth.locker. Uninstalling this plugin deletes this site’s local secret value, after which any previously sent hash can no longer be linked back to an email address, by this site or by the service.

= Every address that appears in the source =

A search of this plugin's files finds these addresses and no others:

* `wp-api.synth.locker` — the Synth Antispam service above; changeable on the settings screen or in `wp-config.php`. **The only address this plugin sends a request to.**
* `checkout.stripe.com` — Stripe's payment page. Nothing is sent there by the plugin; it only checks that the payment page it was handed really is Stripe's before opening it in your browser.
* `synth.locker`, `wordpress.synth.locker`, `stripe.com`, `wordpress.org`, `www.gnu.org` and `fsf.org` — links (terms, privacy, pricing and plugin home pages, Stripe's documents, the support forum in the translation template, the GPL licence). Nothing is sent to them.
* `support@synth.locker` — the email address for deletion requests.
* Not addresses: the example `your-synth-endpoint.example` in the empty settings field, `gmail.com` in the field list above, the translation-template placeholder `LL@li.org`, and names that appear only inside code comments and are never contacted (`wp-api-dev.synth.locker`, `evil.example`, `https://x`, `http://api`, `http://localhost`).

== Installation ==

1. Install from **Plugins → Add New** (search "Synth Antispam") and activate.
2. Go to **Settings → Synth Antispam** and press **Get a site key**, or paste a key you already have.
3. Press **Test connection**.
4. Optional: choose Spam folder or strict mode (moderation queue), and what happens if the service is unreachable or the allowance runs out.

Multisite: set a network-level key before network-activating.
Advanced: the service address can be changed on the settings screen or in `wp-config.php`.

== Frequently Asked Questions ==

= Does Synth use CAPTCHA? =

No. Visitors see no CAPTCHA, puzzles or extra fields. Synth checks the comment in the background.

= Is there a free plan? =

Every site starts with an included allowance of AI spam checks, with no card required. Sites that need more checks can upgrade to a paid plan — see https://wordpress.synth.locker/pricing.

= What happens when the included checks run out? =

AI classification pauses until the allowance resets or you upgrade. Comments follow the fallback you selected — they continue through your normal WordPress moderation (default) or are held for review — so your site never stops accepting comments.

= How is Synth different from keyword-based spam filters? =

Keyword filters look for known words, domains or patterns. Synth analyzes the intent and context of the comment, which helps it recognize rewritten and human-looking promotional spam without maintaining rule lists.

= Can I use Synth together with Akismet or another moderation plugin? =

Yes. Synth never touches a comment another plugin or a moderator already marked as spam or trash, never changes a comment whose status a moderator has already changed, and never publishes a comment held for review, so it can run alongside your existing moderation setup. A held comment it finds to be spam still goes to the Spam folder (in strict mode it stays in the queue).

= Can Synth be used as an Akismet alternative? =

Yes. Synth can be used on its own for WordPress comment spam protection, or alongside Akismet and other moderation plugins. If another plugin has already marked a comment as spam or trash, Synth leaves it unchanged.

= Will it delete my comments? =

No. Synth never automatically deletes comments or moves them to Trash. Spam goes to the Spam folder (or the moderation queue in strict mode), where you can restore it.

= Will it slow down my site? =

It adds no scripts to your pages. Submitting a comment waits for the service for up to 2 seconds; the verdict follows in the background.

= Can spam be held for review instead of going to Spam? =

Yes — turn on strict mode.

= What happens if the service is unreachable? =

Comments follow the fallback you chose: they continue through your normal WordPress moderation (default) or are held for review.

= Which comments are checked? =

Comments, pingbacks and trackbacks from the comment form, REST API and XML-RPC. Commenters with an already-approved comment are trusted and skipped.

= Does it work on multisite? =

Yes, with a key per site or a network-level key.

= Does the plugin do anything before I add a site key? =

No. Apart from the **Get a site key** button, it sends no requests, and WordPress decides on every comment exactly as it would without the plugin.

= What happens to my site key if I delete the plugin? =

It stays, so reinstalling keeps your checks. Enable "Delete the site key when the plugin is deleted" to remove it.

= For site owners: GDPR and similar privacy laws =

Mention Synth in your privacy policy, including what is sent and how long it is kept, with the 24-hour verdict retention and the indefinite retention of training copies (see "External services"). You need a lawful basis for processing commenter data.

== Screenshots ==

1. AI verdicts in the Comments list — see spam category and confidence for every checked comment.
2. One-click site key setup — no account creation or API key copy-and-paste.
3. Choose Spam folder or strict mode, and what happens if the service is unreachable.
4. Remaining checks and upgrade options on the settings screen.
5. Spam goes to the Spam folder, never Trash — restore anything in one click.

== Changelog ==

= 0.1.2 =
* Fixed: comments submitted through the REST API now receive their verdict; previously the check was spent but the verdict was never applied.
* Fixed: the comment text is now sent exactly as the commenter wrote it (previously apostrophes and quotes arrived escaped), so repeated identical comments are also recognised from the local cache again.
* Added: a single review request in the admin, shown on the Comments and Synth Antispam settings screens only after Synth has caught at least 20 spam comments over 14 days or more. You can postpone it or switch it off, and nothing is offered in exchange for a review.

= 0.1.1 =
* Updated the plugin description and the directory listing text. No functional changes.

= 0.1.0 =
* Initial public release.
* AI spam detection for comments, pingbacks and trackbacks (comment form, XML-RPC).
* Verdict column with spam category and confidence.
* One-click site key and connection test.
* Strict mode and configurable fallback for outages or exhausted checks.
* Multisite support.

== Upgrade Notice ==

= 0.1.2 =
Comments submitted through the REST API now receive their verdict, comment text is sent exactly as written, and a single review request you can postpone or switch off.

= 0.1.1 =
Text-only update (plugin description and listing); no functional changes.

= 0.1.0 =
Initial public release.
