== Changelog ==

= 1.6.2 =
* Needs GuardForge Pro 1.4.0 or newer if you run the add-on, as before.

= 1.6.1 =
* Deleting the last Forge plugin or add-on on a site now removes everything Forge stored on the site, on every site of a network: the statistics settings and the install ID, the shared job queue with its scheduled events, and the other settings and caches the Forge plugins share. While another Forge product stays, only this plugin's own share goes.
* A Forge plugin deleted a moment earlier in the same bulk delete is no longer counted as still installed, and a Forge plugin for WooCommerce no longer keeps the shared data on the site for good.
* Needs GuardForge Pro 1.4.0 or newer if you run the add-on, as before.

= 1.6.0 =
* **This release needs GuardForge Pro 1.4.0 or newer if you run the add-on.** The licence class, the License screen, the upgrade prompt and the badge and credit count in the screen header moved out of this plugin into the GuardForge Pro add-on, and so did the Alerts screen. This plugin no longer carries any licence code: it asks only whether the add-on and its features are available. The License and Alerts items in the GuardForge menu now come from the add-on, so a site without it has neither.
* An add-on older than 1.4.0 is not started beside this version, because it would look here for code that has moved. A notice on the GuardForge screens, the plugin list and the dashboard asks for the update and says where to get the package; only users who can update plugins see it. Your logs, baselines and settings are kept.
* On a site without the add-on, the Lockdown, Geo-IP, Malware scanner and Cloudflare screens say that they are part of GuardForge Pro, a separate plugin, and link to its page. Nothing on them is locked or greyed out.
* The Vulnerabilities screen says the same: this package carries no advisory source of its own, and the advisory database is served by the add-on.
* The .htaccess protection is written and removed through WordPress's own insert_with_markers(), between the same BEGIN GuardForge and END GuardForge lines as before, so the block an earlier version wrote is replaced where it stands. If a BEGIN line has no END, the plugin leaves the file alone. The empty pair of lines that deactivation leaves no longer counts as protection in the hardening score.

= 1.5.0 =
* Settings: a check that has not run yet, such as the file-integrity scan on a new site, is listed as “Not run yet” with a “Run first scan” button instead of under “Not working”. The “Not working” tile counts only what stopped working: a protection that ships off and that nobody has switched on counts under “Needs a decision”.
* A file-integrity monitor that is switched on but has completed no scan after a day is reported as not working, with a “Run scan now” button that returns to Settings and a note on WP-Cron. A monitor switched on outside Settings (WP-CLI, a deploy script, a database edit) is dated from the moment it is found on, so it can be reported too.
* A finished integrity scan refreshes the hardening score at once, so the “Not working” tile no longer stays red until the next daily capture, and a failure while the score is recalculated no longer breaks “Run scan now” or the Settings save.
* Reactivating the plugin puts the daily integrity scan back on the schedule when the monitor is switched on, and switching the monitor on or off outside Settings moves its schedule with it. A new install still schedules nothing.
* The sentences of the four checks on the first screen are translated into the plugin's fifteen languages instead of showing English.
* Rows that tie on the column they are sorted by now always show in the same order: the Cases list and the first screen, the Login log and Audit log pages (a row could land on two pages or on none), the Unblock IP list, the hardening checklist and the evidence of a login burst.
* With GuardForge Pro 1.3.1 or newer, the notice after “Fetch now” on the Vulnerabilities screen tells a failed download from a refresh and gives the reason.
* Next to the licensed Pro add-on, the first screen reports the add-on's vulnerability feed instead of saying the feed is not part of this version.

= 1.4.0 =
* With the Pro add-on's vulnerability database, the refresh card no longer says the database updates every day when the add-on's own daily update is switched off, and it asks for a newer add-on when the installed one has no daily update of its own.
* New option vulndb_pro_updates, on by default: the add-on's new “Keep the vulnerability database up to date” switch is saved through it.
* Deleting the plugin leaves the job queue the Forge plugins share in place while another Forge plugin or add-on is still on the site. The check used to miss add-ons, and Rank Forge installed from wordpress.org, so the queue could be dropped while a neighbour still had jobs in it.

= 1.3.6 =
* The new-administrator alert fires however a user becomes an administrator: the profile screen, the Users list, the REST API, WP-CLI, wp_update_user(), set_role(), add_role(), add_cap() or a direct write to the user's capabilities. A promotion through the REST API, add_role() (which is what wp user add-role calls), add_cap() or a direct write used to send no e-mail and no alert. Saving an administrator with the role they already had is not reported as a new one.
* Role changes made through the REST API are written to the audit log as one entry with the roles the user now has and the roles they had, instead of an entry with an empty role. Several roles, and a role taken away, are written in full.

= 1.3.5 =
* No change in this package.

