ntfy Monitoring — Setup & Technical Reference

System Overview The ntfy monitoring system is a module within the Brighter Core MU plugin. It hooks into WordPress events and WP Cron to watch for failure conditions, then pushes notifications to an ntfy server via HTTP. Each monitor is an independent class. Monitors are opt-in via wp-config.php constants. The main controller (class-ntfy-notifications.php) loads only […]

Overview

System Overview

The ntfy monitoring system is a module within the Brighter Core MU plugin. It hooks into WordPress events and WP Cron to watch for failure conditions, then pushes notifications to an ntfy server via HTTP.

Each monitor is an independent class. Monitors are opt-in via wp-config.php constants. The main controller (class-ntfy-notifications.php) loads only the monitors that are enabled — no dead weight.

Prerequisite: An ntfy server is required. This system posts to an existing ntfy instance. Setting up an ntfy server is outside the scope of this guide. Brighter Websites runs a self-hosted instance at ntfy.bweb1.com.au. If deploying for another agency or licensing context, a separate ntfy instance (self-hosted or ntfy.sh cloud) must be provisioned first.

File Structure

brighter-core/includes/
├── class-ntfy-notifications.php          ← Main controller
└── ntfy/
    ├── class-ntfy-client.php             ← HTTP API wrapper (Basic Auth)
    ├── class-ntfy-downtime-monitor.php   ← Site health checks
    ├── class-ntfy-smtp-monitor.php       ← Email delivery failures
    ├── class-ntfy-robots-monitor.php     ← robots.txt verification
    ├── class-ntfy-sitemap-monitor.php    ← Sitemap verification
    ├── class-ntfy-cron-monitor.php       ← WP Cron (stub — not implemented)
    └── class-ntfy-form-monitor.php       ← Form submission alerts

Topic Architecture

This is the most important thing to understand before deploying across multiple sites.

Single-stream mode (default)

All sites share the same NTFY_TOPIC_PREFIX. Every site posts to the same topic names. A subscriber to bw-agency-downtime sees downtime alerts from every site in one stream. The site is identified in the message body, not the topic.

This is the default and works well when Brighter Websites is the only subscriber — one stream per monitor type, all sites visible.

Per-site mode

Set a unique NTFY_TOPIC_PREFIX per site in wp-config.php (e.g. acme-corp, neis-2025). The same ntfy server and credentials can be used — only the topic path changes. This gives each site its own {prefix}-downtime, {prefix}-smtp, etc. channels.

Use this when a client is subscribed to their own alerts, or when you need to separate alert streams by engagement.

Forms topic — known inconsistency

Form alerts use a site slug in the topic ({site-slug}-forms), not the global prefix. This means form topics are always per-site regardless of whether you’re running in single-stream or per-site mode. Keep this in mind when setting up subscriptions — the forms topic won’t follow the same pattern as the other monitors.

Topic reference

MonitorTopic patternMode
Downtime{prefix}-downtimeFollows NTFY_TOPIC_PREFIX
SMTP{prefix}-smtpFollows NTFY_TOPIC_PREFIX
robots.txt{prefix}-robotsFollows NTFY_TOPIC_PREFIX
Sitemap{prefix}-sitemapFollows NTFY_TOPIC_PREFIX
WP Cron{prefix}-cronFollows NTFY_TOPIC_PREFIX (stub)
Forms{site-slug}-formsAlways per-site — does not use prefix

Installation

1. wp-config.php constants

// ntfy Notification System
define('NTFY_ENABLED', true);
define('NTFY_SERVER_URL', 'https://ntfy.bweb1.com.au');   // Your ntfy server
define('NTFY_USERNAME', 'vanessa');
define('NTFY_PASSWORD', 'your-password-here');

// Topic prefix — change per site for per-site mode, leave default for single-stream
define('NTFY_TOPIC_PREFIX', 'bw-agency');

// Monitor toggles — defaults shown
define('NTFY_MONITOR_SMTP', true);       // Email failures — recommended on always
define('NTFY_MONITOR_DOWNTIME', true);   // 5-min health checks — recommended on always
define('NTFY_MONITOR_ROBOTS', true);     // Daily robots.txt check
define('NTFY_MONITOR_SITEMAP', true);    // Daily sitemap check
define('NTFY_MONITOR_FORMS', false);     // Form submission alerts — opt-in per site
define('NTFY_MONITOR_CRON', false);      // WP Cron — stub only, leave false

2. Deploy

git pull origin intent-schema

3. Verify

Visit yourdomain.com/?test_ntfy=1 as admin, or use the test button at: wp-admin/admin.php?page=brighter_support&tab=monitoring

The test pushes to {prefix}-test. If it arrives in ntfy, the connection and auth are working.

Monitor Reference


SMTP Monitor

Status: ✅ Active
Hook: wp_mail_failed
Rate limit: 1 alert per 5 minutes
What it catches: Any WordPress email send failure — SMTP auth errors, connection failures, provider rejections.
What it doesn’t catch: Emails that send successfully but are rejected or filtered by the recipient’s server (delivery confirmation is outside WordPress’s visibility).

Downtime Monitor


Status: ✅ Active
Trigger: WP Cron every 5 minutes
Rate limit: 1 alert per 15 minutes
Checks: HTTP response (non-200 status), database connectivity, filesystem write access
Known limitation: Because checks run from within WordPress itself, a total PHP or Apache failure won’t be caught — the cron won’t fire. True external downtime detection requires an external ping (UptimeRobot, cross-site monitor, etc.). This monitors degraded states, not total failure. This is documented as a Phase 3 concern.


