Skip to content

Releases: CodesWhat/drydock

v1.7.0-rc.2

v1.7.0-rc.2 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 21 Aug 06:35
efcfe04

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/containers and SSE container payloads now include an actionPolicy object (state: blocked/manual/auto, plus triggerId and, when blocked, reason) on updateEligibility, reflecting the winning action trigger's resolved verdict for that container independent of whether it produced a blocker. GET /api/v1/containers/{id}/triggers gains a per-trigger resolvedState field 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 is auto, with tooltips explaining the blocked/manual/auto states. New dd.action.auto container label and onauto AUTO mode: under AUTO=onauto, dd.action.include keeps granting manual access but automatic dispatch additionally requires a dd.action.auto match, letting a container opt into manual-only access without picking up automatic execution. Drydock now logs a startup WARN for any action trigger left on AUTO=oninclude that has dd.action.include-matching containers with no corresponding dd.action.auto label (would silently drop to manual-only if switched to onauto), and a second WARN when a trigger is on AUTO=none but a container carries an inert dd.action.auto label for it. Docs: new dd.action.auto label and onauto value are documented on the labels reference, triggers configuration, and update-eligibility pages, including a callout on all three clarifying that AUTO=none/AUTO=all also set the baseline access default (not just automatic execution) — AUTO=none still opens manual access to every container, AUTO=all opens both.
  • Greptile second-opinion review, summoned behind a label. A new thin caller workflow, .github/workflows/greptile.yml, fires only on pull_request: labeled events for the second-opinion label and calls the org's reusable CodesWhat/.github greptile-summon.yml workflow (pinned to a frozen commit SHA, no floating ref) to post one @greptileai review 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.yaml gains a labeling_instructions entry so CodeRabbit can auto-apply the second-opinion label itself when those criteria hit, in addition to a human applying it manually. (#751)
  • release-cut.yml can now cut a maintenance patch from a retired dev branch without touching the cosign signing identity. A new optional source_ref input (e.g. dev/v1.6) builds and releases from that branch's tip instead of main HEAD, for a patch on a line main has already moved past (v1.6.1 after main is on v1.7). The workflow itself still only ever dispatches from refs/heads/main, so the published cosign OIDC identity is unaffected. source_ref is bounded hard — it must be an exact dev/vX.Y branch that exists on origin, share release_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. See RELEASING.md's new "Maintenance cuts" section for the full precondition list, including the manual ci-verify.yml/e2e-playwright.yml dispatch required before cutting.

Changed

  • Documented the container SBOM attestation in the release-verification guide. release-cut.yml has generated and attested a signed SPDX 2.3 SBOM for every release container image since the v1.5 hardening batch, but content/docs/current/guides/verifying-releases never told readers how to check it. Added a "Verify the container SBOM attestation" section with the gh attestation verify --predicate-type https://spdx.dev/Document/v2.3 command the workflow itself uses, and a note on the human-readable drydock-${TAG}.image.spdx.json copy 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-plugin config driver only ever ran its DS*/KSV* Dockerfile misconfiguration checks, which checkov already covers (the checkov:skip=CKV_DOCKER_3 comment on Dockerfile line 1, with matching rationale in .trivyignore.yaml for the standalone trivy binary the image ships at runtime — a separate concern, untouched here). Removed the [[plugin]] name = "trivy" block and the trivy:DS002/trivy:DS-0002 triage 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. Set DD_NOTIFICATION_MQTT_<name>_HASS_AGENTTOPICSEGMENT=false to 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 updatePolicyRetentionCache that 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-mode handleContainerSync() (every dd:container_sync frame) — 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 in handleWatcherSnapshotEvent(): 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 send security.sbom on its container wire format, so #802 remains open pending its actual root cause.
    ...
Read more

v1.6.1-rc.1

v1.6.1-rc.1 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 21 Aug 07:47
9d5b7da

v1.6.1-rc.1

Full Changelog: v1.7.0-rc.2...v1.6.1-rc.1

[1.6.1-rc.1] — 2026-08-21

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)
  • 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 found and 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)
  • 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-mode handleContainerSync() (every dd:container_sync frame) — 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 in handleWatcherSnapshotEvent(): 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 send security.sbom on its container wire format, so #802 remains open pending its actual root cause.