= 1.3.4 =
* The file-integrity monitor is off until you switch it on, and activation no longer schedules its daily lookup: the comparison asks api.wordpress.org for the official checksums, and no request goes out before you ask for one. "Run scan now" still runs a single scan whenever you press it.
* The Geo-IP screen names the path it actually reads the MaxMind database from, instead of assuming the default uploads folder.
* Generated admin markup — the log pagers, the enrolment QR code, the header logo and the alert channel cards — is escaped against an allowlist where it is printed.

= 1.3.3 =
* This plugin no longer writes into WordPress's own directories. A modified or missing core file is reported exactly as before, and the screen now links to Dashboard → Updates: re-installing WordPress is what puts core files back.
* The alerts screen loads its script through WordPress instead of printing it into the page.
* Two standing notices — the warning about an add-on too old for this core, and the staging banner — appear on GuardForge screens, the plugin list and the dashboard instead of on every screen in the admin.

= 1.3.2 =
* No change in this package.

= 1.3.1 =
* Request input is unslashed and sanitised on every read, queries on the plugin's own tables are prepared, and admin screens escape their output.
* Two-factor and login CAPTCHA keys carry the full guardforge_ prefix. A challenge started before the update still completes, and uninstall now also removes the CAPTCHA transients.
* The plugin's site is now guardforge.app, and the plugin header links there.

= 1.3.0 =
* **This release needs GuardForge Pro 1.2.0 or newer.** An older add-on is switched off whole rather than left running, and here that is the safer half: an add-on from before 1.2.0 has never heard of staging mode, so on a copy of your site it would go on mailing alerts and pushing to Cloudflare while the banner promised silence. Until you update it alerts to Telegram, Slack or a webhook, the Cloudflare firewall push, automatic lockdown, patch by update, the WAF, malware scanning, AI threat analysis, required 2FA and audit export are unavailable. Your logs, baselines and settings are kept. An admin notice names both versions.
* Deleting the last Forge product on a site now clears the job queue the Forge plugins share — its table, its two scheduled events and its version marker. While any other Forge product is still installed, deleting this one leaves the queue exactly as it is, because the pending jobs in those rows are that product's too.

= 1.2.1 =
* Brand: new product icon and admin logo.