robots.txt Monitor

Status: ✅ Active
Trigger: WP Cron daily
Rate limit: 1 alert per 24 hours
What it checks: HTTP request to {home_url}/robots.txt — alerts on non-200 response.

Sitemap Monitor

Status: ✅ Active
Trigger: WP Cron daily
Rate limit: 1 alert per 24 hours
What it checks: HTTP request to {home_url}/sitemap.xml — alerts on non-200 response and validates XML content type.


Form Monitor

Status: ✅ Active (opt-in)
Toggle: NTFY_MONITOR_FORMS = true
Supported: Breakdance forms, Contact Form 7, Gravity Forms
Rate limit: None — can be noisy on high-volume forms
Topic: {site-slug}-forms (not prefix-based — see topic architecture above)
Use case: High-value enquiry forms where immediate awareness matters. Don’t enable on sites with frequent low-value submissions.


WP Cron Monitor

Status: 🚧 Stub — do not enable
Toggle: NTFY_MONITOR_CRON = false (leave this)
What’s missing: WordPress has no built-in hook for missed cron events. The implementation needs a periodic check of _get_cron_array() for overdue events. The class exists but has no detection logic. Set NTFY_MONITOR_CRON = true only when actively building this out — it won’t do anything useful in production yet.


Admin Interface

Location: wp-admin/admin.php?page=brighter_support&tab=monitoring
Access: Restricted to @brighterwebsites.com.au email addresses
Features: Connection status, active monitors list with topics, send test notification button, wp-config constant reference.

Testing Each Monitor

MonitorHow to test
ConnectionVisit ?test_ntfy=1 as admin, or use the admin UI test button. Check for message on {prefix}-test.
SMTPTemporarily set incorrect SMTP credentials, trigger an email send (password reset, test email from SMTP plugin). Restore credentials after.
DowntimeHard to test safely. Wait 5 minutes for cron to run and check debug.log for activity.
robots.txtTemporarily rename robots.txt on the server, wait for daily cron. Rename back.
SitemapTemporarily rename sitemap.xml, wait for daily cron. Rename back.
FormsEnable NTFY_MONITOR_FORMS, submit a form, check {site-slug}-forms topic.

Rate Limiting

All monitors use transients to rate-limit alerts. The transient key pattern is ntfy_rate_{monitor_name}. If an alert isn’t firing when expected, check whether a rate-limit transient is blocking it:

wp transient get ntfy_rate_smtp
wp transient get ntfy_rate_downtime

Delete to reset:

wp transient delete ntfy_rate_smtp

Phase Status

PhaseStatusWhat’s in it
Phase 1✅ Completentfy client, Basic Auth, test connection, admin UI
Phase 2✅ CompleteSMTP, Downtime, robots.txt, Sitemap, Forms monitors
Phase 3⏳ Not startedWP Cron monitor (detection logic), external downtime ping

Phase 3 — what’s needed

WP Cron monitor: Needs a scheduled check that iterates _get_cron_array(), compares scheduled timestamps to current time, and alerts if critical jobs are overdue by more than a threshold. Decision required: monitor all cron jobs or a defined list of critical ones only (backups, updates). The stub class (class-ntfy-cron-monitor.php) exists — it just needs check_missed_events() implemented.

External downtime detection: The internal downtime monitor can’t catch PHP/Apache failures. Options: UptimeRobot API integration (free tier available), a cross-site ping from another Brighter Websites server, or a lightweight external cron hitting a health endpoint. This is the meaningful gap in the current monitoring coverage.

Troubleshooting

No notifications arriving

  1. Confirm NTFY_ENABLED = true in wp-config
  2. Test connection via ?test_ntfy=1 or admin UI — if this fails, the issue is credentials or server URL
  3. Check debug.log for ntfy errors (requires WP_DEBUG_LOG = true)
  4. Confirm the monitor’s specific constant is true
  5. Check rate-limit transients aren’t suppressing alerts (see above)

Admin monitoring tab not showing

  1. Confirm logged-in user has @brighterwebsites.com.au email
  2. Clear browser cache
  3. Confirm Agency Settings tab is present at ?page=brighter_support

Monitor not firing

  1. Check individual monitor constant is true
  2. Confirm WP Cron is running: wp cron event list
  3. Check rate-limit transient for that monitor

Forms topic not matching expected pattern Forms use {site-slug}-forms, not {NTFY_TOPIC_PREFIX}-forms. This is by design — see topic architecture section. Confirm you’re subscribed to the right topic.

Extension Notes

Before adding a new monitor or modifying topic structure, check whether the change affects all sites or only one. If it affects only one site, handle it at the site level in wp-config — don’t build it into the module. If it benefits 2–3+ sites, it belongs in the module.

The forms topic inconsistency (site slug vs prefix) is a candidate for future normalisation — but changing it would require updating subscriptions on all affected sites simultaneously. Flag before touching.

Want to Contribute to SCOS?

SCOS is a Strategic Content Operating System - learn more or contact us on support@brighterwebsites.com.au.

Work with me

Hit submit and I’ll reach out by email or phone to help you get started. Your details stay private,  see the Privacy Policy.