Slack is a second chat notification channel alongside Mattermost, added so an agency-wide alert can land in Slack instead of (or as well as) Mattermost. There are actually two separate Slack integrations in this codebase that are easy to conflate — this page covers both.
Two notifiers, same interface, different audience
Both implement App\Services\Chat\ChatNotifier and are fanned out to by ChatNotifierDispatcher — see Integrations → Mattermost for how the fan-out works.
SlackNotifier |
ClientSlackNotifier |
|
|---|---|---|
| Audience | Us (ops) | The client, per site |
| Webhook | One, agency-wide, from .env |
One per site, client-configured |
| Events | Full ChatNotifier::EVENTS set (same as Mattermost) |
Only site_went_down / site_went_up / contact-form failed/recovered — everything else is a silent no-op |
| Configured via | .env + /settings/slack per-event toggles |
The client’s own Companion wp-admin (not this app) |
| Message tone | Internal — IPs, verdicts, server names | Plain-English — “your website is unreachable,” a support link, no internal fields |
SlackNotifier — the ops channel
Why we use it
Some team members or channels prefer Slack over Mattermost, or the agency wants monitoring alerts in both places during a migration between chat tools. SlackNotifier mirrors MattermostNotifier’s full event set exactly — same ChatNotifier::EVENTS registry — so nothing is Mattermost-only by omission.
Setup
-
In Slack: create an Incoming Webhook (
api.slack.com/apps→ your app → Incoming Webhooks) for the target channel. -
Set in
.env:CLOCKWORK_SLACK_ENABLED=true CLOCKWORK_SLACK_WEBHOOK_URL=https://hooks.slack.com/services/... CLOCKWORK_SLACK_CHANNEL=monitoring CLOCKWORK_SLACK_USERNAME=Clockwork Control CLOCKWORK_SLACK_ICON_EMOJI=:lock: -
Configure per-event toggles at Settings → Slack (
/settings/slack). All events default to ON — same defaults as Mattermost’s per-event settings, tracked independently (notifications.slack.eventsvsnotifications.mattermost.eventsinapp_settings).
There’s no dedicated clockwork:slack-test command yet — verify by toggling on a low-noise event (e.g. ssl_state_changed) and triggering it, or by temporarily setting CLOCKWORK_SLACK_CHANNEL to a test channel and watching a real transition fire.
SlackNotifier is a real, independently installable module (modules/Slack/, package clockwork/slack) — same pattern as every hosting/cloud-provider module. An agency that only wants Mattermost can leave clockwork/slack out of composer.json entirely; there’s no dead settings page or half-configured integration left behind, since the module owns its own routes, settings controller, and nav link too.
Auth
The webhook URL is the credential, same trust model as Mattermost’s — don’t commit it, rotate if leaked.
What we POST
Slack’s incoming-webhook payload shape (text + attachments), same semantic helpers as MattermostNotifier (send, siteWentDown, siteWentUp, serverWentRed, serverRecovered, aiRemedyTriaged, aiRemedyExecuted, ipBlocked, sslStateChanged, contactFormTestFailed, contactFormTestRecovered, companionUnreachable/companionReachable, pluginUpdateFailed, malwareFindingDetected, serverUpdateFailed, backupRelayStale/backupRelayRecovered, queueWorkerRestartFailed, schedulerStale/schedulerRecovered) since both implement the same ChatNotifier contract — see Mattermost for what each one covers.
Files
modules/Slack/composer.json— real, independently installable Composer package (clockwork/slack).modules/Slack/src/SlackNotifier.php— a thin subclass ofModules\Core\Support\WebhookChatNotifier(shared withModules\Mattermost\MattermostNotifier— both channels are ~85% identical code, extracted into one base once both were modularized; this class supplies only theclockwork.slack.*config namespace and log-message label).modules/Slack/src/SlackSettingsController.phpmodules/Slack/src/SlackCheck.php— the/settings/diagnosticsconnectivity check, contributed viaModuleRegistry::diagnosticChecks().modules/Slack/src/SlackServiceProvider.php— tags the notifier intoclockwork.notifiers, loads the module’s own routes, contributes its nav link and diagnostic check.modules/Slack/routes/web.phpresources/views/settings/slack.blade.php— stays in core app (module views aren’t required to move, per the same patternmodules/BillComestablished).- Config:
config/clockwork.php→slackkey.
ClientSlackNotifier — per-site client alerts
A second, unrelated Slack integration: Modules\ClientSlack\ClientSlackNotifier (modules/ClientSlack/) posts plain-English alerts to a per-site Slack webhook that the client configures themselves — in their own WordPress admin (Companion → Notifications page), not anywhere in this app. The webhook URL is read straight out of $site->companion_snapshot['client_notifications']['slack_webhook_url'], so there’s no separate Clockwork Control-side column or settings screen for it — it rides in on the same snapshot Companion already pushes.
Why it’s scoped down
Clients don’t need to know an IP got blocked or a plugin auto-update failed — those are operational details. ClientSlackNotifier only actually sends for the handful of events a client would recognize as “my website” problems:
siteWentDown/siteWentUp— “Your website example.com is currently unreachable” / “…is back online,” with a link to your configured support URL (CLOCKWORK_OPERATOR_SUPPORT_URL) on the down message. No status codes, no server names.contactFormTestFailed/contactFormTestRecovered— same tone, for the contact-form smoke test.
Every other ChatNotifier method (ipBlocked, sslStateChanged, pluginUpdateFailed, companionUnreachable, malwareFindingDetected, generic send) is implemented as a hard return false — not gated by a setting, just a no-op by design. This is the one channel in the fan-out where “doesn’t send most events” is intentional, not a missing feature.
Why a site-level webhook instead of a Clockwork Control setting
The webhook lives in Companion’s own wp_options, surfaced to Clockwork Control through the snapshot Companion already pushes — same “push pattern” used for backups/traffic/security-summary data (see Architecture → Companion plugin). This means the client sets it up entirely within their own wp-admin, with no Clockwork Control-side UI, credential, or migration needed. If a site has no webhook configured (the common case), webhookUrl() returns '' and every send is skipped silently.
Files
modules/ClientSlack/src/ClientSlackNotifier.php
Gotchas
- Don’t confuse the two.
SlackNotifier(ops, agency-wide) andClientSlackNotifier(client, per-site) are separate classes with separate webhooks and separate audiences — there’s no shared config between them beyond both implementingChatNotifier. - Turning off Mattermost doesn’t turn off Slack, or vice versa. Each channel gates independently on its own config — and, since both are real modules, an agency that only wants one can simply not install the other’s Composer package at all.
- No retries beyond the standard HTTP client behavior, same as Mattermost. A flaky Slack endpoint means a missed post, not a missed check — the underlying event still got recorded.
Related
- Mattermost — the original chat channel and the
ChatNotifierDispatcherfan-out both notifiers sit behind. - Architecture → Companion plugin — the push pattern
ClientSlackNotifier’s webhook URL rides in on.
