github target/goalert v0.35.0

latest release: v0.35.0-demofix.1
6 hours ago

GoAlert v0.35.0

It's been nearly a year since v0.34.1, and v0.35.0 brings a substantial set of improvements for both administrators and on-call users. Highlights include multi-ack escalation policy steps, private contact methods, an alerts view for escalation policies, richer webhook payloads, new webhook and Slack hardening options for admins, and continued progress on Universal Integration Keys and the job queue architecture.

⚠️ Upgrade Notes

  • Database migrations: This release includes 7 new migrations. They are applied automatically on startup (or via goalert migrate). As always, take and validate a full backup before upgrading.
    • Three migrations add database triggers that enqueue engine jobs directly for alert status, rotation, and signal changes (see Job Queue: Database-Driven Events below). These replace the in-process event bus and are transparent to operators, but you will see new trigger functions in the schema.
    • Remaining migrations add columns for labels on escalation policies/schedules/rotations, the multi_ack step setting, and the private contact method flag.
  • All new behavior is opt-in. Multi-ack, private contact methods, Webhook.BlockPrivateAddresses, and Slack.DisableBroadcastThreadReplies all default to off and preserve existing behavior unless enabled.
  • Webhook payload changes are additive. New JSON fields were added to alert-related webhook payloads; no existing fields were removed or renamed.
  • Building from source now requires Go 1.26.

🚀 New Features

Multi-Ack Escalation Policy Steps

Escalation policy steps now have an optional multi-ack setting. When enabled, acknowledging an alert no longer silences notifications for everyone else on that step. Each user activated by the step keeps getting notified until they individually acknowledge.

This is designed for teams where more than one person is expected to respond to every alert: primary/secondary pairs, onboarding shadows, or "whole team on call" policies for major outages. Previously the only workaround was telling people not to acknowledge so they wouldn't stop notifications for others.

What doesn't change:

  • Escalation to the next step still stops once the first person acknowledges
  • Closing an alert still cancels all pending notifications
  • Steps without multi-ack behave exactly as before, even when mixed with multi-ack steps in the same policy

On a multi-ack step, each user's acknowledgment is recorded in the alert log, so you can see who came online for an incident.

Multi-ack checkbox in the escalation policy step dialog Escalation policy step list showing a multi-ack step

This is an MVP intended to be safe to opt into; we'll refine it based on feedback. Known gaps: alert bundle messages count unacknowledged alerts (so a multi-ack user may see "0 unacknowledged"), and acknowledging an already-acknowledged alert from the web UI does not count as your individual acknowledgment.

API: multiAck on EscalationPolicyStep, and optional multiAck on CreateEscalationPolicyStepInput / UpdateEscalationPolicyStepInput.

1 PR by @mastercactapus

Private Contact Methods

Users can now mark a contact method as private, hiding its details (phone number, email address, etc.) from everyone except the owner. This addresses a long-standing request to avoid exposing personal contact details to all users of a GoAlert instance.

  • Private is off by default and does not retroactively change existing contact methods; users can edit an existing contact method to make it private (or public again).
  • Private contact methods show a (PRIVATE) label to the owner.
  • When another user views a profile, private contact methods are omitted, and notification rules that reference them display PRIVATE instead of the destination.
  • Note for administrators: private contact methods are hidden from all other users, including admins. Notifications are still delivered normally.
Private checkbox in the contact method dialog

Owner's view:

Owner view showing PRIVATE label

Other users' view:

Other users see PRIVATE in place of details

API: new private field on contact methods, and optional private on the create/update inputs.

1 PR by @mastercactapus

Alerts View for Escalation Policies

Escalation policy detail pages now have an Alerts quick-link that shows alerts across all services assigned to that policy. This makes it easy to see everything a given on-call team is responsible for in one place, with the same filtering and bulk actions available on the service alerts page.

Escalation policy details with Alerts quick link Alerts list filtered to an escalation policy's services
1 PR by @mastercactapus

Multiple Users per Temporary Schedule Shift

When adding a shift to a temporary schedule, you can now select multiple users at once instead of adding each one individually. Each selected user gets their own shift for the chosen time range.

Temporary schedule shift dialog with multiple users selected
1 PR by @Arunkumarlnr

Labels for Escalation Policies, Schedules, and Rotations (API Only)

Labels, previously available only on services, can now be set on escalation policies, schedules, and rotations via the GraphQL API. This enables the same ownership/team/cost-center tagging patterns across all of these resource types.

UI support and searching these types by label are planned for a future release.

API: labels field on EscalationPolicy, Schedule, and Rotation; optional labels on the corresponding create inputs; setLabel now accepts these types as targets.

1 PR by @mastercactapus
  • label: add support for escalation policy, schedule, and rotation types by @mastercactapus in #3429

Richer Webhook Payloads

Alert-related webhook notifications now carry significantly more context, making it easier to build automations and integrations without a follow-up API call:

Payload type New fields
AlertStatus Summary, Details, ServiceID, ServiceName, Meta, GoAlertURL
Alert GoAlertURL
AlertBundle GoAlertURL
AlertStatusBundle GoAlertURL

GoAlertURL is a direct link to the alert (or the service's alert list for bundles), built from the configured public URL. Meta contains the alert's key/value metadata.

Example AlertStatus payload:

{
  "AppName": "GoAlert",
  "Type": "AlertStatus",
  "AlertID": 79694,
  "Summary": "Disk usage above 90%",
  "Details": "Volume /data on db-01",
  "ServiceID": "9d1a4f7e-3a2c-4d6b-8f0e-2b7c1e5a9d34",
  "ServiceName": "Primary Database",
  "Meta": { "host": "db-01", "region": "us-east" },
  "LogEntry": "Acknowledged by Jane Doe",
  "GoAlertURL": "https://goalert.example.com/alerts/79694"
}
1 PR by @MichaelUray
  • Added more json fields to AlertStatus webhook notification by @MichaelUray in #4445

🛡️ Administration & Operations

Webhook: Block Private Network Destinations

A new Block Private Addresses toggle in the Admin → Webhook section rejects webhook deliveries to private, loopback, and link-local addresses (e.g. 10.0.0.0/8, 127.0.0.1, 169.254.169.254). The check happens at connection time against the resolved IP, so DNS names and redirects that resolve to internal addresses are covered as well. Blocked deliveries fail permanently with "destination address is not allowed by administrator" rather than retrying.

This is strictly opt-in and off by default. It was prompted by an external report and is treated as hardening rather than a vulnerability fix: webhooks are disabled by default, the request shape is fixed, and responses are never read.

Two related clarifications for admins:

  • The Webhook.AllowedURLs description and the webhook documentation now make explicit that an empty allowlist permits any destination, including internal ones. If you've enabled webhooks, review this setting.
  • If outbound requests go through an HTTP proxy, the proxy performs the dial, so destination policy must be enforced at the proxy. The setting description notes this.
2 PRs by @CameronJHall, @mastercactapus

Slack: Option to Keep Thread Replies in the Thread

When an alert is acknowledged or closed, GoAlert replies in the original Slack message's thread and, until now, always broadcast that reply to the main channel. A new Disable Broadcast Thread Replies toggle (Admin → Slack) keeps status updates in the thread only, reducing noise in busy channels. Default is off (existing behavior).

1 PR by @AdityaHebballe

Application Name Applied Consistently

The General.ApplicationName setting is now used in the navigation bar and on the login page. Previously these were hardcoded to "GoAlert" even though the browser tab title respected the setting.

1 PR by @mattklaver-hs

Remote Monitor: Error API Key Now Optional

The goalert monitor remote monitor no longer requires an ErrorAPIKey in its config. If omitted, a warning is logged instead, which is convenient for test or staging monitors that don't need error reporting.

1 PR by @mastercactapus

🛠️ Universal Integration Keys (Experimental)

Continued enhancements to Universal Integration Keys (enabled with --experimental=univ-keys):

  • Colored Slack signal messages: Slack channel actions now accept a color dynamic parameter, supporting good, warning, danger, or a #RRGGBB hex value. Unset or invalid values fall back to the default blue. See the UIK documentation for details.
  • sendSignal mutation: Send a signal message directly to a service via GraphQL, for cases where the programmability of a full integration key isn't needed.
  • Auto-close fix: Alerts created through a UIK rule with a configured dedup key were being stored with the default summary+details hash instead, which prevented upstream systems from closing them automatically. The configured dedup key is now applied on create/update.
UIK Slack action with color parameter Colored Slack signal message
3 PRs by @CameronJHall, @KatieMSB, @mastercactapus

🔧 System Improvements

Job Queue: Database-Driven Events

Continuing the job queue migration from v0.33/v0.34, alert status updates, rotation changes, and signal processing are now triggered by database triggers that enqueue jobs directly (using LISTEN/NOTIFY), replacing the in-process event bus and the trigger → queue → poll fallback path for rotations. This guarantees no missed events regardless of which instance made the change, and is another step toward first-class multi-instance deployments.

A follow-up fix ensures these trigger-created jobs land in their dedicated queues rather than the default queue, so they're picked up immediately instead of waiting on the polling fallback.

3 PRs by @mastercactapus

Quieter Alert Logs

Repeated deduplication entries for the same alert are now debounced: if a duplicate arrives within 5 seconds of a previous dedup log entry, no additional entry is written. This keeps alert logs readable for noisy sources.

1 PR by @mastercactapus

🐛 Bug Fixes

  • Slack account linking no longer fails when a Slack profile has empty user.name or username fields; linking is only rejected if both the user ID and team ID are missing.
  • Temporary schedules now reliably render a day header for every day, fixing an intermittent missing-subheader issue.
2 PRs by @Lanithane, @mastercactapus

🏗️ Behind the Scenes

Go 1.26 and Toolchain Updates

GoAlert now builds with Go 1.26, along with updates to all Go dependencies and the build environment image. Deprecated River and net/http APIs were updated accordingly.

3 PRs by @AlaricWhitney, @KatieMSB, @mastercactapus

UI Framework: Material UI v6

The web UI was upgraded from MUI v5 to v6, with list components migrated to the newer CompList implementation as a prerequisite. This is largely invisible to users but keeps the frontend on a supported foundation.

2 PRs by @Lanithane, @mastercactapus

TypeScript Migration

The ongoing JavaScript → TypeScript migration continues, covering the wizard, actions, and remaining top-level config files.

3 PRs by @Azakahul, @KatieMSB
  • ui/wizard: convert wizard components to typescript by @KatieMSB in #4285
  • ui/src: convert misc files directly under src to typescript by @KatieMSB in #4281
  • Updating actions folder to typescript by @Azakahul in #4303

Testing, CI, and Repository Housekeeping

Fixed several deadlock scenarios in the smoke test harness that caused flaky tests, updated the PR size labeler to work with forks, enabled DCO (Developer Certificate of Origin) checks per org policy, and removed the repo-level code of conduct in favor of the org-wide one.

5 PRs by @KatieMSB, @mastercactapus, @ospo-compliance-bot
  • harness: fix some deadlock issues causing flaky tests by @mastercactapus in #4490
  • chore: update pr size labeler workflow by @KatieMSB in #4538
  • fix: add working directory for labeler workflow by @KatieMSB in #4539
  • Add DCO configuration by @ospo-compliance-bot in #4488
  • Remove repo-level CODE_OF_CONDUCT.md (org policy) by @ospo-compliance-bot in #4491

📦 Dependencies

This release includes routine dependency updates for Go modules, JavaScript packages, and GitHub Actions, including gRPC, cel-go, Vite, Storybook, and CodeQL.

22 PRs by @dependabot[bot]
  • build(deps): bump github.com/google/cel-go from 0.28.0 to 0.29.0 by @dependabot in #4529
  • build(deps): bump google.golang.org/grpc from 1.83.1 to 1.83.2 by @dependabot in #4559
  • build(deps): bump google.golang.org/grpc from 1.82.1 to 1.83.1 by @dependabot in #4556
  • build(deps): bump google.golang.org/grpc from 1.81.1 to 1.82.1 by @dependabot in #4526
  • build(deps): bump github/codeql-action from 4 to 4.37.3 by @dependabot in #4534
  • build(deps): bump actions/cache from 4 to 6 by @dependabot in #4518
  • build(deps-dev): bump @babel/core from 7.28.4 to 7.29.6 by @dependabot in #4517
  • build(deps-dev): bump vite from 6.4.2 to 6.4.3 by @dependabot in #4516
  • build(deps): bump the go-deps group across 1 directory with 15 updates by @dependabot in #4512
  • build(deps): bump the go-deps group with 2 updates by @dependabot in #4504
  • build(deps): bump the go-deps group with 7 updates by @dependabot in #4502
  • build(deps): bump the go-deps group across 1 directory with 8 updates by @dependabot in #4493
  • build(deps): bump github.com/slack-go/slack from 0.23.0 to 0.23.1 by @dependabot in #4489
  • build(deps-dev): bump vite from 6.4.1 to 6.4.2 by @dependabot in #4487
  • build(deps-dev): bump lodash from 4.17.21 to 4.18.1 by @dependabot in #4486
  • build(deps-dev): bump storybook from 9.1.8 to 9.1.19 by @dependabot in #4475
  • build(deps): bump the go-deps group across 1 directory with 16 updates by @dependabot in #4446
  • build(deps): bump actions/checkout from 5 to 6 by @dependabot in #4444
  • build(deps): bump golang.org/x/crypto from 0.42.0 to 0.45.0 by @dependabot in #4443
  • build(deps-dev): bump glob from 11.0.3 to 11.1.0 by @dependabot in #4442
  • build(deps-dev): bump vite from 6.3.6 to 6.4.1 by @dependabot in #4428
  • build(deps): bump github/codeql-action from 3 to 4 by @dependabot in #4424

Note: The demo container is published with a separate commit, as the Makefile required a fix.

❯ docker run --rm -it docker.io/goalert/demo:v0.35.0 goalert version
Version:   v0.35.0-demofix.1
GitCommit: 524cb0b73540616898fe85de371edd49e8bb1fcc (clean)
BuildDate: 2026-09-21T16:58:50Z
GoVersion: go1.26.6 (gc)
Platform:  linux/amd64
Migration: cm-private (#281)

🙏 New Contributors

Thanks to our new contributors who helped make this release possible:

Full Changelog: v0.34.1...v0.35.0

Don't miss a new goalert release

NewReleases is sending notifications on new releases.