{"id":316486,"date":"2026-05-28T15:27:49","date_gmt":"2026-05-28T15:27:49","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/cronheart\/"},"modified":"2026-05-28T15:27:38","modified_gmt":"2026-05-28T15:27:38","slug":"cronheart","status":"publish","type":"plugin","link":"https:\/\/ky.wordpress.org\/plugins\/cronheart\/","author":23503763,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"0.1.9","stable_tag":"0.1.9","tested":"7.0","requires":"6.0","requires_php":"8.2","requires_plugins":null,"header_name":"Cronheart","header_author":"Aliaksandr Palazok","header_description":"Monitors WP-Cron with cronheart.com. Detects when scheduled events stop firing \u2014 heartbeat for the whole site plus per-event start\/success\/fail pings.","assets_banners_color":"1b1829","last_updated":"2026-05-28 15:27:38","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/github.com\/alexander-po\/cronheart-wp","header_author_uri":"https:\/\/github.com\/alexander-po","rating":0,"author_block_rating":0,"active_installs":0,"downloads":39,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"0.1.9":{"tag":"0.1.9","author":"cronheart","date":"2026-05-28 15:27:38"}},"upgrade_notice":{"0.1.9":"<p>Plugin Directory review round 2 metadata fix (Contributors set\nto the slug owner). No code changes. Safe to upgrade.<\/p>","0.1.8":"<p>&quot;Tested up to&quot; bump to 7.0. No code changes. Safe to upgrade.<\/p>","0.1.7":"<p>Restored Terms \/ Privacy links with the correct URLs\n(cronheart.com\/terms and \/privacy). No code changes.<\/p>","0.1.6":"<p>Plugin Directory review round 1 metadata fixes. No code changes.\nSafe to upgrade.<\/p>","0.1.5":"<p>&quot;Tested up to&quot; bump to 6.9. No code changes. Safe to upgrade.<\/p>","0.1.4":"<p>Pre-submission cleanup only \u2014 no behaviour changes. Safe to upgrade.<\/p>","0.1.3":"<p>WordPress.org Plugin Check fixes only \u2014 no behaviour changes.\nSafe to upgrade.<\/p>","0.1.2":"<p>WordPress.org metadata polish only. Safe to upgrade.<\/p>","0.1.1":"<p>Adds opt-in endpoint override. Existing installs are unaffected \u2014\nthe default endpoint remains https:\/\/cronheart.com.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3552438,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3552438,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3552438,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3552438,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["0.1.9"],"block_files":[],"assets_screenshots":[],"screenshots":{"1":"The plugin's settings page at <strong>Settings \u2192 Cronheart<\/strong>:\nsite-heartbeat UUID field plus the read-only monitored-events\ntable.","2":"The cronheart.com dashboard listing the configured monitors\nand their last-ping timestamps.","3":"A monitor detail view on cronheart.com after the plugin has\nreported a heartbeat + a successful per-event run."}},"plugin_section":[],"plugin_tags":[4567,264813,196269,5603,4568],"plugin_category":[54,59],"plugin_contributors":[264814],"plugin_business_model":[],"class_list":["post-316486","plugin","type-plugin","status-publish","hentry","plugin_tags-cron","plugin_tags-deadman-switch","plugin_tags-healthcheck","plugin_tags-monitoring","plugin_tags-wp-cron","plugin_category-security-and-spam-protection","plugin_category-utilities-and-tools","plugin_contributors-cronheart","plugin_committers-cronheart"],"banners":{"banner":"https:\/\/ps.w.org\/cronheart\/assets\/banner-772x250.png?rev=3552438","banner_2x":"https:\/\/ps.w.org\/cronheart\/assets\/banner-1544x500.png?rev=3552438","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/cronheart\/assets\/icon-128x128.png?rev=3552438","icon_2x":"https:\/\/ps.w.org\/cronheart\/assets\/icon-256x256.png?rev=3552438","generated":false},"screenshots":[],"raw_content":"<!--section=description-->\n<p><strong>WP-Cron is request-driven.<\/strong> On a low-traffic site no requests\narrive, no events fire, and a scheduled backup can be stalled for\nweeks before anyone notices. Uptime monitors do not catch this \u2014 the\nsite responds to HTTPS just fine, it just is not running its jobs.<\/p>\n\n<p>Cronheart turns WP-Cron into a <strong>dead-man switch<\/strong>: the plugin pings\n<a href=\"https:\/\/cronheart.com\">cronheart.com<\/a> every five minutes and on\nevery individual event you register. If the pings stop, cronheart\nalerts you via email, Telegram, Slack, Discord, or a custom webhook.<\/p>\n\n<h4>What it does<\/h4>\n\n<ul>\n<li><strong>Site heartbeat.<\/strong> A 5-minute custom WP-Cron event whose only job\nis to ping cronheart. Proves WP-Cron itself is alive on this site.<\/li>\n<li><strong>Per-event monitoring.<\/strong> Register any scheduled hook for\nstart \/ success \/ fail pings with one PHP one-liner:\n  cronheart_monitor( 'my_nightly_report', 'xxxxxxxx-\u2026' );<\/li>\n<li><strong>PHP fatal-error capture.<\/strong> When a scheduled callback fatals or\nthrows, the fail-ping body includes the <code>error_get_last()<\/code>\nsummary \u2014 the cronheart dashboard shows the cause without you\ntailing <code>debug.log<\/code>.<\/li>\n<li><strong>Settings page.<\/strong> A read-only \"Monitored events\" table at\nSettings \u2192 Cronheart shows every hook the plugin is watching and\nwhere its UUID came from (constant, option, filter).<\/li>\n<li><strong>Configuration through <code>wp-config.php<\/code> constants<\/strong> for production\n(<code>CRONHEART_HEARTBEAT_UUID<\/code>, <code>CRONHEART_EVENT_&lt;HOOK&gt;_UUID<\/code>), with\nadmin-UI fallback for sites where editing <code>wp-config.php<\/code> is not\npractical.<\/li>\n<\/ul>\n\n<h4>Never breaks WP-Cron<\/h4>\n\n<p>The plugin's hard contract: a broken cronheart backend, an\nunreachable network, a misbehaving PSR-18 HTTP client \u2014 none of\nthem may cause WP-Cron to fail. Every network \/ HTTP error is\nswallowed into a logged warning. If cronheart goes down for a\nday, your <code>wp_schedule_event<\/code> callbacks still run normally; you\njust stop seeing pings on the dashboard.<\/p>\n\n<h4>External services<\/h4>\n\n<p>This plugin sends HTTP requests to <a href=\"https:\/\/cronheart.com\">cronheart.com<\/a>\non every scheduled WP-Cron run, but <strong>only when you supply a\nmonitor UUID<\/strong>. Without configuration the plugin loads and does\nnothing \u2014 no telemetry, no usage statistics, no anonymous reports.<\/p>\n\n<p>The exact data sent per ping:<\/p>\n\n<ul>\n<li>The per-monitor UUID you configured (path segment).<\/li>\n<li>A short body excerpt \u2014 capped at 10 KB \u2014 containing either an\nexception summary (for <code>fail<\/code> pings) or nothing (for <code>start<\/code> \/\n  success \/ <code>heartbeat<\/code>).<\/li>\n<li>The plugin \/ SDK version in a <code>User-Agent<\/code> header.<\/li>\n<\/ul>\n\n<p><a href=\"https:\/\/cronheart.com\/terms\">Cronheart.com Terms of Service<\/a> \u00b7\n<a href=\"https:\/\/cronheart.com\/privacy\">Privacy policy<\/a><\/p>\n\n<h4>Open source<\/h4>\n\n<p>Source code and issue tracker:\n<a href=\"https:\/\/github.com\/alexander-po\/cronheart-wp\">github.com\/alexander-po\/cronheart-wp<\/a>.<\/p>\n\n<p>The plugin wraps the\n<a href=\"https:\/\/github.com\/alexander-po\/cron-monitor-php\"><code>cron-monitor\/php-sdk<\/code><\/a>\nPHP package (also open source, MIT-licensed). Both projects are\nmaintained independently.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Install the plugin: <strong>WP Admin \u2192 Plugins \u2192 Add New \u2192<\/strong> search for\n\"Cronheart\" \u2192 <strong>Install Now \u2192 Activate.<\/strong> Or upload\n   cronheart.zip from a GitHub release.<\/li>\n<li>Sign up at <a href=\"https:\/\/cronheart.com\">cronheart.com<\/a> and create a\nmonitor for your site's heartbeat. Copy the monitor UUID from\nthe dashboard.<\/li>\n<li>Configure the UUID. Either:\n\n<ul>\n<li><strong>Recommended:<\/strong> add to <code>wp-config.php<\/code>:\n define( 'CRONHEART_HEARTBEAT_UUID', 'xxxxxxxx-\u2026' );<\/li>\n<li><strong>Or:<\/strong> paste the UUID under <strong>Settings \u2192 Cronheart<\/strong> in\nthe WP admin.<\/li>\n<\/ul><\/li>\n<li>Done. Within five minutes you should see the first <code>heartbeat<\/code>\nping on the cronheart dashboard.<\/li>\n<\/ol>\n\n<p>For per-event monitoring (a specific scheduled hook, not just the\nsite heartbeat), register the hook from a plugin \/ theme \/\nmu-plugin:<\/p>\n\n<pre><code>add_action( 'plugins_loaded', function () {\n    cronheart_monitor( 'my_nightly_report', 'xxxxxxxx-\u2026' );\n}, 1 );\n<\/code><\/pre>\n\n<p>The hook then emits <code>start<\/code> \/ <code>success<\/code> (or <code>fail<\/code> on a fatal \/\nthrown exception) pings on every scheduled run.<\/p>\n\n<!--section=faq-->\n<dl>\n<dt id=\"does%20this%20work%20when%20wp-cron%20is%20disabled%20%28system-cron%20mode%29%3F\"><h3>Does this work when WP-Cron is disabled (system-cron mode)?<\/h3><\/dt>\n<dd><p>Yes. If you set <code>define( 'DISABLE_WP_CRON', true );<\/code> and trigger\n    wp-cron.php from a real system cron, the plugin's\n    heartbeat_tick action still fires on each run \u2014 the trigger\nmechanism is different, the action chain is the same.<\/p><\/dd>\n<dt id=\"what%20if%20my%20host%20blocks%20outgoing%20https%3F\"><h3>What if my host blocks outgoing HTTPS?<\/h3><\/dt>\n<dd><p>The plugin will retry once (built-in retry budget) and then log a\nwarning to <code>debug.log<\/code>. Your scheduled callbacks still run normally\n\u2014 the plugin never raises an exception that could break the cron\nrunner. To diagnose, check <code>wp-content\/debug.log<\/code> for entries\nbeginning with \"cron-monitor\".<\/p><\/dd>\n<dt id=\"do%20i%20need%20a%20paid%20cronheart.com%20account%3F\"><h3>Do I need a paid cronheart.com account?<\/h3><\/dt>\n<dd><p>No. Cronheart's free tier covers 20 monitors per account \u2014 enough\nfor a typical site's heartbeat plus several per-event monitors.\nPaid tiers (Starter \/ Growth \/ Scale) raise the cap and unlock\nadditional notification channels.<\/p><\/dd>\n<dt id=\"where%20do%20i%20find%20my%20monitor%20uuid%3F\"><h3>Where do I find my monitor UUID?<\/h3><\/dt>\n<dd><p>Sign in at <a href=\"https:\/\/cronheart.com\">cronheart.com<\/a>, open the monitor\nyou created, and copy the UUID from the address bar or the \"Ping\nURL\" block on the monitor page.<\/p><\/dd>\n<dt id=\"what%20happens%20to%20my%20scheduled%20jobs%20if%20cronheart.com%20is%20unreachable%3F\"><h3>What happens to my scheduled jobs if cronheart.com is unreachable?<\/h3><\/dt>\n<dd><p>Nothing. The plugin catches every network \/ HTTP error from the\nSDK and logs a warning \u2014 your <code>wp_schedule_event<\/code> callbacks\ncontinue to run. You will stop seeing pings on the cronheart\ndashboard, and after the configured grace period cronheart sends\nyou the down-alert. When cronheart comes back the next successful\nping resolves the incident automatically.<\/p><\/dd>\n<dt id=\"does%20the%20plugin%20track%20or%20report%20anything%20about%20my%20site%3F\"><h3>Does the plugin track or report anything about my site?<\/h3><\/dt>\n<dd><p>No. The plugin sends a ping to cronheart only when you have\nconfigured a monitor UUID. The ping payload is the UUID, an\noptional short body excerpt (capped at 10 KB), and the\nSDK's <code>User-Agent<\/code> header. There is no anonymous-statistics\nbeacon, no plugin-usage telemetry, no calls to any third-party\nanalytics service.<\/p><\/dd>\n<dt id=\"can%20i%20point%20the%20plugin%20at%20a%20non-production%20cronheart%20deployment\"><h3>Can I point the plugin at a non-production cronheart deployment<\/h3><\/dt>\n<dd><p>(staging \/ private \/ self-hosted)? =<\/p>\n\n<p>Yes. Define <code>CRONHEART_ENDPOINT<\/code> in <code>wp-config.php<\/code> with the URL\nof your alternate deployment. For plain <code>http:\/\/<\/code> endpoints\n(local development, private VPNs without TLS) also set\n    CRONHEART_ALLOW_INSECURE_ENDPOINT to <code>true<\/code>. With both unset,\nthe plugin pings the production cronheart.com over HTTPS.<\/p><\/dd>\n<dt id=\"where%20can%20i%20report%20bugs%20or%20request%20features%3F\"><h3>Where can I report bugs or request features?<\/h3><\/dt>\n<dd><p>Open an issue on\n<a href=\"https:\/\/github.com\/alexander-po\/cronheart-wp\/issues\">GitHub<\/a>.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>0.1.9<\/h4>\n\n<ul>\n<li>Plugin Directory review round 2 fix. <code>Contributors:<\/code> changed\nfrom <code>cronmonitor<\/code> to <code>cronheart<\/code> \u2014 the reviewer's static\nanalysis pointed out that the WordPress.org account that\nactually owns the <code>cronheart<\/code> plugin slug (and uploaded\nevery version including v0.1.8) is <code>cronheart<\/code>, not\n  cronmonitor. v0.1.7's switch to <code>cronmonitor<\/code> was a wrong\nguess at the right owner identity; v0.1.9 puts the actual\nslug owner in the contributors line.<\/li>\n<li>No code changes.<\/li>\n<\/ul>\n\n<h4>0.1.8<\/h4>\n\n<ul>\n<li>Bump \"Tested up to\" from 6.9 to 7.0. The v0.1.7 re-upload was\nrejected by WP.org's automated scan because WordPress 7.0 had\nshipped between our v0.1.5 submission and the v0.1.7\nre-upload, and the \"Tested up to\" header now lagged again.\nDevstack also moved to <code>wordpress:7.0-php8.2-apache<\/code>; smoke\nrun + Plugin Check re-verified green on 7.0.<\/li>\n<li>No code changes.<\/li>\n<\/ul>\n\n<h4>0.1.7<\/h4>\n\n<ul>\n<li>Restored Terms of Service \/ Privacy policy links in the readme.\nThe URLs the WP.org reviewer flagged as HTTP 404 in v0.1.5\n(<code>cronheart.com\/legal\/terms<\/code>, <code>cronheart.com\/legal\/privacy<\/code>)\nwere wrong paths \u2014 the live pages have always been at\n  cronheart.com\/terms and <code>cronheart.com\/privacy<\/code>. v0.1.6\nremoved the links entirely as the most cautious response to\nthe review feedback; v0.1.7 puts them back, pointing at the\ncorrect URLs (both return HTTP 200).<\/li>\n<li>No code changes.<\/li>\n<\/ul>\n\n<h4>0.1.6<\/h4>\n\n<ul>\n<li>Plugin Directory review round 1 fixes. No behaviour changes \u2014\npings, hooks, admin UI all identical to 0.1.5.<\/li>\n<li>Removed two <code>cronheart.com\/legal\/*<\/code> links from the readme that\nresponded with HTTP 404. The \"External services\" section in\nthis readme already provides a full data-flow disclosure;\nstand-alone Terms \/ Privacy pages will be linked back when\nthe corresponding cronheart.com URLs are live.<\/li>\n<li><code>Contributors:<\/code> set to <code>cronmonitor<\/code> (the WordPress.org account\nthat submitted the plugin); previously held a stale GitHub\nhandle (<code>alexanderpo<\/code>) that did not match any WP.org user.<\/li>\n<li>Release zip no longer ships <code>vendor\/bin\/cron-monitor<\/code> or\n  vendor\/cron-monitor\/php-sdk\/bin\/cron-monitor \u2014 those CLI\nbinaries are part of the SDK's local-dev tooling and have no\nuse inside a WordPress plugin. <code>bin\/build-release.sh<\/code> now\nstrips every <code>vendor\/*\/bin\/<\/code> directory at zip time. PSR-4\nautoload of the SDK's runtime classes is unaffected.<\/li>\n<\/ul>\n\n<h4>0.1.5<\/h4>\n\n<ul>\n<li>Bump \"Tested up to\" from 6.7 to 6.9. WordPress.org's automated\nscan blocks submission when the readme's \"Tested up to\" lags\nthe current stable WordPress release, even when the underlying\ncode is unchanged \u2014 the field is treated as a freshness signal\nfor the Plugin Directory search. Devstack also moved to\n  wordpress:6.9-php8.2-apache; smoke run + Plugin Check\nre-verified green on 6.9.<\/li>\n<li>No code changes.<\/li>\n<\/ul>\n\n<h4>0.1.4<\/h4>\n\n<ul>\n<li>Pre-submission cleanup before the WordPress.org Plugin Directory\nreview. No behaviour changes \u2014 pings, hooks, and admin UI all\nidentical to 0.1.3.<\/li>\n<li>Release zip no longer ships <code>CLAUDE.md<\/code> and similar\ncontributor-only docs from vendored packages; the bundled tree\nis now scoped to what the runtime actually needs.<\/li>\n<li><code>LICENSE<\/code> gained an explicit project copyright header\n(<code>cronheart-wp \u2014 Copyright (C) 2026 Alexander Palazok<\/code>); the\nGPL-2.0 preamble follows unchanged.<\/li>\n<li>CHANGELOG.md hygiene: missing <code>[0.1.1]<\/code> section header restored;\ninternal sprint-tracking term (\"Sprint D\") removed from the\npublic 0.1.3 entry; stale \"deferred to v0.1.1+\" notes on vendor\nnamespace prefixing rewritten to reflect the current \"deferred\npending first reported collision\" stance.<\/li>\n<\/ul>\n\n<h4>0.1.3<\/h4>\n\n<ul>\n<li>WordPress.org Plugin Check fixes: added <code>defined('ABSPATH')<\/code>\ndirect-access guards to every PHP file the static analyser\nreaches; refactored the monitored-events table render so the\nescape calls are direct printf arguments (the previous\npre-assigned variable was flagged by EscapeOutput); shipped\n  composer.json \/ <code>composer.lock<\/code> alongside <code>vendor\/<\/code> in the\nrelease zip so the bundled dependencies are reproducible.<\/li>\n<li>No behaviour changes \u2014 pings, hooks, and admin UI all\nidentical to 0.1.2.<\/li>\n<\/ul>\n\n<h4>0.1.2<\/h4>\n\n<ul>\n<li>WordPress.org submission readiness: full readme.txt\n(Description, FAQ, Screenshots, External-services disclosure),\nversion bump from 0.1.1.<\/li>\n<li>No code changes \u2014 pure metadata polish for the Plugin Directory\nsubmission.<\/li>\n<\/ul>\n\n<h4>0.1.1<\/h4>\n\n<ul>\n<li>Endpoint override: <code>CRONHEART_ENDPOINT<\/code> constant and\n  cronheart_endpoint option for pointing the plugin at a\nnon-production cronheart deployment (staging, private VPC,\nlocal backend).<\/li>\n<li><code>CRONHEART_ALLOW_INSECURE_ENDPOINT<\/code> constant \/\n  cronheart_allow_insecure_endpoint option to opt into plain\n  http:\/\/ endpoints (required for local backends behind\n  host.docker.internal or TLS-less private VPNs; default false).<\/li>\n<li>Local end-to-end smoke harness under <code>devstack\/<\/code> for verifying\nthe plugin against either production cronheart.com (public\ncontributors) or a local cron-monitor backend (maintainers).<\/li>\n<li>No breaking changes \u2014 installs without the new constants keep\nthe v0.1.0 behaviour.<\/li>\n<\/ul>\n\n<h4>0.1.0<\/h4>\n\n<ul>\n<li>Initial scaffold (GitHub-only release; WP.org submission\ndeferred to v0.1.2+).<\/li>\n<li>Site-wide heartbeat layer with a 5-minute custom schedule.<\/li>\n<li>Per-event monitoring with <code>cronheart_monitor()<\/code> helper and\n  cronheart_monitor_map filter.<\/li>\n<li><code>CRONHEART_HEARTBEAT_UUID<\/code> and <code>CRONHEART_EVENT_&lt;HOOK&gt;_UUID<\/code>\nconstants for sourcing UUIDs from <code>wp-config.php<\/code>.<\/li>\n<li>Admin page at Settings \u2192 Cronheart for sites without\n  wp-config.php access.<\/li>\n<li>PHP fatal-error capture for the fail-ping body.<\/li>\n<\/ul>","raw_excerpt":"Dead-man-switch monitoring for WP-Cron. Get alerted when scheduled events stop firing \u2014 uptime monitors do not catch this.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/ky.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/316486","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ky.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/ky.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/ky.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=316486"}],"author":[{"embeddable":true,"href":"https:\/\/ky.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/cronheart"}],"wp:attachment":[{"href":"https:\/\/ky.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=316486"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/ky.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=316486"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/ky.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=316486"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/ky.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=316486"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/ky.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=316486"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/ky.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=316486"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}