Schedule icon
Commands icon
Docker icon
Return icon
Switch icon
If icon
SlackIncomingWebhook icon
Log icon

Dagger Pinned Image Digest Integrity Gate

Verify a pinned container image digest still resolves from its registry with Dagger, classify integrity status, and alert Slack on breach.

Categories
Infrastructure

A deployment manifest pinned to an exact image digest is supposed to be immutable, but the registry side is not: digests get garbage-collected, images get deleted for licensing or security reasons, and retention policies silently expire old manifests. The first sign is usually a failed deploy at the worst possible time. This blueprint uses plugin-dagger's native Commands task as the probe, running a containerized Dagger pipeline that attempts to pull the exact pinned digest and classifies whether the registry still serves it.

How it works

  1. verify_pinned_digest (io.kestra.plugin.dagger.Commands) runs container | from <pinned_image_digest_ref> | with-exec echo pinned-digest-resolved | stdout via dagger shell -c on a Docker-based task runner. The task's own install step pulls a pinned Dagger CLI release and verifies it against the official checksums file before use. allowFailure: true lets the flow continue instead of aborting when the pull or exec fails.
  2. classify_digest_integrity (io.kestra.plugin.core.debug.Return) checks outputs.verify_pinned_digest.exitCode. If it is not defined at all (the task never produced output, for example a Docker runner or Dagger install failure), the classification is PROBE_FAILED. If it is defined and non-zero, the classification is DIGEST_MISSING, meaning Dagger ran but the registry rejected the pinned digest. If it is exactly zero, the classification is PINNED_DIGEST_INTACT.
  3. route_by_digest_status (io.kestra.plugin.core.flow.Switch) branches on that string. DIGEST_MISSING runs evaluate_alert_toggle (io.kestra.plugin.core.flow.If), which pages Slack via dispatch_digest_missing_alert only when alert_on_missing_digest is true, logging a suppressed-alert line otherwise. PINNED_DIGEST_INTACT logs a clean audit line. PROBE_FAILED pages Slack directly since integrity is now unknown. The defaults branch logs a fallback line that is unreachable under normal operation.
  4. log_audit_summary records a status line on every run regardless of branch, so execution history reads as a continuous digest-integrity trend.
  5. The flow-level errors block pages Slack separately if the sentinel flow itself fails outright.
  6. The digest_integrity_schedule trigger polls every 6 hours with explicit input overrides, shipped disabled: true until pointed at a real pinned digest.

What you get

  • A scheduled, containerized verification that a specific pinned image digest is still pullable from its registry, with zero custom pipeline code beyond the one Dagger shell command.
  • A three-way status output (PINNED_DIGEST_INTACT / DIGEST_MISSING / PROBE_FAILED) other flows can gate on, for example before promoting a release that references the same digest.
  • A Slack page naming the exact pinned reference and exit code on breach or probe failure.
  • A separate failure channel for the sentinel flow itself, plus a standing per-run audit log line.

Who it's for

  • Platform and release engineers who pin deployment manifests to image digests (not mutable tags) and need to know before a deploy, not during one, that the digest has disappeared.
  • Security and supply-chain teams auditing whether approved, scanned image digests remain available as registries apply retention or garbage-collection policies.
  • SRE teams standardizing Day 2 container supply-chain governance across services inside Kestra.

Why orchestrate this with Kestra

A one-off docker pull from a laptop proves a digest is resolvable for one instant and leaves no record. This flow keeps every check in Kestra's execution history, turns the result into a typed status output other flows (release gates, promotion pipelines) can depend on, and gives you an If/Switch branch to grow into real remediation (re-pinning to a new approved digest, opening a ticket) without rewriting the probe itself.

Prerequisites

  • A Kestra Worker with access to a Docker daemon, since verify_pinned_digest runs on the io.kestra.plugin.scripts.runner.docker.Docker task runner.
  • Outbound network access from that Docker daemon to the image registry holding the pinned digest.
  • A real pinned digest reference to audit; the shipped default is a placeholder and must be replaced.
  • A Slack incoming webhook for the breach, probe-failure, and flow-failure alerts.

Quick start

  1. Add the SLACK_WEBHOOK secret.
  2. Replace pinned_image_digest_ref with a real registry/image@sha256:digest your deployments depend on.
  3. Run the flow once and confirm status reads PINNED_DIGEST_INTACT.
  4. Enable digest_integrity_schedule.

How to extend

  • Record each confirmed-intact digest in a KV store alongside its tag, then compare a freshly resolved tag-to-digest mapping against that record to detect silent tag-to-digest drift, not just outright digest loss.
  • Fan the probe out to every digest referenced by your current production manifests with a Loop task to audit a whole fleet in one run.
  • On DIGEST_MISSING, automatically open a ticket or trigger a re-pin workflow instead of only alerting.
  • Push status into a KV store to alert only on state transitions instead of every run.

Secrets

  • SLACK_WEBHOOK: Slack incoming webhook used for the breach, probe-failure, and flow-failure alerts.
  • OSS note: if your Kestra instance reads secrets from environment variables instead of a secrets backend, set SECRET_SLACK_WEBHOOK as base64, for example echo -n "<your-webhook-url>" | base64.

Inputs

  • pinned_image_digest_ref (STRING, default <your-registry>/<your-image>@sha256:<your-pinned-digest>): fully qualified pinned image reference to audit.
  • dagger_container_image (STRING, default curlimages/curl:latest): base image the Dagger CLI installs into.
  • alert_on_missing_digest (STRING, default "true"): when "true", page Slack on a missing digest; when "false", only log it.

Outputs

  • {{ outputs.verify_pinned_digest.exitCode }}: exit code of the Dagger pipeline invocation.
  • {{ outputs.classify_digest_integrity.value }}: classification string, one of PINNED_DIGEST_INTACT, DIGEST_MISSING, PROBE_FAILED.
  • {{ outputs.status }}: flow-level alias of the same classification.

Pitfalls

  • image-ref, the Dagger Container API function that returns a resolved reference, can only be called immediately after a from() call; it is not used here because chaining it with a comparison inside the same pipeline is not something this blueprint asserts without having executed it. The probe instead proves resolvability indirectly through with-exec, which is the mechanism actually verified against plugin-dagger's source and documented CLI examples.
  • plugin-dagger's ScriptOutput does not expose raw stdout text as a queryable field, only vars (populated solely via Kestra's ::{"outputs":...}:: marker convention), exitCode, and outputFiles. This blueprint classifies on exitCode alone for that reason; it does not attempt to parse the Dagger pipeline's printed output.
  • The Docker-based task runner needs a reachable Docker daemon from the Kestra Worker; without one, every run classifies as PROBE_FAILED, not DIGEST_MISSING.
  • dagger_container_image must provide curl, tar, uname, and sha256sum, since the task's install step downloads and checksum-verifies the pinned Dagger CLI release on first use.
  • A mutable tag (for example :latest) always "resolves" to whatever it currently points at; this gate is only meaningful against a true @sha256: digest reference, not a tag.

Local Container Setup

This flow needs a Docker daemon reachable from the Kestra Worker, not a separate broker container. If Kestra itself runs in Docker, confirm the Worker container can reach the host's Docker socket, and that the Worker's Docker network can resolve the image registry you are auditing:

docker network connect YOUR_KESTRA_NETWORK

(Run docker network ls to verify your active Kestra network name.)

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.