Note: this is a maintenance cut, built from dev/v1.6 at 9d5b7da896b73861ed1a10fb5c8d0104014af478, not from main.

  • The container image and release artifact are cosign-signed (identity release-cut.yml@refs/heads/main, since the workflow run itself always executes at that ref), but carry no SLSA build-provenance attestation — this workflow's own OIDC token always claims main HEAD as the build source, which would be false for this artifact, so attestation is skipped rather than publish a false claim.

v1.7.0-rc.1

v1.7.0-rc.1 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 14 Aug 14:04
dbc47a5

v1.7.0-rc.1

Full Changelog: v1.6.0...v1.7.0-rc.1

[1.7.0-rc.1] — 2026-08-14

Added

  • Edge Portwing polling cadence is configurable (#688). DD_PORTWING_POLL_INTERVAL sets the controller-owned Edge container-refresh interval in positive integer seconds; the authenticated welcome frame and reported agent metadata use the same value, while absent or invalid values retain the 300-second default.
  • Installable PWA support (Roadmap Phase 6.9). Drydock is now an installable Progressive Web App via vite-plugin-pwa: a web app manifest (Drydock, standalone display, theme/background color matched to the One Dark default --dd-bg, 192/512 icons plus dedicated maskable variants with safe-zone padding) and an auto-updating service worker (registerType: 'autoUpdate') that precaches the SPA shell so the dashboard boots offline. /api/** is explicitly excluded from all service-worker handling — no navigation fallback, no runtime caching — so a live dashboard never serves stale API data from cache; those requests always hit the network and surface a normal error if it's unreachable. A dismissible install banner (new InstallBanner component, following the existing AnnouncementBanner pattern) listens for the browser's beforeinstallprompt event and offers a one-click install, with the dismissal persisted under a versioned localStorage key. iOS home-screen install is supported via apple-mobile-web-app-capable and the existing apple-touch-icon. The backend's static UI server now serves sw.js with Cache-Control: no-cache so a new deploy is never masked by a browser-cached service worker script.
  • Clickable port links in the container list and detail views. Each host-published port in a container's details.ports now renders as a link (opened in a new tab, rel="noopener noreferrer") instead of inert text — in the side panel, the full-page detail tabs, and new opt-in "Ports" columns/rows in the table and card views. The scheme is auto-detected from the container-side port (443/8443https://, everything else → http://); the link target host prefers the port's own bound HostIp when it's a real address (not 0.0.0.0/::/::0), falling back to the agent's configured host for agent-watched containers, or the browser's own hostname otherwise. Internal-only (unpublished) ports still render as plain text. A new dd.port.label container label lets you attach a friendly name to a specific port (dd.port.label=80=Web UI,443=Admin Console) shown in place of the raw hostPort->containerPort/protocol mapping.
  • Container uptime, with a live-refreshing display. The existing details.startedAt field (from Docker's State.StartedAt) now also drives an opt-in "Uptime" tooltip showing the exact start timestamp in the container list, and a live "Up …" indicator in the card view's footer — both refresh on a timer and update immediately on SSE container-state changes, matching the full-page detail view's existing uptime display.
  • Keyboard shortcuts. / focuses the search bar from anywhere (unless focus is already in a text input), Escape closes the search bar, and ? opens a new shortcut-reference overlay listing the available shortcuts. A / hint now sits next to the existing ⌘K hint on the sidebar search button.
  • Container dependency ordering — data model and detection (v1.7, discussion #219). New dd.depends_on (comma-separated container names) and dd.depends_on.action (update or restart, default update) container labels. When dd.depends_on is absent, drydock detects dependencies automatically from a compose-managed container's own depends_on key (both short-form arrays and long-form objects; the condition: key is not yet consulted). A present label always overrides compose detection entirely rather than merging with it. Self-references and unknown compose-service targets are dropped with a logged warning, never a hard error; both fields are re-derived from the container's labels/compose file every watch cycle rather than persisted independently, so they self-heal automatically across container recreation. This lands the data model and detection only — using the resulting graph to order updates/restarts is a separate, later change.
  • Container dependency ordering — pure graph engine (v1.7, discussion #219). New app/dependencies/dependency-graph.ts: buildDependencyGraph resolves each container's detected dependsOn names against the rest of the fleet (dropping unknown targets and cross-agent edges with a logged warning, never a hard error — cross-host dependency chains remain out of scope for v1.7), and topologicalSort runs Kahn's algorithm to produce deterministic topological "waves" — arrays of containers safe to dispatch in parallel, tie-broken alphabetically for stable output. A dependency cycle is never a deadlock: cycle members are grouped and scheduled together as one unordered wave, while any non-cycle container downstream of that cycle still resolves correctly in its own later wave. Pure, dependency-free, and unwired — no watcher, trigger, or dispatch behavior changes yet; this only lands the engine that a later change will use to order updates.
  • Container dependency ordering — execution integration (v1.7, discussion #219). Accepted bulk container updates now dispatch wave-by-wave through runAcceptedContainerUpdates (app/updates/request-update.ts) instead of all at once, so a container never starts updating before every container it depends on has finished. A dependsOnAction: 'restart' dependent is admitted through the same admission gates as any manual request (widened updateAvailable check) and, once its dependency finishes updating, is restarted rather than re-pulled via the new restartDependentContainer primitive (app/updates/dependency-restart.ts); operations that never got a chance to run because an earlier wave failed land in a new skipped-dependency status/phase instead of silently vanishing. The Docker Compose trigger (app/triggers/providers/dockercompose/Dockercompose.ts) reorders multi-service docker compose up invocations by dependency order (sortMappingsByDependencyOrder) so compose itself never fights the same ordering. Maintenance-window batches (app/triggers/providers/Trigger.ts's runAcceptedUpdateBatch) cascade dependents through the same wave logic once their window opens.
  • Container dependency ordering — API exposure (v1.7, discussion #219). The container list response gains per-container dependencyCount/dependentCount badge counts. New endpoints: GET /api/v1/containers/dependencies returns the full resolved dependency graph (nodes, edges, detected cycles, unresolved targets, cross-host-ignored edges); POST /api/v1/containers/:id/update-chain-preview dry-runs the topological waves for the dependency chain rooted at a container without dispatching anything; POST /api/v1/dependency-groups/:rootId/update bulk-accepts every container in that chain, annotated with the wave index it will actually run in. The preview and dispatch endpoints call the exact same buildDependencyGraph/topologicalSort pair over the same input set, so the preview can never drift from what an accepted update actually runs.
  • Container dependency ordering — UI (v1.7, discussion #219). The container list now carries dependencyCount/dependentCount through to the UI Container type. A new "Update dependency chain" action (confirmDependencyGroupUpdate in useContainerActions) previews the resolved update waves for a container's dependency chain and shows them in a confirm dialog — including any detected cycle or unresolved-target warnings — before bulk-accepting the chain through the new dependency-groups endpoint. A dedicated dependency-hierarchy grouping view for the container list is deferred to a follow-up.
  • Debounced container discovery (#156). Docker briefly exposes transient rename aliases while a container is being recreated; drydock previously registered whatever it saw the instant listContainers returned it, so a container could momentarily register under its <hex-prefix>_<name> alias. First-seen containers (identified by Docker container ID, not present in the store) now enter a configurable "pending" state and must remain visible for a settling window — DD_WATCHER_{watcher_name}_DISCOVERY_SETTLE_MS, default 30000 (30s), 0 disables settling — before they're added to the store, triggers, or the API/UI; pending containers are visible in debug logs only. A deduplicated follow-up watch is scheduled for the earliest pending deadline, so an event-discovered container still registers on time when no further Docker event arrives before the next cron scan. If a pending container is renamed mid-window it registers under the final name once settled; if it disappears mid-window it's silently discarded (debug log only). Containers already known to the store are unaffected and continue to update immediately — settling applies exclusively to first-seen containers, so a same-ID recreation is never blocked from updating for 30 seconds. This complements, and does not replace, the unconditional hex-prefix alias stripping shipped in v1.5 for the same issue (getContainerName/canonicalizeContainerName in app/watchers/providers/docker/docker-helpers.ts, and the name-shape-triggered transient-alias suppression in filterRecreatedContainerAliases) — that mechanism is keyed off the container's name looking like a recreate alias, while the new settling wind...
Read more

v1.6.0

Choose a tag to compare

@github-actions github-actions released this 12 Aug 04:36
103bfdc

v1.6.0

Full Changelog: v1.6.0-rc.13...v1.6.0

[1.6.0] — 2026-08-11

Consolidates the 1.6.0-rc.11.6.0-rc.13 prereleases. Users upgrading from
1.5.2 get everything below; users already on 1.6.0-rc.13 receive no additional
runtime changes.

Added

  • Portwing edge and agent integration matures into a first-class transport. Portwing 0.9.0+ agents work over either inbound Standard HTTP or dial-out Edge WebSocket transport, and drydock 1.6.0-rc.11+ recognizes Portwing's own Docker watcher marker to run native registry checks plus single/batch Docker updates controller-side (#632, #637, Portwing #76). Standard-mode agents can sign every request with Ed25519 (DD_AGENT_{name}_AUTHMODE=ed25519, upgraded mid-series to signature version 2) instead of sending a shared secret. Edge agents stream live container logs and process deletes over the existing WS tunnel with correlated per-request IDs, pick their own display name (bound to their signing key so it can't be squatted), and are proactively disconnected when they stop answering pings. The portwing/1.0 edge endpoint is enabled by default (DD_EXPERIMENTAL_PORTWING=false remains an emergency disable), backed by a real fleet-soak workflow that runs signed Portwing processes against production infrastructure on every PR and on a weekly schedule.
  • Declarative, three-tier update policy with a maturity stabilization gate (Discussion #307, Discussion #406, #320). Containers can declare dd.updatePolicy.* labels (maturity mode/min-age, skip tags, skip digests) with watcher-level defaults and UI/API overrides, resolving through label → watcher default → persistent override precedence and surviving agent refresh, container recreation, and error-state rebuilds. A candidate held back by maturityMode: mature is visible immediately with a live minute-by-minute countdown to its unlock time, and a new maturity-cleared notification (backed by a 5-minute background sweep) announces the moment it clears instead of waiting for the next scan. Update Now remains available as an explicit soft-policy override.
  • Per-rule notification templates, bell preferences, and a new health-status event (Discussion #205, Discussion #198, Discussion #210). The Notifications view can override title/body/batch-title text per rule and provider with a live preview, and independently control which event categories reach the in-app bell — including a new container-unhealthy rule (disabled by default) that fires off Docker health-check transitions. Bidirectional MQTT lets Home Assistant's Install button trigger a real drydock update through the normal eligibility/dispatch path, rate-limited per container and audited. dd.action.*/dd.notification.* labels are now strictly scoped to their own trigger category instead of one silently gating the other (#494).
  • Global update mode and an actionable Update Status panel (Discussion #325). Settings → General now selects one server-wide mode — notify, manual, or auto — and the container side panel and detail view replace the old eligibility-badge stack with plain-language status plus a structured, deep-linking condition list covering all 16 eligibility reasons. Existing installs migrate to auto on upgrade so configured automatic updates keep working; fresh installs default to manual (see Upgrade Notes).
  • Cross-device preference sync and a zero-dependency dashboard grid (Discussion #220, #281). An opt-in Sync across devices toggle stores the full UI preference set server-side per user and propagates changes to a user's other signed-in sessions in real time over SSE. The dashboard grid drops its grid-layout-plus dependency for a deterministic CSS Grid implementation with edit-mode drag/reorder, bounded resizing, touch support, and per-breakpoint layout persistence. Audit, Security, Servers, and Watchers gain the same per-view column show/hide picker Containers and Agents already had.
  • More signal, less noise in update visibility. A pinned tag now surfaces the best newer same-family tag as a purely informational badge without making it an actionable update candidate (#498); a new Version Update filter isolates real semver bumps from digest-only churn on fleets that rebuild images daily (#538); and a startup warning flags minute-precise maintenance-window crons that only open their window for one minute per matching hour instead of the intended full range (Discussion #639).
  • Opt-in wud-card/Homepage compatibility endpoints. With the unversioned /api/* alias removed (see Removed, below), DD_COMPAT_WUDCARD=true (default false) mounts a narrow compatibility layer covering exactly the four endpoints the Home Assistant wud-card integration and Homepage's whatsupdocker widget call, reshaped into the bare-array response those integrations expect. (Discussion #469)

Changed

  • Every major list view is responsive and consistent. Containers, Agents, Notifications, Security, Triggers, Watchers, Servers, Registries, Audit, and Auth now share one DataTable with a persisted per-view table⇄card toggle that auto-reflows to cards below ~640px; card views gained a sort control; and Source/Release-notes/Registry resource shortcuts render through one consistent 44px toolbar everywhere they appear (#498, Discussion #295). The Dashboard and Containers views no longer overflow horizontally on narrow phone screens. Update-status vocabulary was overhauled ("Digest update", "Security hold", neutral Major/Minor/Patch badges for informational-only updates), pinned reverted to being a tag property rather than an update status, and the maturity panel now shows exactly one countdown clock instead of two that could disagree with the gate (#556). Table polish across the series fixed pinned-column overlay clipping, a registry error overwriting the visible tag, sticky-column/auto-hide width miscalculation, and a clipped icon column.
  • Trigger taxonomy migration reaches its final warning stage. Every DD_TRIGGER_* variable and the deprecated dd.trigger.include/dd.trigger.exclude labels still work in v1.6 but now log at error level ahead of removal in v1.7; use DD_ACTION_*/DD_NOTIFICATION_* and category-scoped labels, or run config migrate --source trigger.
  • Less duplicate work on the hot paths. Registry tag-list requests are shared across containers within a poll instead of being repeated per container; scheduled security scans and the shared log viewer avoid redundant store writes and re-renders; and transient registry network errors (timeouts, connection resets, DNS blips) now retry with backoff before a watch error is recorded.
  • Base image bumped from Alpine 3.21 to 3.24, later rebased onto node:24-alpine at Node 24.19.0 with a matching Trivy build-stage bump to 0.73.0, closing several HIGH/MEDIUM CVEs in the underlying image (#682).

Deprecated

  • GET /api/auth/methods and the legacy GET /auth/strategies response shape are deprecated in v1.6.0 (removal in v1.7.0 and v1.8.0 respectively). Both now log on every request and return Deprecation/Sunset headers pointing callers at canonical GET /api/v1/auth/status. See DEPRECATIONS.md.

Removed

  • Unversioned /api/* and WS /api/log/stream aliases are gone, returning 410 Gone (or a rejected upgrade) instead of serving the request. This is a breaking change for integrations that hardcode the unversioned base path, notably the Home Assistant wud-card integration and Homepage's whatsupdocker widget — use the DD_COMPAT_WUDCARD shim above, or migrate to /api/v1/* directly.
  • Legacy v1.4-era authentication compatibility. Basic auth now accepts only argon2id hashes ({SHA}, APR1/MD5, crypt, and plain-text hashes fail validation), and OIDC discovery now requires https:// with no insecure HTTP workaround.
  • Legacy WUD configuration aliases, obsolete watcher switches, and legacy trigger-template variables. WUD_* environment variables and wud.* labels are ignored (the migration CLI still recognizes them so existing files can be rewritten); DD_WATCHER_<name>_WATCHDIGEST/WATCHATSTART are no longer configuration keys (use dd.watch.digest=true; startup watches always run); and $id/$name/$watcher/$kind/$semver/$local/$remote/$link/$count no longer populate in trigger templates.
  • Kafka clientId and token-only public-registry compatibility handling. Kafka validation accepts only lowercase clientid, and malformed public-registry configurations (a bare PUBLIC_TOKEN with no PUBLIC_LOGIN) now fail closed instead of silently falling back to anonymous pulls.

Fixed

  • **The maturity soak clock, update-poli...
Read more

v1.6.0-rc.13

v1.6.0-rc.13 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 09 Aug 04:17
103bfdc

v1.6.0-rc.13

Full Changelog: v1.6.0-rc.12...v1.6.0-rc.13

[1.6.0-rc.13] — 2026-08-08

Security

Fixed

  • Icon bundle no longer silently drops referenced icons at image build time (#683). The bundle is regenerated from the locked @iconify-json packages during every Docker image build, but the extractor only looked up plain icon entries — lucide:history (the Audit navigation icon in the Lucide icon theme) became an alias in lucide 1.2.121 and vanished from shipped images, rendering blank. The extractor now resolves alias chains, and references that never existed in the locked collections are fixed: iconoir:historyiconoir:clock-rotate-right, iconoir:gitlabiconoir:gitlab-full, iconoir:stackiconoir:multiple-pages, and the Font Awesome brand glyphs (GitHub/GitLab/Google/Microsoft registry icons) gained the previously missing @iconify-json/fa6-brands package. A new test asserts every icon referenced in icons.ts exists in the committed bundle.
  • Star History chart is now self-hosted (#671). The homepage card and README embed rendered a broken image after api.star-history.com's global outage (their GitHub tokens rate-limited; starchart.cc also failing). A new /api/star-history route on the website fetches stargazer timestamps from the GitHub API server-side (optional GITHUB_TOKEN, edge-cached six hours with stale-while-revalidate, short-lived fallback SVG on fetch failure) and renders the chart in the site's own palette for both themes; the README uses a <picture> element with theme-matched variants. No third-party chart service remains in the path, and api.star-history.com is dropped from the site's CSP img-src.
  • Digest-update comparison no longer anchors on an arbitrary RepoDigests[0] entry (#669). A local Docker image can carry multiple repo@digest entries for one Image ID (pull/retag accumulation, no ordering guarantee); getRepoDigest blindly took index 0, so a stale or foreign-repo entry landing first anchored the whole digest-update pipeline to the wrong manifest and produced a persistent digest-update false positive that survived applying the update. getOrderedRepoDigests (app/watchers/providers/docker/docker-helpers.ts) now returns every RepoDigests entry whose repo component matches the container's own image reference, ordered, falling back to the full list only when nothing matches; the container model gained an optional image.digest.repoDigests field carrying that ordered list, re-derived from the live Docker image inspect on every discovery/refresh cycle. handleDigestWatch (app/watchers/providers/docker/image-comparison.ts) now walks that candidate list — a cheap raw-value check first, then a normalize-and-compare registry call per remaining candidate, skipping anchors whose manifest lookup fails — and re-anchors digest.repo to whichever candidate actually matched, so a store already poisoned with a stale digest.repo self-heals on its own. A genuine same-tag republish (no candidate matches) still flags an update exactly as before; if every candidate fails to normalize, the failure now propagates instead of being silently coerced into a false "no update".

v1.6.0-rc.12

v1.6.0-rc.12 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 05 Aug 00:46
4c2c7ed

v1.6.0-rc.12

Full Changelog: v1.6.0-rc.11...v1.6.0-rc.12

[1.6.0-rc.12] — 2026-08-04

Changed

  • Routine dependency maintenance across the root tooling, demo, UI, and website workspaces (#653, #654, #655, #656). The dependency-version guard's Next.js check became a 16.x floor instead of an exact pin so routine patch bumps stop tripping it (#664).
  • Crowdin translation sync (#665): one French container-component string corrected.

Fixed

  • "Agent Mismatch" no longer appears in the container list/SSE display during the brief window an agent's docker/dockercompose trigger is still (re)registering (#605). Eligibility is recomputed live on every read, and AgentClient._doHandshake() deregisters the agent's components before awaiting the /api/triggers fetch and re-register. A read in that window found zero triggers for the agent and computeUpdateEligibility raised a hard agent-mismatch blocker, disabling the Update button, even though nothing was actually misconfigured — the condition self-corrected once registration finished. agent-mismatch now downgrades to a soft blocker (button stays enabled) on display surfaces whenever the container's own agent is mid-registration, per the new AgentClient.isRegisteringComponents flag (true only for the deregister→re-register span, not the whole reconnect backoff). Update admission (app/updates/request-update.ts) is unaffected and stays hard/fail-closed throughout, so an update can never be enqueued through a wrong-agent trigger during that window.
  • WebSocket log streams no longer reject anonymous-auth sessions (#636). Both WS upgrade paths — the system log stream and the container log stream — gated on isAuthenticatedSession() requiring session.passport.user, which passport-anonymous never sets, so under DD_ANONYMOUS_AUTH_CONFIRM=true the log stream WebSocket always rejected the upgrade even though every REST endpoint worked. isAuthenticatedSession now also accepts the session when anonymous authentication is the registered mode.
  • Maturity clock: swallowed auth errors surfaced, per-container threshold respected (#604). getImagePublishedAt failures — including GHCR/LSCR 401/403 auth errors — now log at warn instead of debug, so the maturity gate's silent fallback from the registry publishedAt to updateDetectedAt is no longer invisible. getRawUpdateMaturityLevel (app/model/container.ts) and getContainerMaturityLevel (app/api/container/maturity-filter.ts) now resolve each container's own updatePolicy.maturityMinAgeDays before falling back to the global DD_UI_MATURITY_THRESHOLD_DAYS, matching the gate's own isUpdateSuppressed/isMaturityGatePending logic so the hot/mature badge can no longer disagree with the gate in the same API response.
  • Container start/stop/restart/rollback return an explicit 501 instead of an ambiguous 404 for agent containers without lifecycle transport (#637). POST /:id/start|stop|restart and POST /:id/rollback returned a bare 404 No docker trigger found for this container whenever the lookup missed, indistinguishable from "container not found" — for agent-owned containers this was the only signal the UI got. That lookup miss now returns 501 naming the likely cause (the agent's connection typically hasn't advertised usesControllerDockerTransport) when container.agent is set; non-agent containers still get the existing 404. This complements the native-transport support that shipped in rc.11 via #651, which closed #637's core gap — this is the remaining explicit-error half.

Security

v1.6.0-rc.11

v1.6.0-rc.11 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 01 Aug 20:22
67f2341

v1.6.0-rc.11

Full Changelog: v1.6.0-rc.10...v1.6.0-rc.11

[1.6.0-rc.11] — 2026-08-01

Added

  • Portwing controller-owned Docker watcher, update, and lifecycle transport (#632, #637, Portwing #76). Drydock 1.6.0-rc.11+ recognizes Portwing 0.9.0's exact Docker watcher marker (transport=docker-api, execution=controller, events=portwing) and runs its native registry checks plus single/batch Docker updates controller-side. Container start, stop, restart, update preview, and backup rollback actions use that same native Docker capability surface instead of failing because Portwing intentionally advertises no remote trigger. A loopback-only bearer-authenticated bridge carries Docker API calls through Portwing over Standard HTTP or Edge correlated request/response/stream messages, while Portwing remains the lifecycle-event source. Later raw Portwing inventory (updateAvailable=false, updateKind=unknown) preserves rather than erases the controller-enriched update state.

Changed

  • Standard Portwing Ed25519 requests now use signature version 2. The controller sends five authentication headers, including X-Portwing-Signature-Version: 2, and signs the exact origin-form request target (escaped path plus the unmodified raw query) instead of a decoded path without its query.

v1.6.0-rc.10

v1.6.0-rc.10 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 31 Jul 21:44
b9d9873

v1.6.0-rc.10

Full Changelog: v1.6.0-rc.9...v1.6.0-rc.10

[1.6.0-rc.10] — 2026-07-31

Added

  • Startup warning for minute-precise maintenance-window crons. DD_WATCHER_{name}_MAINTENANCE_WINDOW is matched minute-by-minute, so a fixed minute field like 0 2-6 * * * only opens the window for one minute per matching hour instead of the whole hour range — a common copy-paste trap reported in Discussion #639. The Docker watcher now logs a one-time warning at init when the configured window's minute field doesn't contain *, pointing at the fix (* 2-3 * * *). Step values like */5 are intentional and are not flagged.
  • "Learn more" link in the update confirm dialog for policy-blocked overrides. When overriding a soft-blocked update, the confirm dialog now links to the update-eligibility reasons reference, matching the link already shown in the Update Status panel. (Discussion #639)

Changed

  • Translations resynced from Crowdin (#620), refreshing the container-component and list-view catalogs across all 15 non-English locales, plus a fuller French pass over the agents, app-shell, common, and config catalogs.
  • Routine dependency maintenance across the app and UI workspaces (#614, #615, #617, #646): undici 8.9.0 (app) and the 7.29.0 UI override, express-rate-limit 8.6.1, @aws-sdk/client-ecr 3.1096.0, vue-i18n 11.4.8, knip 6.29.0, postcss 8.5.24, and the fast-uri security override advanced from 3.1.4 to 4.1.1 (both retain the CVE-2026-16221 fix; the dependency-version guard now rejects the still-vulnerable 4.0.0–4.1.0 range). Renovate no longer proposes @babel/core majors while Stryker's instrumenter requires Babel 7.

Fixed

  • Unchanged update-available audit entries are no longer re-recorded on a timer. Previously an unchanged pending update was re-written to the audit log every time the 1-hour dedupe window lapsed, bloating the audit log for fleets with long-pending updates (a fleet with 10 persistent pending updates wrote 140 rows in 24h). Audit rows are now recorded only on first detection or when the update target/kind changes. The RC-only DD_AUDIT_UPDATE_AVAILABLE_DEDUPE_MS variable is removed.
  • Maintenance-window documentation corrected and made findable (Discussion #639). Every documented example used a minute-precise cron (0 2-6 * * *) while describing it as an hourly range ("2am–6am daily"). MAINTENANCE_WINDOW is matched minute by minute, so that expression opens the window for one minute at the top of each listed hour and reports "closed" the rest of the day. Examples now use * 2-6 * * *, with a table of correct forms in Watchers. The update confirm dialog's own wording ("This update is currently policy-blocked", "Outside maintenance window — auto update deferred until the window opens", "Update anyway") appeared nowhere in the docs, so searching for the on-screen message returned nothing; Update Eligibility & Blockers now documents that dialog verbatim and states plainly that a soft blocker never gates a manual update.
  • Infrastructure-mode self-updates no longer fail to spawn the helper container on Docker Hub installs (#644). For containers labeled dd.update.mode=infrastructure, the self-update helper is spawned from Drydock's own image, but the reference was rebuilt as registry-1.docker.io/codeswhat/drydock:<tag> — the Docker daemon stores Hub images under their short name (codeswhat/drydock:<tag>), the helper image is never pulled since it must already exist locally, and container creation failed with 404 No such image, rolling the update back. The helper image reference is now resolved through the registry provider's getImageFullName normalization (the Hub provider strips registry-1.docker.io/, docker.io/, library/) so it matches the daemon-local image name; the previous raw construction remains only as a fallback when no registry manager resolves.

v1.6.0-rc.9

v1.6.0-rc.9 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 28 Jul 23:32
9cc3010

v1.6.0-rc.9

Full Changelog: v1.6.0-rc.8...v1.6.0-rc.9

[1.6.0-rc.9] — 2026-07-28

Added

  • Continuous Portwing edge log streams. The authenticated container-log WebSocket now bridges correlated dd:container_log_chunk, dd:container_log_end, and dd:container_log_error frames, cancels the agent stream when the viewer closes, preserves stdout/stderr and timestamp decoding, and caps each downstream viewer at 1 MiB of buffered data. Older Portwing agents degrade to their one-shot response without breaking the viewer.
  • Real Portwing fleet-soak workflow. Pull requests and the weekly quality schedule run actual signed Portwing processes against Drydock's production edge gateway through concurrent exec, sustained logs, forced controller backpressure, and reconnect storms. Machine-readable evidence records RSS/heap budgets and is retained for 90 days.

Changed

  • The portwing/1.0 edge endpoint is enabled by default. DD_EXPERIMENTAL_PORTWING=false remains as an emergency disable for new edge connections. OpenAPI, operator docs, and translated feature summaries now describe the stable/default-on behavior.

Fixed

  • The row "Updating/Queued/Scanning" overlay chip is no longer clipped at the pinned-column edge. The table's pinned identity-cluster cells (sticky z-10) painted over the full-width row overlay hosted in the first cell and cut the centered chip in half at the cluster boundary. While an overlay is active the host cell now sits above its z-10 row siblings (and still below the sticky header). (#631)
  • Registry errors no longer replace the container's tag in the tag column. A rate-limit/auth/not-found registry error rendered a danger pill instead of the current tag (table mode) or appended one (card mode), duplicating the warning glyph + tooltip the registry column already shows. The tag cell now always shows the actual tag; the registry column glyph remains the single error flag. (#631)

v1.6.0-rc.8

v1.6.0-rc.8 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 28 Jul 18:12
bcbbf71

v1.6.0-rc.8

Full Changelog: v1.6.0-rc.7...v1.6.0-rc.8

[1.6.0-rc.8] — 2026-07-28

Fixed

  • Auto-update no longer stops when the update-available notification rule is scoped to specific channels (#623). Action triggers (docker, dockercompose, command) were gated by the same trigger allow-list on the update-available notification rule that routes messages to notification channels — but action-trigger ids are deliberately barred from that list by the API validator, the UI picker, and the documented rule model, so the moment any notification trigger was assigned to the rule, every action trigger (local and agent-hosted alike) silently failed the membership check with excluded-from-allow-list and auto-update stopped fleet-wide, with only a debug log as evidence. Action-category triggers are now exempt from the allow-list membership check in getUpdateAvailableAutoTriggerDispatchDecision (app/triggers/providers/Trigger.ts), mirroring the exemption the lifecycle-notification path has always had; disabling the rule itself still acts as the global kill switch. Present since the rule allow-list landed in v1.6.0-rc.1.
  • Controller-set update policy overrides no longer vanish from agent-managed containers (#565). Remote agents resolve their own declarative (env/label) policy but never learn controller-side runtime overrides, so every container report they send carries an explicit empty override layer. The controller persisted that layer verbatim, and updateContainer (app/store/container.ts) treated any present updatePolicyOverrides key as authoritative — clearing maturity mode/min-age days, skip lists, and snoozes on every periodic agent sync or manual recheck. This was the settings-deletion mechanism behind #565, distinct from the soak-clock resets fixed in #568 and rc.7. The controller now reapplies its stored overrides when ingesting agent reports, and the store itself encodes the rule the recreate path has had since #497: an empty incoming override layer carries no controller intent and only clears stored overrides when the update-policy PATCH handler marks the write as authoritative, so deliberate clears from the UI still stick.