Sitelet https://github.com/crazy-max/diun/pull/1813
Skip to content

feat(notif): add digest mode to batch notifications into a single message per scan run - #1813

Open
slmingol wants to merge 11 commits into
crazy-max:masterfrom
slmingol:feat/notif-digest
Open

feat(notif): add digest mode to batch notifications into a single message per scan run#1813
slmingol wants to merge 11 commits into
crazy-max:masterfrom
slmingol:feat/notif-digest

Conversation

@slmingol

Copy link
Copy Markdown

What this does

Adds a digest boolean field to the Notif config that, when enabled, buffers all image update notifications from a single scan run and fires them as one batched message instead of one message per image.

This is useful when monitoring many containers across multiple hosts -- without digest mode, a single scan can flood a Slack channel (or other notifier) with dozens of individual messages.

How it works

  • A pendingNotify flag is set on each NotifEntry that passes all notification filters in job.go, instead of immediately calling Send
  • After all workers finish (wg.Wait() in app.go), SendBatch is called once with the full NotifEntries set
  • Notifiers that want custom batch formatting implement the new optional BatchHandler interface (SendBatch(*model.NotifEntries) error)
  • Notifiers that don't implement BatchHandler fall back to individual Send calls for each pending entry, so existing notifiers work without changes

Changes

Model / core

  • internal/model/notif.go — adds Digest bool to Notif; adds pendingNotify field with MarkPendingNotify()/PendingNotify() accessors to NotifEntry; adds mutex-safe Add() to NotifEntries
  • internal/notif/notifier/notifier.go — adds optional BatchHandler interface
  • internal/notif/client.go — adds IsDigest() and SendBatch()
  • internal/app/job.go — marks entry pending instead of sending immediately when digest mode is on
  • internal/app/app.go — calls SendBatch after wg.Wait() when digest mode is on

Slack notifier

  • internal/notif/slack/client.go — implements BatchHandler.SendBatch with a compact bullet-list summary (registry-stripped image names, HubLink hyperlinks where available, build date, short sha256 digest, clean hostname)

Config

notif:
  digest: true
  slack:
    webhookURL: https://hooks.slack.com/services/...

Or via environment variable:

DIUN_NOTIF_DIGEST=true

Example output (Slack)

2 image(s) need attention — 0 new, 2 updated (host: docker-host-02)
• <https://hub.docker.com/r/library/nginx|library/nginx:latest> updated · Jul 17 · `sha256:1c4e1721`
• `searxng/searxng:latest` updated · Jul 11 · `sha256:8a865b0a`

Notes

  • Only the Slack notifier implements BatchHandler in this PR; other notifiers fall back to individual sends automatically
  • The NotifEntries.mu mutex guards concurrent worker writes; the pointer receiver on SendBatch avoids copying it
  • No breaking changes to existing config or notifier interfaces

🤖 Generated with Claude Code

slmingol and others added 11 commits July 15, 2026 02:04
…sage

Adds a `digest` boolean to the notif config. When enabled, per-image
Send calls are suppressed during a scan run; a single SendBatch call
fires after all workers complete, sending one message per notifier.

Slack implements BatchHandler for a true summary message (one attachment
per update, @channel header with counts). All other notifiers fall back
to deferred individual sends at end-of-run via the SendBatch loop.

Also fixes a pre-existing data race: NotifEntries.Add was called
concurrently from the worker pool without a mutex.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Routes image publish to ghcr.io/slmingol/diun instead of upstream.
Removes DockerHub login/push/inspect (no secrets in fork).
Adds feat/** to push triggers so feature branches build images.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… counters

Global CountNew/CountUpdate include entries filtered by notifyOn/firstCheck
rules that never get PendingNotify() set — causing header count to exceed
the actual bullet list length.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Strip registry prefix and sha256 digest from image names, trim diun__
hostname prefix, and remove <!channel> mention from batch summary.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each bullet now shows the image build date and first 8 hex chars of
its sha256 digest for quick identification.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
mode=max provenance and sbom push in-toto attestation manifests that
older Nexus proxy versions cannot serve, causing pull failures on hosts
with newer Docker versions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When a HubLink is available, render the image as a Slack hyperlink
with a short name (last two path segments + tag) hiding the full URL.
Falls back to backtick code formatting when no HubLink exists.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@slmingol
slmingol requested a review from crazy-max as a code owner July 18, 2026 04:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant