=== CloudScale Cyber & DevTools ===
Contributors: andrewjbaker
Tags: security, code block, mermaid, AI security scan, WordPress hardening
Requires at least: 6.2
Tested up to: 7.1
Requires PHP: 8.0
Stable tag: 1.10.1077
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Free AI security audit, 2FA, passkeys, brute-force protection, SMTP and server logs, plus code blocks that draw Mermaid diagrams.

== Description ==

A free, open-source security and developer toolkit centred on an **AI Cyber Audit**.

= Security Features =

* **AI Cyber Audit** and **Deep Dive**: config, plugins, users, HTTP, DNS, TLS, AI-triaged code
* **Active Vulnerability Verification** (optional, subscription)
* **Firewall**, **rate limiting**, **geoblocking**, **IP reputation**, **CVE shields**, **bot verification**, **Live Traffic**
* **Vulnerability feed** for plugins, themes, core and server software; report only; never updates or deactivates anything by itself
* **File Integrity** with **repair** and **quarantine**; **database malware scan**
* **Login security**: hidden login URL, lockout, **2FA** (TOTP, email, passkeys) for any role, breached-password check
* **Comment Guard** and **comment flood limits**, **Activity Log**, **SSH Monitor**, **Server Logs**
* **Quick Fixes** with undo; **Fix Recipes** you run over SSH, never the plugin
* **Alerts** by Telegram, email or signed webhook; **weekly report**

= Developer Tools =

* **Code block** with **Mermaid diagrams**, **charts** (Chart.js, Vega) and bundled highlight.js
* **Export to PDF**, **Code Block Migrator**, **Social Preview Diagnostics**, **SMTP Mail**, **Performance Monitor**
* **Custom 404 Page** with mini-games (off by default)

== External services ==

