v1.7.0-rc.9
Full Changelog: v1.6.1-rc.7...v1.7.0-rc.9
[1.7.0-rc.9] — 2026-09-03
Security
fflateoverride added ine2e/for CVE-2026-45820.@smithy/middleware-compressionpinsfflateat 0.8.1 exactly, so the fix had to come through an npm override;fflatenow resolves to 0.8.3 ine2e/package-lock.json, which is the only workspace that carried the vulnerable range. Lockfile-only change, no runtime code touched.
Fixed
watchFromCron()had no re-entrancy guard, so overlapping scans on a large fleet fired the same trigger multiple times for one update. A full scan can take minutes, and the cron schedule, the docker-events debounce, the discovery-settle timer, and the startup timer could all start one while the previous scan was still running. Each overlapping call ran its ownwatch(), and a tag first seen mid-burst passed theonce=truehistory check in every one of them before any of them recorded it, so a trigger like Telegram fired once per overlapping scan for the same update.watchFromCron()is now single-flight: a request while a scan is running does not start a second one, it records that a rescan was requested, and exactly one follow-up scan runs once the current scan finishes, so a docker event that arrives mid-scan is not lost. A scan that never settles cannot wedge later ticks either: the in-flight scan is raced against a deadline, and when it fires every caller waiting on that scan, the one that started it and any that coalesced into it, resolves to an empty result and the next tick starts a fresh scan. The "Cron started" log line now also names what triggered the scan (schedule, docker-event, discovery-settle, startup, or maintenance-window). Reported by @tarzan77cz in #972.- A
once=truetrigger re-fired hours later for a tag update it had already announced, whenever a registry rate-limited the accompanying digest lookup. The notification-history hash for a tag-kind update included the digest and, when the digest was absent, the image'screatedtimestamp. A transientDigest watch failed (429)dropped the digest for that scan only, socreatedswapped into the hash on the failed scan and back out once the digest lookup next succeeded, and the once history stopped matching an update it had already sent. The hash for a tag-kind update on a container with digest watching configured is now keyed on the tag alone, so a digest lookup failing or succeeding does not change it; a container without digest watching configured keeps the originalcreatedfallback, and digest-kind updates stay keyed on the digest. Concurrent evaluations of the same history key are also now atomic within the process: the check and the reservation happen in the same synchronous step, so two overlapping scans evaluating the same result cannot both pass the check and both send, which is why one trigger could fire four times in the same millisecond while another skipped. After upgrading, a container with digest watching enabled, a tag update already pending, andonce=truewhose stored hash came from a scan where the digest lookup had succeeded re-notifies exactly once, because the stored hash format changed; every scan after that stays stable. Reported by @tarzan77cz in #972. - Two deprecation banner strings in the UI still described the legacy
DD_TRIGGER_*env vars and the curl-based healthcheck override as active with a future removal. Both were removed outright in v1.7.0;legacyConfigBodyandcurlHealthcheckBody(all 17 locales) now say so instead of pointing at a deadline that already passed. The curl banner's{bin}slot also rendered the compose migration snippet with a single$, which compose expands from the host environment before the container starts; doubled it to$${DD_SERVER_PORT:-3000}to match the corrected DEPRECATIONS.md snippet. - The marketing site's Get Started snippets deployed an instance that never became healthy. Neither the
docker runquick-start nor the hardened compose preset configured any authentication, so the instance ended up with zero registered auth strategies, which is a deliberate fail-closed state:/healthstays503forever and there's no way to log in. Both presets also skipped a/storevolume, so container state and audit history didn't survive a restart. The quick preset now setsDD_ANONYMOUS_AUTH_CONFIRM=true, the hardened preset setsDD_AUTH_BASIC_ADMIN_USER/HASH, and both mountdrydock-store:/store.
Documentation
- A docs audit turned up a batch of claims in the README, DEPRECATIONS.md, and the configuration/triggers/registries/API/monitoring/agents docs that no longer matched this tree's code. Compose examples that mangled a pasted argon2id hash and omitted the
/storevolume; translated READMEs with mistranslated provider names and a backwards roadmap pointer; a deprecation entry using the wrong removal-status label plus four missing DEPRECATIONS.md entries; hook, threshold, and rollback documentation describing behavior the code doesn't have (amajorthreshold letting digest-only updates through unfiltered, an allowlist that's exact-match rather than basename, a third rollback path that doesn't exist); trigger docs missing template variables and shared config keys; registry docs describing a routing mechanism and a rate-limit scope that don't exist and missing thepublic.ecr.awsrate row; API docs with a stale audit action list, wrong log-filter semantics, a nonexistent SSE event, and a rate limit bypass that isn't real; and feature docs conflating the docker trigger's health gate withdd.rollback.auto, describing a malformed signing key as a graceful skip when it crashes the controller, and framing a fail-closed/health503 as a transient startup state. All of it corrected against this tree's actual code, not against v1.8's.