= 1.2.0 =
* New in Pro, off until you switch it on: patch by update. When an advisory matching an installed plugin names the version that fixes it, and WordPress is offering an update that reaches that version, GuardForge installs it — on cron, one plugin per run, never during a page request — then e-mails you and writes it to the audit log. An allow list and a deny list decide what it may touch; a staging copy, a plugin directory that is a symbolic link, and anything that failed in the last twenty-four hours are all left alone. An update that is merely newer is never enough: without a named fixed version the rule does nothing.
* Cloudflare's published edge addresses are now refreshed daily instead of being a snapshot compiled into the plugin, and IPv6 edges are covered for the first time. A stale list is why a site behind a newer Cloudflare edge would log, count and geo-locate every visitor as the edge itself. If the refresh fails the previous list stays in use, and the Cloudflare screen says when it last changed.
* New: the audit log is hash-chained. Every row now carries the hash of the row before it and a hash of its own contents, and a "Verify chain" button walks the table. Edit one field on one row and that row stops matching its own hash; delete a row and the gap shows, because the next row points at a hash nothing in the table produces. Neither was visible before, and an audit log that can be tidied up after the fact is the one piece of evidence an intruder has a reason to touch. Rows written before this release have no hashes and are read as unhashed rather than as a break. Writes are serialised with a real database-level lock, so two events at the same instant cannot fork the chain and have it read afterwards as tampering.
* New in Pro, off until you switch it on: lockdown can throw itself. Twenty different addresses crossing the brute-force threshold inside ten minutes, or — separately, and off by default — a single finding from the malware scanner, and GuardForge closes registration, comments, XML-RPC and anonymous REST writes without waiting for you to read an e-mail. It lifts itself after an hour by default, both ends go into the audit log, you are e-mailed, and it never fires on a copy of your site. The ten-minute window is measured against your site's own clock rather than the database server's, which on a host where the two disagree is the difference between a trigger that fires on the first lockout and one that never fires at all.
* Pro alerts are configurable by a person. The screen was a textarea of raw JSON that printed your bot tokens and HMAC secrets back in clear text; it is now a card per channel — Telegram, Slack or a signed webhook — with an enabled toggle, a minimum severity of its own, and a "Send test" that really sends through the same dispatcher and saves nothing. Secrets are encrypted at rest and shown only as their last four characters; leaving the field empty keeps what is there, so the first save through the new form migrates an old clear-text secret without you touching it. Added with them: a digest you can set to immediate, hourly or daily, and quiet hours in your site's own timezone — which a lockdown or a malware finding bypasses, because those are the two you wanted waking up for.
* New: file integrity against the official checksums. Every core file, and every plugin hosted on wordpress.org, is now compared against the hashes wordpress.org publishes for that exact release, so the screen can say what a scan-to-scan comparison never could: this file is not part of the official build. That is the shape a backdoor has, and it is invisible to a snapshot diff — a shell that was already on disk when the baseline was taken looks exactly like an unchanged core file.
* Each file gets one of five verdicts: official, modified, not in the official build, missing, or no reference. A premium or custom plugin has no published hashes anywhere, and neither does any theme; those files keep the scan-to-scan comparison and are never counted as clean. wordpress.org answers 404 for everything it does not host, and reading that as a pass would put a tick beside the files an attacker is likeliest to be living in.
* The scan asks wordpress.org at most ten times per run and caches every answer, so a site with sixty plugins is covered over a few days rather than in one burst that a cron tick cannot survive. What it could not reach is queued for the next run and the screen names it.
* The hardening score no longer calls a baseline fresh on a site whose own scan found a file nobody published. "File integrity has a fresh baseline — Done" over a web shell located an hour ago was the worst line this plugin could print.
* New: staging / development mode. GuardForge works out whether it is running on a copy — from WP_ENVIRONMENT_TYPE, from a GUARDFORGE_STAGING_MODE constant in wp-config.php, or from a list of staging host names you keep — and then stops acting on the outside world. Lockouts are counted and logged as "soft-locked" but nobody is shut out, the lockout, new-administrator and file-integrity e-mails stop, and with Pro installed the alerts, the Cloudflare firewall push and the credit-spending AI analysis stop too.
* Nothing that can switch staging mode ON is kept in the database. The host list is matched against the address the site is actually served on, so copying a staging database onto your live site stops it matching the instant the domain changes; the only manual on-switch is a wp-config.php constant, which no database dump can carry. Two independent ways to force production exist for the case where a real site is mistaken for a copy.
* While the mode is on it says so on every admin screen and in the admin bar on the front end, names the signal that decided it, and counts what it has held back. The transition on and off is written to the audit log.
* New: a hardening score. Twenty-one checks of your own site, one number out of a hundred, and a checklist where every row states what it is worth and links to the screen that changes it. The weights are published in the readme so the number can be argued with — a score nobody can audit is a score nobody should believe.
* A check that cannot apply to your server leaves the sum instead of scoring zero, so an nginx site is no longer marked down for not having an Apache file, and can still reach 100.
* The score is recorded once a day and kept for a year, so the screen can show whether the site is getting better or quietly drifting.
* New, and off until you ask for it: a public badge. It publishes a letter grade and the month it was checked, worked out only from the eleven things a stranger could already determine by loading your site. Your score and every private check stay private. The link carries an unguessable token, can be reissued or revoked, logs nothing, and does not exist at all until you switch it on.
* Anyone who can log in can now enrol in 2FA. The screen was registered for administrators only, so on a site with one admin and twenty editors, twenty people had no way to switch on a second factor — while the plugin's own handlers had been willing to enrol them the whole time. GuardForge → 2FA is now open to every logged-in role, exactly like WordPress's own profile screen; every other GuardForge page, and the site-wide "require 2FA for administrators" policy, stay administrators-only.
* Staging mode names only the channels the installed add-on actually holds back. The alert and Cloudflare guards live in GuardForge Pro 1.2.0; beside an older add-on the banner and the settings panel stay quiet about them rather than promising a suppression that is not on the disk.

= 1.1.0 =
* Two-factor authentication is now part of the free plugin: TOTP enrolment, the login challenge and single-use recovery codes, for your own account. Requiring 2FA of every administrator stays in Pro.
* File-integrity monitoring now covers plugins and themes on the free plugin too — it is a setting, not a licence.
* The 2FA enrolment screen draws its QR code on your own server. It used to fetch the image from a third-party QR service with your TOTP secret in the URL — the whole second factor, handed to somebody else on every page view. GuardForge now ships its own encoder; nothing about enrolment leaves the site.
* Deleting the plugin now cleans up after itself. There was no uninstall routine before this release, so removing GuardForge left all five of its tables in the database — including your two-factor secrets. Enrolments are always removed now; the rest goes only if you ask, with a new setting for it.
* Audit log export moved into the Pro add-on, where its code now lives — the free plugin no longer carries an exporter it will not run.
* Free screens no longer show disabled Pro controls.
* GuardForge Pro 1.1.0 pairs with this release; nothing in the plugin enforces it, so an older add-on keeps running with the features it does not have simply absent.

= 1.0.1 =
* Audit log export (CSV, streamed) — the Pro feature the licence already listed.
* Removed two licence entries with no implementation behind them (multisite admin, daily PDF report); they return when built.

= 1.0.0 =
* Initial public release. WordPress.org Free core; Pro features delivered by the separate GuardForge Pro add-on.
