v1.7.0-rc.2
Pre-release
Pre-release
v1.7.0-rc.2
Full Changelog: v1.7.0-rc.1...v1.7.0-rc.2
[1.7.0-rc.2] — 2026-08-20
Added
- Action-policy resolution is surfaced on the API and UI (spec-6.0.1-action-policy.md, slice 5).
GET /api/v1/containersand SSE container payloads now include anactionPolicyobject (state:blocked/manual/auto, plustriggerIdand, when blocked,reason) onupdateEligibility, reflecting the winning action trigger's resolved verdict for that container independent of whether it produced a blocker.GET /api/v1/containers/{id}/triggersgains a per-triggerresolvedStatefield with the same three values, so every candidate action trigger's individual verdict is visible, not just the winner. The container list/detail UI shows a new Auto badge (Update Status panel, full-page Actions tab, side panel trigger rows) wherever a container's or trigger's resolved state isauto, with tooltips explaining the blocked/manual/auto states. Newdd.action.autocontainer label andonautoAUTOmode: underAUTO=onauto,dd.action.includekeeps granting manual access but automatic dispatch additionally requires add.action.automatch, letting a container opt into manual-only access without picking up automatic execution. Drydock now logs a startupWARNfor any action trigger left onAUTO=onincludethat hasdd.action.include-matching containers with no correspondingdd.action.autolabel (would silently drop to manual-only if switched toonauto), and a secondWARNwhen a trigger is onAUTO=nonebut a container carries an inertdd.action.autolabel for it. Docs: newdd.action.autolabel andonautovalue are documented on the labels reference, triggers configuration, and update-eligibility pages, including a callout on all three clarifying thatAUTO=none/AUTO=allalso set the baseline access default (not just automatic execution) —AUTO=nonestill opens manual access to every container,AUTO=allopens both. - Greptile second-opinion review, summoned behind a label. A new thin caller workflow,
.github/workflows/greptile.yml, fires only onpull_request: labeledevents for thesecond-opinionlabel and calls the org's reusableCodesWhat/.githubgreptile-summon.ymlworkflow (pinned to a frozen commit SHA, no floating ref) to post one@greptileaireview request per exact PR head. CodeRabbit stays the automatic review lane; Greptile is opt-in only, for security-sensitive diffs, large refactors, or a tiebreaker when CodeRabbit and the author disagree..coderabbit.yamlgains alabeling_instructionsentry so CodeRabbit can auto-apply thesecond-opinionlabel itself when those criteria hit, in addition to a human applying it manually. (#751) release-cut.ymlcan now cut a maintenance patch from a retired dev branch without touching the cosign signing identity. A new optionalsource_refinput (e.g.dev/v1.6) builds and releases from that branch's tip instead ofmainHEAD, for a patch on a linemainhas already moved past (v1.6.1aftermainis on v1.7). The workflow itself still only ever dispatches fromrefs/heads/main, so the published cosign OIDC identity is unaffected.source_refis bounded hard — it must be an exactdev/vX.Ybranch that exists on origin, sharerelease_tag's line, not be the active (highest) dev line, and pair with a nonzero patch version — and the main-sync drift guard is skipped rather than failed, since there's no drift claim to make against a line main has already superseded. Left empty, every normal cut is unchanged. SeeRELEASING.md's new "Maintenance cuts" section for the full precondition list, including the manualci-verify.yml/e2e-playwright.ymldispatch required before cutting.
Changed
- Documented the container SBOM attestation in the release-verification guide.
release-cut.ymlhas generated and attested a signed SPDX 2.3 SBOM for every release container image since the v1.5 hardening batch, butcontent/docs/current/guides/verifying-releasesnever told readers how to check it. Added a "Verify the container SBOM attestation" section with thegh attestation verify --predicate-type https://spdx.dev/Document/v2.3command the workflow itself uses, and a note on the human-readabledrydock-${TAG}.image.spdx.jsoncopy attached to each release. No workflow change — the SBOM and build-provenance attestations, and their in-workflow verification, already met the house SLSA Build L2 / SBOM-attestation standard; only the user-facing documentation was missing. (#759) - Dropped the trivy qlty plugin in favor of Grype. Grype already owns the vuln-scanning surface (
.github/workflows/security-grype.yml); trivy's qlty-pluginconfigdriver only ever ran its DS*/KSV* Dockerfile misconfiguration checks, which checkov already covers (thecheckov:skip=CKV_DOCKER_3comment onDockerfileline 1, with matching rationale in.trivyignore.yamlfor the standalonetrivybinary the image ships at runtime — a separate concern, untouched here). Removed the[[plugin]] name = "trivy"block and thetrivy:DS002/trivy:DS-0002triage entry from.qlty/qlty.toml; trufflehog stays as-is. (#753) - Faster initial load on the login screen. The app's logo asset had drifted 4x oversized and was still being base64-inlined into the parse-blocking entry chunk (~640KB, reachable before authentication). It's now correctly sized and served as a separately cacheable file, cutting the entry chunk's gzipped size roughly in half and no longer re-downloading on every release. (#805)
- BREAKING: the Home Assistant MQTT topic layout now includes an
agent/<name>segment by default for agent-owned containers. Announced in v1.5.x and scheduled for v1.7.0. Multi-agent deployments previously collided on shared watcher names; the corrected layout scopes topics, sensor counts, and discovery cleanup per agent. Home Assistant references to the old topic paths must be re-pointed. SetDD_NOTIFICATION_MQTT_<name>_HASS_AGENTTOPICSEGMENT=falseto keep the old layout temporarily. Drydock does not retroactively clean up the old, pre-upgrade discovery entities. They remain retained on the broker and show up in Home Assistant as orphaned, frozen-at-last-state duplicates until manually removed.
Fixed
- Dependency-group admission no longer loses restart context when an upstream member is rejected. A restart-only dependent whose upstream was rejected during admission was previously misclassified as a normal update in wave 0 instead of being restarted after its dependency finished (or skipped if it never ran). The original admission batch is now carried through dispatch and API annotation so rejected members no longer corrupt the graph for their accepted dependents. (#718)
- Docker Compose updates no longer carry forward stale image-inherited environment defaults. A compose refresh could leave an old image's default environment value in place after updating to a new image with a different default, even though explicit runtime overrides on the container were always respected correctly. (#736, closes #734)
- Stashed update-policy overrides now survive drydock's own self-update. The
updatePolicyRetentionCachethat lets a recreated container inherit its predecessor's policy overrides lived only in process memory and was silently lost on the exact restart caused by a self-update. It now persists write-through to the store and rehydrates at startup, mirroring the same fix already applied to the maturity-clock cache. (#743, fixes #565)
Fixed
- Drydock no longer reports "Up to date" when an update check failed. A registry error while verifying a candidate's digest discarded the newer tag that had already been found, and the UI presented the result as a confirmed "no update available." A check that could not complete now surfaces as an explicitly unknown status instead of a negative answer. This also covers transient registry failures reported separately. (#814, #808)
- A single malformed container no longer zeroes out an entire agent inventory sync.
AgentClient.processAuthoritativeContainers()— the loop driving both the standard-mode handshake (_doHandshake(), every initial load and reconnect) and edge-modehandleContainerSync()(everydd:container_syncframe) — had no per-container error isolation, unlike the sibling SSE watcher-snapshot path. One container that threw while building its report (for example, during controller-side SBOM document offload) aborted the whole batch before any container reached the store, so a single bad container could make an otherwise-healthy fleet's inventory sync silently report zero containers. Each container in the batch is now processed independently, matching the existing isolation pattern inhandleWatcherSnapshotEvent(): a failure is logged with the container id and the batch continues with the rest. A batch where every container fails now also logs a single proportionate warning, since a silently empty result was what let this go unnoticed for a release cycle. Found while investigating #802; this closes the isolation gap but does not confirm the SBOM path as that issue's trigger — Portwing does not currently sendsecurity.sbomon its container wire format, so #802 remains open pending its actual root cause. - Nested OCI image indexes now resolve to the real image manifest. An image whose per-platform entry is itself an index, which is what Buildx produces when SBOM or provenance attestations are enabled, failed with
Unexpected error; no manifest foundand left the container unable to complete a digest check. Drydock now follows the nested index to the platform's actual manifest, bounded to three levels, and correctly ignores the attestation manifest alongside it. (#814)
Removed
- Retired the self-hosted star-history provider and the Warpchart embed that replaced it, in favor of a committed SVG chart. The self-hosted
/api/star-historyroute readprocess.env.GITHUB_TOKENat request time, and since that variable was always unset in production, it served a placeholder "check back shortly" SVG at HTTP 200 instead of erroring. Nothing ever went red, so the chart silently never rendered for as long as the route existed. Deleted the route handler, its SVG-rendering and request-resolution libraries, and the homepageStarHistorycard. Warpchart was adopted next as its replacement, then retired again days later once that decision reversed. Both now give way todocs/assets/star-history.svg, a chart built from GitHub's own stargazer timestamps and committed straight into the repo: it needs no runtime secret and cannot return a placeholder at HTTP 200, so a stale chart is visible and a missing one is a broken image, not a silent failure. Regeneration is handled by a scheduled shared workflow landing separately. All seven README language variants now point at the committed SVG instead of an external chart service. - BREAKING:
trigger-excludedandtrigger-not-includedare now hard blockers, ending the v1.5.0–v1.6.x soft-severity grace period (spec-6.0.1-action-policy.md, slice 6 — seeDEPRECATIONS.md). Previously, a container filtered out bydd.action.exclude/dd.action.include(or the legacydd.trigger.*labels) showed a Trigger filtered/Trigger excluded pill but still let a manual Update click through after a confirm-modal warning. As of this release both reasons are hard: the Update button is locked, and the API rejects a manual update request with the blocker's409message instead of queuing it — matching every other hard eligibility gate. This ships together with, and not before, the per-action execution policy (dd.action.autolabel,AUTO=onautotrigger mode) landed across slices 1-5, so the manual-only escape hatch isn't removed without a replacement path: an operator who wants a container to stay manually updatable while excluded from automatic dispatch now expresses that withAUTO=onautoplusdd.action.include(ordd.action.auto), rather than relying on the soft bypass.AUTO=oninclude's existing meaning is unchanged — a matchingdd.action.includelabel still grants both manual and automatic access under that mode. - Removed the unversioned
GET /api/auth/methodsalias. Deprecated since v1.6.0 and scheduled for removal in v1.7.0, it now returns 410 like the rest of the unversioned/api/*surface. UseGET /api/v1/auth/statusinstead.
Removed
- Removed
curlfrom the Docker image. Deprecated since v1.5.x with removal scheduled for v1.7.0. The image's own healthcheck has used the compiled/bin/healthcheckbinary since v1.5.0; user-defined HEALTHCHECK overrides that shell out to curl will stop working and should switch totest: /bin/healthcheck $${DD_SERVER_PORT:-3000}(the doubled$is compose escaping) or drop the override. Drydock now also logs a startup warning naming the container when it detects that its own healthcheck override still shells out to curl (in addition to the existing UI banner).
Security
- Scoped the Grype image gate around CVE-2026-14456 (openssl/libssl3/libcrypto3, Alpine 3.24) pending an upstream fix.
ci-verify.yml'sgrype-imagejob started failing--fail-on highafter this CVE (CWE-770, unbounded memory growth in OpenSSL's QUIC server incoming-channel-queue handling) entered the Grype DB; the packages are pinned at3.5.7-r0and no Alpine branch — 3.24-stable or edge — has repackaged the upstream 3.5.8 fix yet. Drydock links these libs only as a TLS client (theopensslCLI and Node's own TLS stack) for registry queries and the healthcheck, never as a QUIC/HTTP3 server, so the vulnerable path isn't reachable; OpenSSL's own advisory rates the flaw Low, versus the generic CVSS 7.5 the gate scores. Added a CVE-scoped (not location-scoped).grype.yamlignore entry for exactly this CVE on these three package names, so a different HIGH/CRITICAL finding in openssl still fails the gate. Two removal triggers: bump the Dockerfile's pinnedopenssl=version once Alpine ships >= 3.5.8 for 3.24, or 2026-11-17 (90 days out) regardless. - Closed a remote-property-injection path in the container list's URL query sync.
ContainersView's query-normalization helper wrote URL-controlled keys onto a plain object with bracket notation, which could in principle let a crafted__proto__query key reach the object's prototype chain. It now accumulates entries in aMapand materializes the result withObject.fromEntries, closing the vulnerability class rather than just suppressing the scanner finding. (#750)
Documentation
- Retroactively documented the
WUD_AGENT_SECRET/WUD_AGENT_SECRET_FILEremoval. v1.6.0-rc.1 silently dropped agent mode'sDD_AGENT_SECRET ?? WUD_AGENT_SECRETfallback (and the_FILEequivalent) with no deprecation period, and it was never recorded in the v1.6.0 changelog entry orDEPRECATIONS.mdat the time. An agent configured with only the legacyWUD_AGENT_SECRETwent from authenticating to a startup failure whose message doesn't name the variable actually set. See the newDEPRECATIONS.mdentry.