Version 5.2.0
September 29, 2026
Security
- Webhook URLs can no longer reach internal addresses. Every webhook POST — the "Send test" button and real alerts alike — now requires an
http/httpsURL whose host resolves only to public addresses, pins the connection to those addresses (so DNS cannot be rebound between the check and the connect), refuses URLs carrying credentials, and does not follow redirects. Before, anyone allowed to manage webhooks could make the server POST to the cloud metadata service or the private network. Webhooks pointed at a private address are now refused when saved. - "Send test" reports the HTTP status only. It used to echo the first 500 characters of the response body (or the connection error) back to the browser, which let the test button read internal endpoints. The body is still logged.
- Visitor IP hashes are keyed. IPs were stored as a plain SHA-256, which can be reversed by hashing all 2^32 IPv4 addresses. They are now an HMAC keyed on the site's security key and the day. A migration clears the hashes stored by earlier versions.
Added
allowPrivateWebhookHostsconfig setting for sites that genuinely alert an internal endpoint. Set it inconfig/control-tower.php; likedisableLicenseEnforcement, it is not exposed in the CP. Redirects are still not followed.- Integration checks (
tests/integration/checks.php,http.php) and ECS.
Fixed
- Webhook alerts sent from the queue no longer fail. Building the alert link read the request's host, which a console queue runner doesn't have, so every Slack/Teams/generic webhook sent from a CLI queue worker threw.
Changed
- Dashboard, rule and webhook screens use plugin CSS classes on Craft's spacing scale instead of inline styles. The alert email keeps its inline styles, which email clients need.
- Visitors active when you upgrade are counted once more, because session hashes are now keyed too.
Version 5.1.4
August 19, 2026
Added
- Craftnet license validation. Control Tower now checks its license key status through Craft's built-in Craftnet integration and gates itself when the install isn't licensed. Enforcement is strict: only
validandtrialunlock the plugin. - License screen at Control Tower → License, showing the current status, Craft's license issues, and a key field (admins only). This page is never gated — it's how a locked install gets unlocked.
disableLicenseEnforcementconfig setting for CI and unrecognised staging domains. Set it inconfig/control-tower.php; it is intentionally not exposed in the CP.- Refresh status button on the license screen, and an automatic re-check when a key is saved, so a valid key unlocks the install immediately instead of waiting for Craft's next scheduled update check.
Notes on expired licenses
- An expired license does not lock the plugin. Licenses are perpetual for versions released before they expire, so an install that lapses its renewal and stays on its current version keeps reporting
validand keeps working. Onlyastray— installed version newer than the license covers — locks the plugin, and Craftnet determines that itself. - From
astray, the license screen presents both remedies as equally valid: renew to cover the newer version, or downgrade to the last covered version and continue without renewing.
Changed
- A locked install collapses its CP nav to the License item, redirects all Control Tower pages there, returns
402from the JSON API, and renders a locked notice in the dashboard widget. - A locked install stops collecting visitor, editor, content, and metrics data, and stops running alert checks and notifications. Retention cleanup still runs, and no existing data is deleted.
- Installs on domains Craft considers testable (local/dev) are never gated. The verdict is mirrored into the cache so console and queue requests agree with web requests.
Version 5.1.3
August 19, 2026
Fixed
- Plugin settings are saved again. Every field in the settings screen was named
settings[…], but both screens that render those fields already apply thesettingsnamespace themselves — so the values posted assettings[settings][…]and Craft'splugins/save-plugin-settingsaction ignored them. The screen reported "Plugin settings saved" and discarded every edit. Fields are now emitted bare, and Control Tower's own Settings tab wraps them in{% namespace 'settings' %}to match the Settings → Plugins screen, which Craft namespaces for it.
Note on 5.1.2
- The
5.1.2tag was originally published against the 5.1.1 commit, so the fixes listed under 5.1.2 below never actually reached anyone. The tag has been corrected, and 5.1.3 contains that work regardless — if you are coming from 5.1.1 or earlier, upgrading to 5.1.3 picks up both releases.
Version 5.1.2
July 19, 2026
Fixed
- Alert
contextand rulewebhookIdsare no longer stored as the literalfalsewhenjson_encode()fails; they now fall back tonull. - Server metric collection no longer passes a
falsefile-read result intopreg_match()/substr_count()when/proc/meminfoor/proc/cpuinfocan't be read, so CPU/memory sampling degrades gracefully instead of misreporting. - Content events now cast the current user id to an integer before storing, avoiding a type mismatch on the
userIdcolumn.
Added
- PHPStan static analysis is wired up for the plugin (
craftcms/phpstandev dependency,phpstan.neonat level 5, and acomposer phpstanscript). Thesrc/tree passes cleanly.
Version 5.1.1
May 17, 2026
Fixed
- Republish 5.1.0 under a corrected tag so Packagist accepts it. The 5.1.0 tag was placed on a commit whose
composer.jsonstill read5.0.2, causing Packagist to skip it.
Version 5.1.0
May 14, 2026
Added
- User-configurable alert rules. New Alerts → Rules section lets users define alert conditions in the CMS without editing config. Each rule has a metric, operator, threshold, severity, and enable toggle.
- Webhook notifications for Slack, Microsoft Teams (Power Automate / Adaptive Cards), and generic JSON receivers like Zapier. Defined once globally and referenced per-rule.
- Per-rule email recipients. “Notify admins” toggle plus a freeform list of additional email addresses.
- Send test button on the webhook edit page — fires a sample payload and reports delivery status inline.
- Flap throttle —
minNotifyInterval(minutes) per rule suppresses repeat notifications when an alert flaps. Alert still appears in the CP regardless. - Notify on resolve option per rule.
- User permissions —
controltower:viewDashboard,controltower:manageAlerts, andcontroltower:manageSettings. CP navigation respects them so non-admin users can be granted scoped access. - Metric registry — extensible list of metrics that rules can target: queue failed/pending, CPU/memory/disk %, editor collisions, active editor count, stale content count.
- Alerts now record which rule fired them via a new
alertRuleIdforeign key; the alerts UI shows the rule name instead of the internal type slug. - Email template at
templates/_cp/_emails/alert.twig, overrideable by site builders. SendAlertNotificationJobqueue job — notifications dispatch asynchronously so SMTP / webhook latency doesn't block the request that triggers the alert.
Changed
- The three hardcoded checks (queue failure, editor collisions, server resources) are now data — seeded as default rules on install. Existing installs are migrated automatically by the install of the new schema. The corresponding settings (
queueFailureAlertThreshold,enableCollisionDetection) are now ignored; configure the seeded rules instead. Plugin::PERMISSION_VIEWreplaces the implicitaccessPlugin-control-towerrequirement in controllers.
Migration
- New tables:
controltower_alert_rules,controltower_webhooks. - New column:
alertRuleIdoncontroltower_alerts(FK to alert rules, SET NULL on delete). - Schema version bumped to 1.1.0.
Version 5.0.2
May 14, 2026
Fixed
- Install migration now skips table creation if the table already exists, so installing on a load-balanced environment with a shared database no longer fails on the second node.
Version 5.0.1
May 14, 2026
Fixed
- Plugin store install failing with “Table
controltower_editor_sessionsdoesn't exist” because activity tracking ran before the install migration. Tracking now waits until the plugin is fully installed. - Correct documentation URL in composer.json to point to https://craft-controltower.com/docs instead of the placeholder URL. (Thanks @brandonkelly!)
Version 5.0.0
May 2, 2026
Added
- Initial release of Control Tower for Craft CMS 5.
- Live operational monitoring dashboard with site activity, editor tracking, content health, queue watch, and system metrics.