Every service below is idle until you configure or click something; **none is contacted on a default install.** Admin-screen hyperlinks (FAQ links, help pages on https://help.cloudscale.consulting) are never requested.

CloudScale features below call api.cloudscale.consulting, provided by CloudScale Consulting (this plugin's author): terms of use https://cloudscale.consulting/terms/, privacy policy https://cloudscale.consulting/privacy/.


* **API keys:** your own, stored in wp_options, sent only to that service, never to CloudScale.
* **Never sent:** post content, visitor data, database contents, passwords or credentials, unless a section says otherwise.
* **Never on a visitor's page load,** except Telegram alerts you set up (failed login, REST authentication failure, CSP report). Otherwise only a scheduled task or an administrator's action.

= WordPress.org APIs (core, plugin and theme integrity checks) =

**Service:** WordPress.org, https://wordpress.org
**Endpoints:** https://api.wordpress.org/core/checksums/1.0/, https://api.wordpress.org/plugins/info/1.0/ and /1.2/, and on downloads.wordpress.org the per-plugin checksum file (plugin-checksums/SLUG/VERSION.json) theme zip (theme/SLUG.VERSION.zip), and on Repair only release/wordpress-VERSION-no-content.zip or plugin/SLUG.VERSION.zip
**Data sent:** Your WordPress version and locale, or an installed plugin or theme's slug and version. No site URL, user data or credentials.
**When:** During a File Integrity check, Site Audit scan or Update Risk Assessor run. **Off by default:** nothing is requested until you switch on the scheduled File Integrity check under Security > File Integrity, or run a scan yourself.

WordPress.org Privacy: https://wordpress.org/about/privacy/ (no separate terms are published for these APIs)

= Own-IP discovery (CloudScale whoami) =

**Service:** CloudScale Consulting (this plugin's author), https://cloudscale.consulting
**Endpoint:** https://api.cloudscale.consulting/whoami
**Data sent:** No parameters, body, licence key or site URL; the user agent is this plugin's name and version. The server sees only the requesting address, which is the answer.
**When:** At most daily, by a scheduled task or WP-CLI, only when this site cannot find its own public address locally. **Off by default:** it runs after you turn on CVE Alerts (Security tab), so the auto-blocker never blocks the server's own address. The browser asks it only on Check my address (Visitor IP Source).

CloudScale privacy policy: https://cloudscale.consulting/privacy/
Terms of service: https://cloudscale.consulting/terms/

= Cloudflare IP ranges (trusted-proxy list for visitor-IP resolution) =

**Service:** Cloudflare, Inc., https://www.cloudflare.com
**Endpoint:** https://api.cloudflare.com/client/v4/ips
**Data sent:** Nothing: a plain GET, no key or account.
**When:** Only after you choose Cloudflare under Protect > Visitor IP Source: on saving, then weekly. **Off by default;** until then the bundled ranges are used.

Cloudflare Privacy: https://www.cloudflare.com/privacypolicy/
Terms: https://www.cloudflare.com/website-terms/

= OpenStreetMap basemaps (map tiles for the attack-origin map) =

**Service:** OpenStreetMap Foundation, https://www.openstreetmap.org
**Endpoint:** tile.openstreetmap.org (and its a, b, c subdomains; path zoom/x/y.png)
**Data sent:** Tile coordinates, plus what a browser sends with any image request (IP address, user agent); nothing about the events plotted.
**When:** Only while an administrator views the attack-origin map on the Login Security tab, from the admin's browser, never the server. Not bundled: see the FAQ.

OpenStreetMap https://www.openstreetmap.org/copyright
Privacy: https://osmfoundation.org/wiki/Privacy_Policy
Terms: https://osmfoundation.org/wiki/Terms_of_Use

= CloudScale vulnerability lookup (CVE and advisory data during a security scan) =

**Service:** CloudScale Consulting (this plugin's author), https://cloudscale.consulting
**Endpoints:** https://api.cloudscale.consulting/vuln-lookup and https://api.cloudscale.consulting/v1/software-releases (sends nothing)
**Data sent:** One component per request: plugin/theme slug, WordPress version, or server product/version (PHP, MySQL/MariaDB, nginx/Apache, curl, ImageMagick, Redis, Memcached, SQLite). Never your hostname, site URL or plugin list.
**When:** During a scan or host software check you start, and daily once CVE Alerts is on (Security tab); **off by default.** Cached for 12 hours.

CloudScale privacy policy: https://cloudscale.consulting/privacy/
Terms of service: https://cloudscale.consulting/terms/

= CloudScale breached-password lookup (optional, off by default) =

**Service:** CloudScale Consulting (this plugin's author), https://cloudscale.consulting
**Endpoint:** https://api.cloudscale.consulting/v1/pwned-range/
**Data sent:** First 5 hex characters of the password's SHA-1 hash; never the password or full hash.
**When:** On setting a password, only once you choose Warn or Block (Login tab).

CloudScale privacy policy: https://cloudscale.consulting/privacy/
Terms of service: https://cloudscale.consulting/terms/

= CloudScale PHP advisories, IP reputation, virtual patch and AI model lists =

**Service:** CloudScale Consulting (this plugin's author), https://cloudscale.consulting
**Endpoints:** https://api.cloudscale.consulting/v1/php-advisories (plus the release snapshot), https://api.cloudscale.consulting/ip-reputation-feed, https://api.cloudscale.consulting/v1/virtual-patches and https://api.cloudscale.consulting/v1/plugin-models
**Data sent:** Nothing. No parameters.
**When:** PHP advisories: every 6 hours, hourly while a release is announced, once you tick Send PHP advisory alerts (Security tab). IP reputation and virtual patches: daily, or on "Refresh now" (Firewall tab), once set to Log or Block. **All off by default.** Checks use cached copies only. Model list: weekly at most, once an AI key is saved.

CloudScale privacy policy: https://cloudscale.consulting/privacy/
Terms of service: https://cloudscale.consulting/terms/

= Wordfence Intelligence API (optional, only when you supply an API key) =

**Service:** Defiant, Inc. (Wordfence), https://www.wordfence.com
**Endpoint:** www.wordfence.com/api/intelligence/v3/vulnerabilities/scanner
**Data sent:** Nothing about your site; the public dataset is downloaded and matched locally.
**When:** Only during a security scan with a Wordfence Intelligence API key entered; never contacted without a key.

Wordfence Privacy: https://www.wordfence.com/privacy-policy/
Intelligence Terms: https://www.wordfence.com/wordfence-intelligence-terms-and-conditions/

= Anthropic Claude API (optional, several AI features) =

**Service:** Anthropic PBC, https://www.anthropic.com
**Endpoint:** api.anthropic.com/v1/messages
**Data sent:** AI Cyber Audit: site URL; plugin, theme, PHP and server software versions; permission flags, debug settings, role counts, table prefix, wp-config.php flags, scheduled event names; usernames of administrators with a weak login or no two-factor; SSH settings, banner and fail2ban status; exposed-file check results; a homepage fingerprint; login, 2FA and application-password counts. The deep dive adds HTTP security headers, flagged PHP snippets, core-integrity and malware file paths and salts status. Site Audit: sample post titles and URLs, inactive themes, unwritable file paths, exposed folders. Site Optimizer: DB table/column names, a plugin's changelog, or text you paste. Thumbnails: your post title/summary, when chosen as image-prompt writer.
**When:** An AI-labelled button, "Test connection" or a scheduled scan you enabled, with Anthropic selected.
**API key:** Your own Anthropic key.

Anthropic Privacy: https://www.anthropic.com/legal/privacy
Terms: https://www.anthropic.com/legal/commercial-terms

= Google Gemini API (optional, several AI features) =

**Service:** Google LLC, https://ai.google/
**Endpoint:** generativelanguage.googleapis.com/v1beta/models/
**Data sent:** Same as Anthropic above.
**When:** As Anthropic above, with Gemini selected.
**API key:** Your own Google AI key.

Google Privacy: https://policies.google.com/privacy
Terms: https://policies.google.com/terms

= OpenAI API (optional) =

**Service:** OpenAI, LLC, openai.com
**Endpoints:** api.openai.com/v1/responses (image prompt and AI features) and api.openai.com/v1/images/generations (the image)
**Data sent:** Your post title and excerpt, for the image prompt. AI features: as Anthropic above.
**When:** On "Generate" in the AI Image Generator with ChatGPT as prompt writer, "Test connection" on saving a key, or AI features with OpenAI selected.
**API key:** Your own OpenAI key.

**Privacy policy:** https://developers.openai.com/api/docs/guides/your-data
**Terms of service:** openai.com/policies/terms-of-use/

= CloudScale Managed AI (optional, paid monthly subscription) =

**Service:** CloudScale Consulting (this plugin's author), https://cloudscale.consulting
**Endpoints:** https://api.cloudscale.consulting (base), then https://api.cloudscale.consulting/v1/messages and https://api.cloudscale.consulting/v1/messages-async (AI text), https://api.cloudscale.consulting/v1/images (featured image), https://api.cloudscale.consulting/v1/openai-chat (image prompt), https://api.cloudscale.consulting/checkout, https://api.cloudscale.consulting/checkout-start, https://api.cloudscale.consulting/status, https://api.cloudscale.consulting/cancel, https://api.cloudscale.consulting/refund-request, https://api.cloudscale.consulting/recover, https://api.cloudscale.consulting/recover/verify, https://api.cloudscale.consulting/contact; https://api.cloudscale.consulting/billing-portal and https://api.cloudscale.consulting/manage open in your browser on Manage card, never called by the plugin.
**Data sent:** The AI audit data described above. Image generator: the post title and a content summary (/v1/openai-chat), then the resulting prompt (/v1/images). For billing and Leave us a message, your email address, site URL and message typed, and any PayPal subscription reference not yet confirmed on this site. Every request carries your licence key, plugin name and version as headers. Never your own API keys.
**When:** AI data only while "Use Managed AI" is on and you run an AI feature. Billing data only when you click a billing button or Send message; with a license key stored, the Home tab refreshes status at most every 10 minutes. The PayPal button sends the product name, your hostname and the optional email above it, if you typed one, to /checkout-start, to follow up a failed checkout.
**Stored:** A per-site license key and subscription status, cached in wp_options.

CloudScale privacy policy: https://cloudscale.consulting/privacy/
Terms of service: https://cloudscale.consulting/terms/

= CloudScale Active Vulnerability Verification (optional, requires the paid subscription) =

**Service:** CloudScale Consulting (this plugin's author), https://cloudscale.consulting
**Endpoint:** https://api.cloudscale.consulting/nuclei-scan
**What it is:** The only active test: CloudScale's servers send real HTTP requests to a site you own. No scanning tool ships with or runs from this plugin. Ownership is enforced server-side (FAQ).
**Data sent:** Your license key, the domain to scan, and a scan id while polling. No site content, database contents, credentials or configuration.
**Data received:** Findings and a diff against your previous scan, cached in wp_options.
**When:** Only when an administrator clicks "Run Active Verification" or "Check ownership", and while polling that result. Never automatic.

CloudScale privacy policy: https://cloudscale.consulting/privacy/
Terms of service: https://cloudscale.consulting/terms/

= CloudScale Fix Recipe Usage (optional, opt-in, off by default) =

**Service:** CloudScale Consulting (this plugin's author), https://cloudscale.consulting
**Endpoint:** https://api.cloudscale.consulting/remediation-event
**What it is:** Counts of which fix recipes are used. Off unless you tick "Help CloudScale improve these fix recipes" in Security > Quick Fixes.
**Data sent:** Recipe name, which button (viewed, copied, downloaded), finding severity, recipe pack and plugin versions, and your license key.
**Not sent:** Site URL, IP address, server paths, file contents, or anything you type into a placeholder.
**When:** Only while opted in, when you open, copy or download a recipe.

CloudScale privacy policy: https://cloudscale.consulting/privacy/
Terms of service: https://cloudscale.consulting/terms/

= CloudScale Fix Order (optional, opt-in, off by default) =

**Service:** CloudScale Consulting (this plugin's author), https://cloudscale.consulting
**Endpoint:** https://api.cloudscale.consulting/remediation-plan
**What it is:** Suggests a fix order and warns of a fix known to break sites on your WordPress version. Off unless you tick "Ask CloudScale what order to fix these in" on the Site Audit tab.
**Data sent:** Which checks failed, as identifiers from a fixed list, with severity and category; your WordPress, plugin and recipe-pack versions; your license key.
**Not sent:** Finding titles or descriptions, site URL, IP address or file contents.
**Data received:** An order and warnings only, never code.
**When:** Only while enabled, after an audit finishes.

CloudScale privacy policy: https://cloudscale.consulting/privacy/
Terms of service: https://cloudscale.consulting/terms/

= PayPal API (optional, managed-AI subscription checkout) =

**Service:** PayPal Holdings, Inc., https://developer.paypal.com
**Endpoints:** on api-m.paypal.com: v1/oauth2/token (access token), v2/checkout/orders (create an order, and its capture sub-path), v1/billing/subscriptions/ID (subscription status); www.paypal.com/sdk/js (browser JavaScript SDK). Sandbox mode: the same paths on api-m.sandbox.paypal.com.
**What it is:** Managed AI payment. Direct API calls only where wp-config.php defines PayPal credentials; otherwise checkout goes through CloudScale and only the SDK loads.
**Data sent:** Your email address, the amount, a reference (email and product name) and the return addresses (admin screens on your site). The SDK load carries CloudScale's public PayPal client ID.
**When:** Only when you initiate or check the status of a PayPal subscription on the Home tab.

PayPal Privacy: https://www.paypal.com/us/legalhub/paypal/privacy-full
Terms: https://www.paypal.com/us/legalhub/paypal/useragreement-full

= Cloudflare API (optional: cache purge, comment rate limit, edge blocking) =

**Service:** Cloudflare, Inc., https://www.cloudflare.com
**Endpoints:** api.cloudflare.com/client/v4/zones (purge_cache; /rulesets/phases/http_ratelimit/entrypoint, /rulesets/{id}/rules/; /firewall/access_rules/rules)
**Data sent:** Your API token, plus URLs to invalidate, one rate-limit rule (/wp-comments-post.php, 3 per 10 seconds per IP), or blocked IPs.
**When:** With zone ID and token: purge on publish/update or "Purge cache"; daily and on Test, one non-page URL; rule changes via buttons; Edge WAF Push (off by default) every 5 minutes.

Cloudflare Privacy: https://www.cloudflare.com/privacypolicy/
Terms: https://www.cloudflare.com/terms/

= Telegram API (optional, alert notifications) =

**Service:** Telegram Messenger Inc., https://telegram.org
**Endpoints:** api.telegram.org, Bot API methods sendMessage (alerts) and getUpdates (chat-ID lookup)
**Data sent:** The alert text and your site URL: PHP errors (type, file, line); failed logins, REST authentication failures, blocks and honeypot hits (username tried, IP address, country; for REST also the request method, path and user agent); scan, file integrity, CSP (blocked URL, page), performance and licence summaries; the contact form's email and message. Chat-ID lookup: only your bot token.
**When:** Only with your bot token and chat ID set, when one of those events happens and its alert is enabled. Chat-ID lookup only on "Fetch Chat ID".

Telegram Privacy: https://telegram.org/privacy
Terms: https://telegram.org/tos

= GitHub / GitLab (optional, GitHub Sync feature) =

**Service:** GitHub, Inc. and GitLab Inc.
**Endpoints:** api.github.com and gitlab.com (or your self-hosted GitLab host)
**Data sent:** Repository owner, name, branch and file path, in the URL; no token or site data (public repositories only).
**When:** When an editor links a code block to a file or clicks Sync, and daily, refreshing linked blocks.

GitHub Privacy: https://docs.github.com/en/site-policy/privacy-policies/github-general-privacy-statement
Terms: https://docs.github.com/en/site-policy/github-terms/github-terms-of-service
GitLab Privacy: https://about.gitlab.com/privacy/
Terms: https://about.gitlab.com/terms/

= DB-IP GeoIP Database (optional, Visitor Map / Geo feature) =

**Service:** DB-IP.com, https://db-ip.com
**Endpoint:** download.db-ip.com (this month's dbip-city-lite .mmdb.gz)
**What it is:** A free monthly GeoIP database, queried locally; no IP address reaches DB-IP.
**Data sent:** Only a GET for the file.
**When:** First on a "Download / Update GeoIP Database" click; then a scheduled task fetches each new month's file. Untick the monthly refresh to stop it.

DB-IP Privacy: https://db-ip.com/privacy.php
Terms: https://db-ip.com/db/lite.php#faq6

= Uptime heartbeat (optional, off by default, your own endpoint) =

**Endpoint:** An https URL you set (Activity tab), your own monitor, not CloudScale.
**Data sent:** Site URL, timezone, time, a one-time value, your token (bearer and signing key), plugin version, and signals: plugin active, admin count and ID hash, firewall mode, file-change count, mu-plugin hash. No names, emails, paths or IPs.
**When:** Every 5 minutes once on with a URL and token; while deactivated too if you install the loader. Never from a standby.

= Your own server addresses (optional, your own endpoint) =

**Endpoints:** Addresses you enter, never CloudScale: the alert webhook URL (Alerts tab; signed JSON alerts and weekly report), `csdt_fpm_probe_url` (Diagnostics tab; its /fpm-status page) and `csdt_devtools_cf_origin_refresh_url` (Thumbnails tab, Cloudflare; your own page cache).
**Data sent:** A GET with this plugin's user agent; the origin refresh adds your host name, the page path and an X-Cache-Refresh header.
**When:** FPM status when you view it or run Auto-detect; origin refresh just before a Cloudflare purge. Nothing while empty.

== Installation ==

1. Upload the plugin folder to /wp-content/plugins/
2. Activate the plugin through the Plugins menu in WordPress
3. Go to Tools > Cyber and Devtools to configure

== Frequently Asked Questions ==

= What AI providers are supported? =

Anthropic Claude (Sonnet 5 and 5.5, Opus 5 and 5.5, Haiku 4.5 and 5.5), Google Gemini (3.8 Flash, 3.6 Flash, 3.5 Flash Lite and 3.1 Pro Preview) and OpenAI (GPT-6.1 Sol, GPT-6 Astra and GPT-6 Luna). You supply your own API key, no keys are stored anywhere other than your WordPress database (wp_options). A free Gemini tier is available.

= What does Live Traffic store? =

Nothing until you switch it on (Firewall tab). Then, for each request that reaches WordPress: the time, address, Cloudflare country, method, path without its query string, HTTP status, the first 200 characters of the user agent, and the user ID if signed in. Never the query string, body, cookies or referrer. Rows are kept for 24 hours, at most 20,000, and uninstall drops the table. Pages served from a cache never reach PHP, so they are not shown.

= How does the Fake Crawler Check work? =

A visitor claiming to be Googlebot, Bingbot, Applebot, YandexBot, Baiduspider or Yahoo Slurp has its address checked with forward-confirmed reverse DNS, through your server's own resolver. The lookup runs after the response is sent and the result is cached, so no visitor waits. It starts in Log only; Block refuses a proven fake on its next request.

= How does virtual patching work? =

When a plugin or theme you have installed has a published vulnerability, a virtual patch refuses the one kind of request it needs, for example a non-number in one AJAX action's id field, until you update. The patches come from a feed signed by CloudScale and checked against a key built into the plugin; each one is drafted from the difference between the vulnerable and the fixed release and approved by a person. A patch is a description, not code: it names an AJAX action, REST route or file inside that component, and one of a fixed set of checks the plugin carries. Only patches for a component installed here at an affected version are used, and each stops applying when you update. Off by default; Log only records matches as vpatch in Recent Matches, Block also refuses them. Signed-in editors are never checked, and the firewall's exceptions apply.

= What is the Out of Band Heartbeat? =

An optional signed message, every 5 minutes, from WordPress to an uptime monitor you run (such as a Cloudflare Worker). It stops exactly when WordPress stops, and carries a few signals the monitor can alert on: a new administrator, the firewall switched off, a new must-use plugin, persistence watch changes, the plugin deactivated. The token is stored encrypted. Off by default.

= What does Upgrade Verification check? =

Every 5 minutes the plugin compares a fingerprint of what the site runs: the WordPress version, the PHP version the web server uses, the active theme and each active plugin with its version. When any of them changes, whether through an update in WordPress, copied files or a rebuilt server image, it waits 2 minutes and checks the front page, the login page, the REST API, admin AJAX, PHP fatals since the change, that each changed part reports its new version, core files against WordPress's checksums (only while file integrity is on), and that scheduled tasks still run. A failure sends one alert. It is a report only: nothing is rolled back or changed, and the requests go only to your own site. On by default; never runs on a standby copy.

= What does Edge WAF Push send to Cloudflare? =

Only IP addresses, each as one IP Access Rule on your zone with a note marking it as this site's. An address qualifies when it is blocked here now and was blocked by hand, is a repeat offender, made a firewall attack in Block mode, or is a proven fake crawler in Block mode; the newest 200. An administrator's address from the last 30 days, your never-block list, private or reserved ranges, Cloudflare's own ranges and the site's own address are never sent. Off by default; switched on it starts in Dry run, which only reads. Rules without this site's mark are never changed. The token needs Zone > Firewall Services > Edit.

= What does Emergency Lockdown do? =

One switch (Login Security tab, or the link in an alert) for when someone may already be in. While it is on: no registration, no new administrators or role grants, no plugin or theme install, update, edit, activation or theme switch, no application password or XML-RPC logins, and 3 failed logins refuse an address for 30 minutes. Other administrators are signed out. Two-factor and the hidden login page are untouched, and your own and recent administrator addresses are never refused. Release it in wp-admin. The automatic trigger only reports unless you set it to Enforce.

= How does the deep dive scan avoid HTTP gateway timeouts? =

The plugin uses fastcgi_finish_request() to close the browser connection immediately, then continues the scan in the same PHP-FPM worker. A progress bar polls every 3 seconds. This does not depend on WP-Cron.

= How do I change the syntax color theme? =

Go to Tools > Cyber and Devtools > Code Block tab > Code Block Settings panel. Select your preferred theme from the dropdown and click Save.

= What languages are supported for code highlighting? =

highlight.js with auto-detection. The bundled build registers 37 languages, and accepts 112 names in total once aliases are counted: Bash, Shell, Python, JavaScript, TypeScript, PHP, SQL, Go, Rust, Java, C, C++, C#, Objective-C, Ruby, Swift, Kotlin, Lua, Perl, R, VB.NET, WebAssembly, JSON, YAML, XML, HTML, CSS, SCSS, Less, Markdown, Makefile, Dockerfile, GraphQL, INI/TOML, diff and plain text.

HCL and Terraform are accepted and highlighted with the INI/TOML grammar: highlight.js has no HCL grammar of its own, and rather than ship an unmaintained third-party one we map it to the closest grammar that is bundled, which covers keys, quoted strings, numbers, booleans and comments.

= Can the code block draw charts? =

Yes. Set the block's language to Chart (or paste a ```chart fence) and write one setting or series per line, for example `type: bar`, `title: Revenue by quarter`, `x: Q1, Q2, Q3`, then `2025: 12, 15, 17` and `2026: 19, 23, 26`. Types are line, area, bar, hbar, stacked, pie and donut. Other settings: subtitle, x-label, y-label, y-min, y-max, prefix, suffix, source, height, colors. A line whose x values are numbers is plotted to scale, and a series may give its own points as `0:400, 5:700`. Readers get a legend that hides or shows each series, tooltips, a Source button and the light or dark toggle. A chart with a mistake shows its source and the line that is wrong. The bundled chart library loads only on pages with a chart block.

= Can I paste a Chart.js, Vega or Vega-Lite chart? =

Yes. A code block whose language is Chart or Chart.js (a ```chart or ```chartjs fence) and whose content starts with `{` is read as a Chart.js configuration in JSON, the `type`, `data` and `options` object that Chart.js documents and that AI assistants write. A block whose language is Vega or Vega-Lite (a ```vega or ```vega-lite fence) is drawn from its JSON specification. Colours you set are kept and anything you leave unset follows the block's light or dark theme. It must be JSON: no functions, comments or trailing commas. A Vega chart may load data files from your own site only, so put the data in the specification as `values` or upload the file to the media library. Vega expressions are run by Vega's interpreter rather than eval, so charts draw on sites with a strict Content Security Policy. Each library loads only on pages that use it.

= Can the code block draw Mermaid diagrams? =

Yes. Set the block's language to Mermaid diagram (or paste a ```mermaid fence) and the page draws the diagram. A Source button shows the text, Copy copies it, and the theme button redraws it in light or dark. If the diagram has an error, the block shows its source with a one-line reason. The bundled Mermaid library loads only on pages with a Mermaid block, and draws with its strict security level.

= Why are the map tiles not bundled with the plugin? =

A world basemap is hundreds of megabytes, so it cannot practically ship inside a plugin. The tiles are OpenStreetMap's own rendering, credited in the map's attribution control. They are fetched by the administrator's own browser only while the attack-origin map is on screen, never by the server and never on a visitor's page load; the full disclosure is under External services above.

= What third-party libraries are bundled, and under what licences? =

The following open-source libraries are bundled in minified form. Unminified source is available at the linked repositories.

* **highlight.js** 11.12.0, syntax highlighting engine. The bundled `assets/highlight.min.js` is upstream's own published browser build, unmodified, and `assets/hljs-lang-dockerfile.min.js` plus the `assets/hljs-*.min.css` themes are upstream files from the same 11.12.0 release. License: BSD 3-Clause. Source: https://github.com/highlightjs/highlight.js
* **Mermaid** 12.2.0, draws diagrams from text in the code block. The bundled `assets/mermaid.min.js` is upstream's own published `dist/mermaid.min.js`, unmodified, with its licence in `assets/mermaid.LICENSE.txt`. License: MIT. Source: https://github.com/mermaid-js/mermaid. The file contains documentation and licence links from Mermaid and the libraries it is built from (chevrotain.io, langium.org, www.eclipse.org, lodash.com, underscorejs.org, jquery.org, openjsf.org, opensource.org, tldrlegal.com, engelschall.com, en.wikipedia.org) in error messages and licence comments; it contacts none of them.
* **Chart.js** 4.5.1, draws charts from text in the code block. The bundled `assets/chartjs.min.js` is upstream's own published `dist/chart.umd.min.js`, unmodified, with its licence in `assets/chartjs.LICENSE.txt`. License: MIT. Source: https://github.com/chartjs/Chart.js. The file names its project site (www.chartjs.org) and a colour library it includes (github.com/kurkle/color) in its banner; it contacts neither.
* **Vega** 6.4.0, draws Vega charts in the code block. The bundled `assets/vega.min.js` is upstream's own published `build/vega.min.js`, unmodified, with its licence in `assets/vega.LICENSE.txt`. License: BSD-3-Clause. Source: https://github.com/vega/vega. It contacts no server unless a chart names a data file, and the plugin only allows files on your own site.
* **Vega-Lite** 6.5.0, compiles the shorter Vega-Lite grammar to Vega. The bundled `assets/vega-lite.min.js` is upstream's own published `build/vega-lite.min.js`, unmodified, with its licence in `assets/vega-lite.LICENSE.txt`. License: BSD-3-Clause. Source: https://github.com/vega/vega-lite. The file names https://vega.github.io/schema/vega/v6.json, the Vega schema's address, as a label it writes into the specifications it compiles; it never requests it.
* **vega-interpreter** 2.3.2, evaluates Vega expressions without eval. Upstream publishes it as an ES module only, so `assets/vega-interpreter.js` is upstream's readable `build/vega-interpreter.js` with its import line and its export line replaced so a script tag can load it; nothing else is changed and the file is not minified. License: BSD-3-Clause (`assets/vega.LICENSE.txt`). Source: https://github.com/vega/vega/tree/main/packages/vega-interpreter.
* **QRCode.js**, QR code generator. License: MIT. Source: https://github.com/davidshimjs/qrcodejs
* **Leaflet** 1.9.4, interactive maps (project site https://leafletjs.com, named in the file's own banner; the plugin never contacts it). The bundled `assets/leaflet.min.js` is upstream's own published `dist/leaflet.js`, unmodified, with its copyright banner intact. License: BSD 2-Clause. Source: https://github.com/Leaflet/Leaflet
* **MaxMind DB Reader** (PHP), GeoIP2 database reader, modified: namespace prefixed with CSDT\ and exception messages escaped. License: Apache 2.0. Source: https://github.com/maxmind/MaxMind-DB-Reader-php
* **DejaVu Sans Bold**, the font `assets/fonts/DejaVuSans-Bold.ttf`, used by GD to draw text onto the social-share images the plugin generates. Bundled because a generated image must render identically on every host, and a server font cannot be relied on to exist. License: DejaVu Fonts License (a permissive, GPL-compatible free licence derived from the Bitstream Vera Fonts License). Source: https://github.com/dejavu-fonts/dejavu-fonts

All licences are GPL-compatible.

= Does the plugin add public REST endpoints? =

Two, both open by necessity and both limited. `csdt/v1/csp-report` (POST) receives the violation reports browsers send without credentials; it exists only while CSP violation logging is on, accepts 20 reports per address per 10 minutes, cuts each field to 512 characters and keeps the latest 100. The Custom 404 leaderboard (`hiscore/<game>`, GET and POST) lets visitors read and post game scores; posting is limited to 5 per address per game per 10 minutes. Nothing else is reachable without logging in.

= Why does the Performance Monitor's query list ask me to edit wp-config.php? =

WordPress records each query and its timing only while the SAVEQUERIES constant is on, and a plugin may not switch that on for your whole site. Add `define( 'SAVEQUERIES', true );` to wp-config.php (the DB tab has a copy button), and remove it when you have finished, because it records every query on every request. Everything else in the monitor works without it.

= Is syntax highlighting loaded from a CDN? =

No. highlight.js, Mermaid, Chart.js and Vega ship inside the plugin and load from its own /assets/ directory. No CDN request is ever made for syntax highlighting, diagrams or charts.

= Outbound admin links (browser navigation only, the plugin sends no data) =

Some admin screens contain ordinary hyperlinks you may click; the plugin itself never contacts these sites: ipinfo.io (whois lookup beside logged attacker IPs), documentation and product pages cited in audit findings and help text (gravityforms.com, wpml.org, learndash.com, affiliatewp.com, memberpress.com, paidmembershipspro.com, berocket.com, support.microsoft.com, help.yahoo.com, zoho.com, support.google.com), the pages where you obtain your own API key or manage your own account (console.anthropic.com, aistudio.google.com, platform.openai.com, dash.cloudflare.com), this plugin's own help site (help.cloudscale.consulting), the public advisory page for a CVE or advisory listed in a scan result (a link the vulnerability lookup returned, or wordfence.com/threat-intel/… for a Wordfence advisory), and social-preview debuggers (facebook.com, linkedin.com, twitter.com). Separately, Social Preview Diagnostics, when you run it, fetches your own pages, your robots.txt and the image each page names in its og:image tag (wherever that image is hosted) the way link-preview crawlers do, sending their published user-agent strings to your own site; those strings contain www.facebook.com and www.linkedin.com, which the plugin never contacts.

= How do I allow third-party services in the Content Security Policy? =

The policy the plugin builds allows your own site and nothing else. Anything your pages load from elsewhere (analytics, adverts, fonts, embedded video, a payment form) you add yourself in the Allowed sources field, one directive per line, for example `script-src analytics.example.com`. Start in Report-Only: the Violation Log lists what real browsers would have blocked, and an Allow button beside each entry adds that source for you.

The plugin ships no list of third-party hosts and no service presets. Earlier versions did; a site upgraded from one keeps sending its policy as Report-Only until the panel is saved once, so nothing that used to be allowed is suddenly blocked.

= What is CloudScale Managed AI, and do I need it? =

No, every AI feature works with your own Anthropic, Google Gemini or OpenAI key, which you supply and pay for directly. Managed AI is the alternative for people who would rather not hold an API key at all: about $10 USD a month via PayPal.

**One allowance, three capabilities:** 200 text requests, 30 generated images and 500,000 characters of narration each month. Demanding work runs on **CS-Text-Pro** and everyday work on **CS-Text-1**.

**It does not lock you in.** If the managed service is ever unavailable, the plugin falls back to your own key automatically, so keeping a key configured is worth doing either way. The same subscription also powers the CloudScale SEO plugin. Card details are entered on PayPal's hosted checkout and never reach this plugin, and your own API keys are never sent to CloudScale.

Tiers are named by capability (CS-Text-1, CS-Text-Pro, CS-Voice-1, CS-Voice-Pro, CS-Image-1) because which engine serves a tier is chosen per job and changes as better or cheaper options appear.

= Can Active Vulnerability Verification scan a site other than mine? =

No. It only ever scans the site your CloudScale license is registered to, there is no field for a different target. To audit another domain, that site needs its own license.

The scan is rate-limited per license and has a hard time limit per run.

= Why do the fix commands ship inside the plugin instead of being downloaded? =

Because they are shell commands you may run as root. Fetching them over the network would mean a compromised or impersonated server could hand your server arbitrary commands to run with full privileges. The recipes are bundled in the plugin and are never downloaded, so what you see is what shipped, reviewed, in the version you installed.

= Does the plugin check my own site in the background? =

Not unless you switch it on. The Exposed folders check is off by default: until you tick "Exposed folders check" in the AI Cyber Audit panel it makes no requests and schedules nothing.

Switched on, once a day it asks this site, and only this site, for the real folders on its own server, for the files whose content proves a leak (a .git folder, a copy of wp-config.php, a .env file, a backup, a database tool) and for about 1,500 common leftover, backup and staging folder names, in small batches. It contacts no outside service, has no target field and cannot be pointed at any site but its own. The result is kept in your own database for the site audit to read.

A name only becomes a finding when the content of the answer proves it, such as a real file listing or a real git file. A page that merely answers 200 is not a finding. The check sees the site as its own server reaches it; a proxy in front of the site may be stricter. Unticking the box stops the daily run and removes the stored result.

The Certificate Expiry Warning (Activity tab) is off by default too. Switched on, once a day it opens a secure connection to this site's host name and to up to 10 other host names you list, reads the certificate each one presents and closes the connection. No page is requested and nothing about your site is sent. It contacts no CloudScale or third-party service, only the names you entered, and refuses one that points at a private address.

= Does the plugin tell me about PHP security releases? =

Yes. PHP Advisory Alerts (Security tab, beside Host Software) sends one alert each when php.net announces an upcoming security release (with the release time in your timezone and in UTC), when the release for your PHP branch is out (saying whether this site is affected), when your branch is end of life and will not get the fix, and when this site runs the fixed version. The version checked is the one your web server runs, read on an admin page load, never the command line PHP. Alerts go to Telegram, if set up, and the admin email address. It changes nothing on your site, and the dashboard widget shows the current state. Off by default: tick Send PHP advisory alerts to start it.

= Does the plugin hide the WordPress version? =

Only if you ask it to. "Hide WP Version" is off by default. Switched on, the plugin removes the generator tag that WordPress prints in the page head and the version numbers on script and style links. Switch it off and WordPress prints them again.

= Does the plugin ever change or remove anything without being told to? =

Nothing runs on a schedule that alters your site, and the plugin never executes a shell command: the fix recipes above are text for you to run yourself. Two presentation defaults apply from activation, each with its own switch in the admin: WordPress's emoji image script is skipped so emoji render natively, and each post shows an Export to PDF button. The custom 404 page and the performance monitor are both off until you switch them on, so a new install leaves your theme's 404 template and your admin requests exactly as they were.

No Quick Fix deletes or deactivates another plugin. "List inactive plugins", offered by the site audit when it finds deactivated plugins still on disk, is read-only: it names each one with its version so you can review them and remove any you no longer need from Plugins, Installed Plugins yourself. The plugin does not edit wp-config.php either: the "Limit to 5 Revisions" fix applies its cap at runtime, and can be undone.

== Screenshots ==

1. A Mermaid flowchart drawn by the code block, straight from the text in the post, with the Source, theme and Copy buttons
2. AI Cyber Audit panel with Quick Fixes and scan controls
3. Deep dive scan results with scored findings and remediation steps
4. Server Logs tab with source picker, filters, and log viewer
5. Code block on the frontend with Atom One Dark theme and copy button

== Changelog ==

= 1.10.1077 =
* Changed: the AI tab shows two panels, Managed AI Settings (pay monthly, no key) and Client AI Settings (your own key), so the choice is clear.
* Changed: the CSP, Notifications and new Comment Protection (Comment Guard plus Comment Flood) panels start collapsed. CSP warns to start in Report-Only.
* Changed: every Off or Not Working row in Control Status links to the setting that fixes it.
* Changed: tab introductions are colour coded, with the tip in its own callout.
* Fixed: faint lines between attack map tiles on scaled and high-DPI screens.
* Fixed: the AI key and its Show button did not appear on the AI tab.

= 1.10.1075 =
* New: AI Settings tab, second after Home. Choosing a provider and pasting a key used to sit at the bottom of the Security tab, where people missed it. A status line says whether AI features are ready and what is missing.
* Changed: the AI key field's Show button is always visible and reveals what you typed. AI features now count as ready only when the selected provider has a key.
* Changed: a failed AI key test explains the network error (DNS, blocked port 443, timeout, TLS or certificate store) instead of showing only the cURL code.
* Changed: Edge WAF Push and Edge Gateway sit together under Edge Protection on the Request Firewall tab, marked Enterprise, with a short note on why most sites run both. A Cloudflare authentication error now says how to fix the token.

= 1.10.1072 =
* First release on the WordPress.org plugin directory. No functional change from 1.10.1070; 1.10.1071 and 1.10.1072 are release builds.

= 1.10.1070 =
* New: Edge Gateway panel (Request Firewall tab) and `wp csdt edge` commands to pair a CloudScale Edge Gateway, a separately installed reverse proxy. Nothing runs until one is paired.
* Changed (WordPress.org review): plugins and themes are never updated unattended; Update now does it. External services names the CloudScale API's terms and privacy policy up front; /proc is read through WordPress's filesystem class.
* Changed: the DejaVu Sans credit now links to the font's source repository on GitHub.
* Changed: the bundled Mermaid library is updated to 12.2.0, upstream's latest release.

= 1.10.1069 =
* NEW: a red warning on every admin screen, a critical audit finding and one email when the server's PHP has php2shell (CVE-2026-109618, CVE-2026-109619). Built in, works with every check off, sends nothing; only a PHP upgrade fixes it.

= 1.10.1068 =
* FIX: the licence card's Disconnect link and subscription ID were low contrast.

= 1.10.1067 =
* FIX: Home's Top Security Issues were unreadable grey on white; the CS Monitor stylesheet no longer restyles the plugin's own page.
* IMPROVED: the active tab is a white tab, and tabs and buttons lift on hover.

= 1.10.1066 =
* FIX: a standby promoted to primary books every security schedule it had switched off again, including the scheduled audit; `wp csdt crons` checks them.

= 1.10.1065 =
* IMPROVED: Home tab redesigned as a dashboard: score summary, top issues with a Fix button each, then setup.

= 1.10.1064 =
* NEW: when the daily CVE check or PHP advisory alerts are off, the plugin's screens, the Site Audit and the weekly report say new CVEs are not being checked, with a one-click Turn on that states what each sends. External Lookups is now CVE Alerts, beside PHP Advisory Alerts.
* IMPROVED: Home tab links are now readable buttons, and the admin menu reads Cyber & Devtools.

= 1.10.1063 =
* NEW: Emergency Lockdown (Login Security tab), one reversible switch for a suspected break-in.

= 1.10.1062 =
* NEW: Edge WAF Push panel (Firewall tab). Copies the worst blocks (by hand, repeat offenders, firewall attacks, fake crawlers) to Cloudflare IP Access Rules so they are refused before PHP, and removes them when the block ends here. Never pushes admin, allow-listed, reserved, Cloudflare or own addresses; touches only its own marked rules. Off by default, starts in Dry run.
* IMPROVED: the Cloudflare API token is stored encrypted and the Credentials panel shows only whether it is set.

= 1.10.1061 =
* IMPROVED: the Site Audit and the weekly security report now include PHP security advisories: critical when a PHP security release is announced or out and this site is not on the fixed version, high when the PHP branch is end of life, with the release time in your timezone and UTC and a link to PHP Advisory Alerts. Read from stored data only, so it works with the alerts off and contacts nothing.
* FIXED: Upgrade Verification no longer skips its PHP fatals check where PHP logs to the container's output (Docker). The plugin records each fatal page load itself, so the check counts fatals with or without a readable log.

= 1.10.1060 =
* IMPROVED: the Activity Log has a search box (user, IP, event and detail, across every page) that combines with the event filter and carries into Export CSV; its settings and toolbar now line up. "Where to fix this" opens, scrolls to and highlights the exact panel, and each finding names the same place. Tab introductions are a card aligned with the panels, with the tip on its own line. Audit findings that name posts link to each post's view and edit screens.

= 1.10.1059 =
* NEW: Upgrade Verification panel (Activity tab). After core, PHP, the theme or a plugin changes (including file copies and image rebuilds), checks the front page, login, REST API, admin AJAX, new PHP fatals, that each change landed whole, core checksums and the schedule, and alerts on a failure. Report only, contacts only your own site. On by default.

= 1.10.1057 =
* NEW: Out of Band Heartbeat panel (Activity tab). The uptime heartbeat is now signed (HMAC with a timestamp and one-time value, so it cannot be replayed), carries signals your monitor can alert on, stores its token encrypted, and can keep beating from a must-use loader while the plugin is deactivated. Off by default; existing heartbeat settings carry over.

= 1.10.1056 =
* NEW: PHP Advisory Alerts. One alert each when php.net announces a PHP security release, when the fix for your branch is out (affected or not, and what to do), when your branch is end of life, and when this site runs the fix. Uses the web server's PHP, never the command line's; times in your timezone and UTC. Off by default; alerts only.

= 1.10.1055 =
* NEW: Virtual Patching (Firewall tab). A signed feed of narrow per-CVE rules, applied only to a plugin or theme installed here at an affected version, each lifting itself once you update. Off by default; Log only records matches as vpatch in Recent Matches, Block refuses them.

= 1.10.1054 =
* FIXED: the Fake Crawler Check no longer raises a PHP error at the end of a request that claims to be a search engine crawler, so its verdict is now stored.

= 1.10.1053 =
* NEW: Firewall rule exceptions. Lift one rule on one path, optionally for one parameter only, instead of switching it off for the whole site. The Except button on a Recent Matches row fills in the line. A path containing .. or // never gets an exception.
* NEW: Fake Crawler Check. A visitor claiming to be Googlebot, Bingbot, Applebot, YandexBot, Baiduspider or Yahoo Slurp is checked with forward-confirmed reverse DNS after the response is sent, and the verdict is cached. Log only by default; Block is refused while every claim has failed, which means the site is reading a proxy's address.
* NEW: Live Traffic, a feed of the requests that reach WordPress, with filters, pause and auto-refresh. Off by default. Never stores the query string, body, cookies or referrer; kept 24 hours, at most 20,000 rows.

= 1.10.1048 =
* NEW: Export to PDF. Every post has a button, for every visitor, that saves the article as a PDF through the browser's own Save as PDF, so the text stays selectable and the links work. Only the article prints, without the menu, sidebar, ads or comment form, whatever the theme. Headings stay with the text they introduce, and images, tables, code blocks and diagrams that fit on a page are never split; a wide diagram shrinks to the page instead of being cut off. Nothing is generated on the server or sent anywhere. On by default; switch it off under Code Block Settings.

= 1.10.1046 =
* Changed: every security report (AI audit, deep dive, Site Audit) opens with an executive summary: a verdict, a score gauge, findings by severity, what to fix first and what is already good. Each severity has its own colour, heading and timeframe, and colours now print.
* Changed: the deep dive merges a finding that both halves report into one.
* Fixed: the cron audit no longer reports this plugin's own scheduled tasks as belonging to no active plugin.
* Fixed: country flags are built without mb_chr(), which WordPress 7.1 polyfills.
* Changed: the AI model list class is renamed CSDT_Model_Feed, under the plugin's own prefix.
* Fixed: the weekly AI model-list request now sends a fixed User-Agent instead of WordPress's default, which carries the site URL, so it sends nothing, as documented. Site Audit and Social Preview requests to your own site name the plugin too.
* Changed: the remember-me tick box is reworded in every language when the Website field is removed.

= 1.10.1027 =
* Changed: alert settings (Telegram, environment label, mute) are stored under this plugin's own prefix, migrated automatically, and kept in sync with the other CloudScale plugins.

= 1.10.1023 =
* Added: the Other Alerts Show button on the dashboard widget flashes when there is an alert you have not opened yet.

= 1.10.1022 =
* Added: Other Alerts on the dashboard widget folds behind a Show / Hide button, the same as Recent Security Events.

= 1.10.1020 =
* Fixed: the Telegram Alert Log banner no longer reports old delivery failures as a present fault. It shows red only when the newest alert failed, and otherwise says delivery is working and when the last failure was.
* Fixed: the Telegram Alert Log is readable on a phone. Each message now sits on its own full-width row.

= 1.10.1019 =
* Changed: while Comment Guard refuses links, the comment form's remember-me tick box says "Save my name and email in this browser" and no longer mentions a website.

= 1.10.1018 =
* Changed: in the weekly security report, low risk exposed files (the WordPress readme and licence file, a leftover folder) are listed and marked low risk, and no longer count as items that need attention.

= 1.10.1017 =
* Added: the weekly security report has an Exposed folders section. It lists each folder or file the Exposed folders check found served to visitors who are not signed in, and counts them as items that need attention. When the check is off or has not finished, it says so.
* Changed: the message a visitor sees when a comment is refused for containing a link now says first that the comment was not posted, then why, then what to do.

= 1.10.1016 =
* Changed: Auto for Anthropic now uses the newest Claude models: Sonnet 5.5 for standard scans, Opus 5.5 for deep scans and Haiku 5.5 for triage. The older models stay in the list if you prefer them.

= 1.10.1015 =
* Changed: the External services section now lists what the Site Audit sends to your chosen AI provider (sample post titles and URLs, inactive themes, unwritable file paths, exposed folders). Two admin notes that named only Anthropic and Gemini now name OpenAI too.

= 1.10.1014 =
* Fixed: a JavaScript error inside another plugin's script is sent as a critical alert again. 1.10.1012 turned all of them into warnings. Only one known harmless error, Akismet's link preview on the Comments screen, is listed as a warning.

= 1.10.1013 =
* New: OpenAI can be chosen as the AI provider for scans and the other AI features, beside Anthropic Claude and Google Gemini. It uses the OpenAI key you may already have saved for Featured Images.
* Changed: the model lists hold current models. Gemini now offers 3.8 Flash, 3.6 Flash, 3.5 Flash Lite and 3.1 Pro Preview, and Auto uses 3.8 Flash. Gemini 2.5 stays available for Google projects that already use it. Claude adds Sonnet 5.5, Opus 5.5 and Haiku 5.5.
* Fixed: a Gemini model chosen in AI Settings is still shown after the page reloads. It used to show Auto, and the next Save replaced your choice with Auto.
* Fixed: the model chosen for writing image prompts is kept. A model whose name contains a dot, which is every Gemini model, was stored without the dot and showed as Automatic afterwards.
* Fixed: Test Key for Gemini works with a new Google key. It tested a model Google only serves to older projects.

= 1.10.1012 =
* New: Comment Guard can refuse comments that contain a link (off by default). Tick "Refuse comments that contain a link" in the Comment Guard panel. A visitor who posts a link in the text or the Name, or fills in the Website field, is told why and the comment is not saved. The Website field is removed from the comment form. Nobody is blocked for it.
* New: the Code Block Migrator converts blocks made by the Code Block Pro plugin. The code is carried over exactly, with its language where this block highlights it. Code Block Pro does not need to be installed.
* Fixed: the Editor Tools bar on a post could sit under the post or be missing on some themes. It is now directly above the post on every theme.
* Fixed: the Explain button is always the last button on the right of a panel header.
* Fixed: a JavaScript error inside another plugin's script is listed as a warning that names the plugin, and is no longer sent as a critical alert.

= 1.10.1010 =
* New: Certificate Expiry Warning (Activity tab, off by default). Once a day the plugin reads the HTTPS certificate of this site and up to 10 other host names you list, and alerts through your usual alert channel when one has 7 days or less left, then daily until it is renewed. A host that cannot be reached is shown as not checked and never alerts.

= 1.10.1005 =
* New: Copy on a drawn diagram or chart copies a picture of it (PNG, with its title and legend) instead of the source text. The Source view still copies the text, and a browser that cannot copy a picture copies the text and says so.
* Fixed: on a phone the code block toolbar broke its language badge and Source button in the middle of a word. Labels now stay on one line and the credit link shortens instead.

= 1.10.1004 =
* Internal: the build now runs on Linux as well as macOS, so a release can be started from the deploy bot.

= 1.10.1003 =
* Fixed: the zip offered by the help site Download button was not refreshed by releases and had fallen far behind. Each production release now publishes it and checks the version it serves.

= 1.10.1001 =
* FIXED: a pasted Chart.js chart that described only one of its axes drew the other axis in dark grey, unreadable on a dark block. Every axis a chart uses now takes the block's colours.
* FIXED: a long title or subtitle on a Vega or Vega-Lite chart ran off the edge on a phone. Plain titles are now written as page text above the chart, so they wrap.

= 1.10.999 =
* NEW: Code block: Vega and Vega-Lite charts. A block whose language is Vega or Vega-Lite is drawn from its JSON specification, themed light or dark with the block. Vega 6.4.0 and Vega-Lite 6.5.0 are bundled, load only on pages that use them, run expressions without eval, and may load data files from your own site only.
* NEW: Code block: a Chart.js configuration in JSON (type, data, options) is now drawn. The languages Chart, chartjs and chart.js all work.
* FIXED: Code block: a chart written as Chart.js JSON in a chartjs block was shown as code instead of drawn.
* FIXED: Code block: in the light theme the language badge, theme switch, line number switch and Copy button were white on a pale toolbar and could not be seen.
* FIXED: Code block charts: an axis with a unit suffix no longer shortens its numbers, so 1000 with suffix M reads 1,000M rather than 1kM.

= 1.10.997 =
* NEW: Code block: a block whose language is Chart is drawn as a line, area, bar, stacked, pie or donut chart from a few lines of text, with a clickable legend, tooltips, light and dark themes and a Source button. Chart.js 4.5.1 is bundled and loads only on pages with a chart.

= 1.10.995 =
* NEW: Featured image: "Infographic" article style. It makes a branded, multi-panel explainer (headline, numbered steps, a central diagram, result cards, key points and a takeaway) with its text written from the post, and no title is stamped over it.
* CHANGED: Featured image: the Technology and Infographic article styles now ask for the real logos, wordmarks and brand colours of the companies and products the post is about. Other styles are unchanged.

= 1.10.992 =
* FIXED: Alerts are now listed on the dashboard widget and the plugin home screen as well as being sent. Security alerts (a new administrator, vulnerable software, a blocked attack, a security control that stopped working) appear under Recent Security Events. Everything else (a failed backup, a PHP error, a server health alarm) appears in a separate Other Alerts list. Which list an alert belongs to is stated by the code that raises it, never guessed, and alerts keep a place in the list however many firewall blocks arrive after them.
* FIXED: The home screen's event times were pale grey on white and are now readable.

= 1.10.990 =
* FIXED: Alerts the plugin sends (a new server software vulnerability, a security control that stopped working, a changed file, a new administrator, a server health alarm) now also appear under Recent Security Events on the dashboard, one row per alert, linking to the alert log. They used to reach Telegram and email only.
* NEW: Server Health Alarms (Activity tab), off by default. When switched on, the plugin reads the server once a minute and sends an alert when CPU, memory, disk IO wait, temperature or free disk space stays past its limit for three minutes, when a Raspberry Pi reports low supply voltage, and after a restart, saying roughly how long the server was not running. Limits are adjustable, each alarm is sent at most once an hour, and nothing is read or stored while it is off.

= 1.10.988 =
* FIXED: The "security controls are switched on but not working" email no longer names the daily vulnerability re-check when that re-check is switched off, and no longer keeps reporting Telegram delivery as failing after a single failed send. A recorded failure is now re-tested on the next hourly check with a call that sends no message, and clears itself when Telegram answers.

= 1.10.987 =
* CHANGED: Comment flood alerts are sent at most once an hour, so a flood that lasts all night no longer sends a message every ten minutes.

= 1.10.986 =
* NEW: Comment Flood Protection has a site-wide gap between comments. Set it to 5 and the site accepts at most one comment every five seconds from all addresses together; the rest get HTTP 429, and five or more refusals in ten minutes send one alert. Off by default, and it needs no Cloudflare.
* NEW: The security audit reports a site that takes comments with no site-wide comment rate limit, with a one-click fix that sets the gap to 5 seconds.

= 1.10.985 =
* NEW: Comment Guard can refuse a comment the same visitor already sent on the same post, even when they changed their name before sending it again. WordPress alone only catches a repeat when the name and email are both unchanged. Off by default: Security, Comment Guard.

= 1.10.984 =
* CHANGED: Two more labels in the dashboard widget (the 2FA Details link and the "today" badge) are darker, so all widget text meets the 4.5:1 contrast level.

= 1.10.983 =
* CHANGED: The AI deep scan no longer asks a model to restate the site inventory first. The inventory the plugin builds in code is used directly, so the scan makes one fewer AI call and a reworded plugin name can no longer reach the vulnerability lookup.

= 1.10.982 =
* FIXED: The dashboard widget no longer lists a failed login older than four weeks under Recent Security Events.
* FIXED: The Blocked IPs and Failed logins tiles no longer print the label over the number on a narrow screen.
* CHANGED: Small grey text in the dashboard widget is darker and easier to read.

= 1.10.981 =
* FIXED: The readme and the Quick Fix description no longer say the plugin edits wp-config.php or deletes inactive plugins. It stopped doing both in earlier versions.

= 1.10.980 =
* CHANGED: Request metadata is now validated with WordPress's own UTF-8 check on the line that reads it. No change in behaviour.

= 1.10.979 =
* CHANGED: The firewall's read of the incoming request and the read-only routing arguments each sit in their own small documented function. No change in behaviour.

= 1.10.978 =
* FIXED: The monthly GeoIP refresh now runs only for a database downloaded in this plugin. A copy kept by another CloudScale plugin no longer triggers a download here.

= 1.10.977 =
* CHANGED: Password policy: the reset and registration forms now carry a form check, and a new password posted without it is refused with a message to reload the page.
* CHANGED: The quarantine folder no longer writes its own .htaccess. It sits inside the log folder, whose deny rule already covers it.
* CHANGED: Scan findings no longer build links to advisory sites. A finding shows its CVE ID, plus a link only when the vulnerability lookup returned one.

= 1.10.976 =
* CHANGED: Code this plugin has in common with other CloudScale plugins (Telegram alerts, licence card, standby check) now loads under this plugin's own names, so two CloudScale plugins never share a class or an AJAX action.

= 1.10.975 =
* CHANGED: Settings and social image data saved under the plugin's oldest names are renamed once after updating. Uninstall removes only names that carry the plugin's prefix.

= 1.10.974 =
* DOCS: External services lists everything the AI audit sends, including server software versions, SSH settings and check results.
* CHANGED: The fix recipe usage checkbox names everything it sends, including your licence key.
* CHANGED: Two Custom 404 games are renamed Alien Defence and Digger.
* CHANGED: The bundled Mermaid library is updated to upstream's latest release at the time.

= 1.10.972 =
* NEW: Exposed folders check. It does nothing until you switch it on. Tick "Exposed folders check" in the AI Cyber Audit panel and once a day the plugin asks your own site for folders and files it should not serve: source control data, copies of wp-config.php, .env files, backups, database tools and open file listings. A finding needs proof in the content of the answer, never just a 200.
* FIXED: Comment moderation emails showed a proxy's internal address in place of the commenter's.
* CHANGED: The passkey step-up stores a fingerprint of the session, never the session token. Expect one extra passkey prompt after updating.
* CHANGED: Score submissions from the 404 games and CSP violation reports are checked before their handlers run.

= 1.10.963 =
* FIXED: Behind Cloudflare or another proxy, the automatic blocker could block the site's own address, which refused the site's own background requests. The plugin took the proxy's address for its own. It now recognises its own requests when they arrive, with no outside lookup, and never treats a proxy address as the site's own.

= 1.10.906 =
* CHANGED: The Passkeys panel header shows how many passkeys you have registered, and updates when you remove one.
* FIXED: Checkboxes on the plugin's screens showed two ticks on top of each other; they show one.

= 1.10.905 =
* CHANGED: The admin tab, the login screen's action, the shields' request checks and the logged-in cookie checks now read the request in one place, with the same results as before.

= 1.10.904 =
* DOCS: External services now says Managed AI image generation sends the post title and a content summary, and that Telegram alerts can be sent during a visitor's request, with the REST alert's method, path and user agent listed.

= 1.10.903 =
* CHANGED: The admin menu entry is labelled "Cyber Devtools" with the shield icon, with no emoji in the label.
* CHANGED: File Integrity decides which checksum entries are core by what it checks (the root, wp-admin, wp-includes), with no hard-coded content folder name.
* DOCS: External services now lists the two addresses you can set yourself (the PHP-FPM status address and the origin refresh address).

= 1.10.902 =
* FIXED: A bundled auto-block alert now gives the time a block lifts from when the block started, not from when the digest was sent, and says so when a block has already lifted.
* FIXED: A copy of the site restored from a backup no longer sends the auto-block digest it inherited from the original site.

= 1.10.901 =
* CHANGED: CloudScale Cyber and Devtools is a top-level admin menu again, directly under CloudScale SEO AI, and no longer also appears under Tools. Old Tools links and bookmarks redirect to it.
* CHANGED: The External services section now follows the Description, and names only the services the plugin itself contacts.

= 1.10.890 =
* CHANGED: The plugin's log files and newly generated social-share images now live in its own uploads folder, cloudscale-cyber-devtools. Existing logs are moved there once; social images made earlier stay where they are, so links already shared keep working.

= 1.10.889 =
* CHANGED: The GeoIP database now lives in the plugin's own uploads folder, cloudscale-cyber-devtools/geo. An existing download is moved there once, so no new download is needed.

= 1.10.888 =
* CHANGED: External services now names every CloudScale endpoint by its full address, exactly as the plugin calls it. Each of those addresses opens in a browser and describes itself.
* CHANGED: Visitor IP Source no longer contacts CloudScale when the panel opens. The independent address check runs only when you click Check my address.
* CHANGED: Recompressing a featured image for WhatsApp saves the result as a new file beside the original, the way the WordPress image editor does, instead of overwriting it. The original stays in the Media Library folder, so existing links to it keep working.

= 1.10.887 =
* CHANGED: The Email Activity Log is now off by default and has its own switch (Mail tab, Email Activity Log, Record outgoing emails). It stores message bodies, which include password reset links, so no site records mail unless an administrator chooses to. Switching it off deletes the stored entries.
* FIXED: Leave us a message (licence card) now reaches CloudScale. It used to post through the Telegram bot configured on your own site, so the message went to you while promising a reply from us.

= 1.10.886 =
* CHANGED: The firewall's request inspector now states, next to the code, why it reads the request unmodified: sanitising first would remove the attack payloads its rules detect.

= 1.10.885 =
* REMOVED: The bundled APCu object-cache drop-in. The Object Cache panel now shows which object cache is active, removes a CloudScale drop-in left by an older version, and links to the plugin directory search for a maintained object-cache plugin.
* REMOVED: The EXPLAIN button in the performance monitor's query list. The full SQL and call stack are still shown for every captured query.
* CHANGED: The scheduled File Integrity check is off on a new install, because every check downloads its reference checksums from WordPress.org. Switch it on under Security > File Integrity. Sites that already ran it keep it on.
* CHANGED: Cloudflare's IP ranges are fetched only after you choose Cloudflare as the visitor IP source. Until then the plugin uses the ranges bundled with it and contacts Cloudflare not at all.
* CHANGED: The plugin no longer runs WordPress core's or LiteSpeed Cache's hooks itself. Loopback TLS verification and XML-RPC state are read without invoking core filters, LiteSpeed is purged and bypassed through its own response headers and API, and a two-factor brute-force lockout is recorded by the plugin's own failed-login handlers.
* CHANGED: The two-factor step takes "Remember Me" from WordPress's own sign-on rather than reading the login form, so it also works for logins that do not come from the standard form.
* CHANGED: Theme integrity downloads identify themselves with the plugin's name and version only, never the site URL.
* CHANGED: Two batch lookups (brute-force usernames, and SEO fields in the Site Audit) no longer build SQL placeholder lists.
* CHANGED: Uninstall removes only names that carry the plugin's prefix.

Entries before 1.10.885 are kept in the author's development changelog.
