Skip to content

Releases: CodesWhat/drydock

v1.5.0-rc.15

27 Apr 20:22
fdb285d

Choose a tag to compare

v1.5.0-rc.15 Pre-release
Pre-release

v1.5.0-rc.15

[1.5.0-rc.15] — 2026-04-27

Fixed

  • #308 — Per-row scanning chip wasn't anchored to the container being scanned (begunfx, rc.14). Backend was already broadcasting dd:scan-started / dd:scan-completed with { containerId, status } payloads, but the UI's SSE service was dropping the payload on the floor — both listeners emitted bare bus events with no container reference. The per-row "Scanning" chip was therefore driven entirely by the optimistic local actionInProgress map populated when the user clicked Scan, which (a) couldn't reflect cron-driven scheduled scans and (b) was tied to the HTTP request lifecycle instead of the actual scan lifecycle. Threaded the containerId through the SSE service → bus → AppLayout dd:sse-scan-started / dd:sse-scan-completed CustomEvent payloads, added a singleton useScanLifecycle composable that maintains a scansInFlight set keyed by container id (with a 120s safety timeout), and refactored scanContainer so the per-row chip is set on click and cleared by the SSE completion event (or on HTTP failure). The chip now stays correctly anchored to the row whose container is actually being scanned, regardless of whether the scan was started by a click or by the scheduler.
  • #308 — Scanning chip escaped its row and floated in viewport-fixed gutter space (begunfx, rc.14). The icon-column overlay chip is position: absolute; inset: 0, which requires a positioned ancestor on the <tr> to stay pinned to the row. The repo already documents this in style.css and applies the transform: translateZ(0) containing-block hack via the dd-row-updating class — but rc.13 deliberately decoupled scan from the lock state (scan must keep the row interactive), which removed the containing-block hack from scanning rows as a side-effect. Without a positioned ancestor the chip escaped up the layout tree and rendered at a fixed viewport position, appearing in the gutter between unrelated rows or section headers and persisting across scrolls until the scan completed. Added a sibling dd-row-scanning class (containing block only — no opacity dimming, no pointer-events-none) and applied it from tableRowClass whenever a row is scanning but not locked. Locked-state still wins when a row is somehow both updating and scanning.
  • #317 — Lifecycle notifications silenced by auto: false (begunfx, rc.14). A notification trigger configured with auto: false (a common Pushover setup to suppress every-detection update-available spam) was also silently losing every other lifecycle notification — update-applied, update-failed, security-alert, agent-connected, agent-disconnected. The init code wrapped all event registrations in a single if (auto !== 'none') block. Decoupled them: auto-fire-on-detection handlers stay gated by auto, lifecycle handlers register unconditionally. A user who's configured the trigger at all now gets completion / failure / security / agent notifications regardless of how auto is set.
  • #317 — Lifecycle notification rules silently dropped triggers without an explicit allow-list. update-applied / update-failed / security-alert / agent-connect / agent-reconnect dispatched with allowAllWhenNoTriggers: false while update-available used true. The asymmetry meant a notification rule with no per-rule allow-list silently disabled lifecycle notifications even though update-available worked. Flipped the four lifecycle dispatch sites to match: empty / missing allow-lists now permit dispatch. Explicit per-rule allow-lists still win when populated.
  • #317 — Update button bypassed eligibility blockers, queuing requests the API would only reject one-by-one (s-b-e-n-s-o-n, rc.14). The Update button (per-row + Update-all) only gated on the legacy bouncer === 'blocked' (security scan), even though rc.13 added 11 other eligibility blocker reasons surfaced as row pills. Clicking Update on a row pill-marked AGENT MISMATCH produced the toast No docker trigger found for this container; clicking Update-all on a stack of TRIGGER FILTERED proxies queued every row before the API rejected each one individually. Added a severity: 'hard' | 'soft' field to UpdateBlocker and made update-eligibility the single source of truth: the API rejects manual updates on any hard blocker with the blocker's user-friendly message; the UI locks the Update button on hard blockers and shows a confirm-modal warning on soft blockers ("This update is currently policy-blocked: … Click Update anyway to override.").

Changed

  • security-scan-blocked now also fires when the current container's scan status is blocked. Previously the eligibility model only inspected security.updateScan (the candidate) while request-update.ts independently inspected security.scan (the running image). Both gates are now unified under the eligibility blocker — either being blocked halts a manual update with the same 409 + "Security scan is blocking this update" message. Use the existing force-update path to override.

Deprecated

  • dd.action.include / dd.action.exclude (and legacy dd.trigger.include / dd.trigger.exclude) become hard manual-update blockers in v1.7.0. v1.5.x keeps them as soft blockers — the pill reads Trigger filtered / Trigger excluded but the manual Update button stays clickable (with a warn-and-confirm). v1.7.0 will lock the button and reject the API call when the labels filter out the matching trigger. See DEPRECATIONS.md for migration guidance.

v1.5.0-rc.14

26 Apr 17:52
83a5043

Choose a tag to compare

v1.5.0-rc.14 Pre-release
Pre-release

v1.5.0-rc.14

[1.5.0-rc.14] — 2026-04-26

Fixed

  • Update-eligibility pill never rendered (rc.13 regression). The blocker pills shipped in rc.13 (Trigger filtered, Below threshold, Maturing, etc.) silently rendered nothing for every container in every view (table rows, side panel, full-page detail, dashboard widget). The backend computed eligibility correctly and serialised it in the API response, but the UI's mapApiContainer() didn't declare or pass through the updateEligibility field — TypeScript's structural typing dropped the unknown JSON property at the boundary. Fix is two-part:
    • UI mapperApiContainerInput now declares updateEligibility, and mapApiContainer passes it through with a deriveUpdateEligibility validator that rejects malformed eligibility objects (missing eligible boolean, missing evaluatedAt) and filters out malformed individual blockers (bad reason strings, missing message). One change lights up every consumer (list, side panel, full-page, dashboard).
    • Backend SSE enrichment — the store broadcasts raw container objects on dd:container-added / dd:container-updated, but updateEligibility is computed on-demand in the list handler and never persisted. Without enrichment, live SSE patches would deliver containers with eligibility undefined, flickering the pill on every update. New sse-container-enrichment.ts helper computes eligibility from registry triggers + active operations and attaches it to each lifecycle payload before broadcast.
    • Reported in #307.
  • #323 — Popovers on the Containers list rendered off-screen for the last row + drifted on scroll. The "More" actions menu and the column picker both used position: fixed with coordinates captured from the trigger's getBoundingClientRect() and unconditionally opened below the trigger. When the trigger sat near the bottom of the viewport (e.g. the last container row), the menu was clipped off-screen and inaccessible. The popovers also stayed at their captured pixel coordinates while the table scrolled, drifting away from the trigger. Added a small buildPopoverStyle helper that measures available space below vs above and flips the popover upward when there's not enough room below; added a global scroll listener (capture phase, to catch internal scroll containers) that closes both popovers since their fixed coordinates can't track a moving anchor. Sibling popovers in this codebase (NotificationBell, etc.) intentionally untouched here — different UX surfaces, separate consideration.

Tests / CI

  • Eligibility coverage closed end-to-end. 13 new mapper unit tests (full eligibility roundtrip including actionHint / liftableAt / details, all 13 valid UpdateBlockerReason values, malformed-blocker filtering, missing-field rejection, non-array blockers handled as empty list). 7 new SSE enrichment tests (happy path, malformed payloads, error resilience). The 55 existing UpdateEligibilityBadges component tests already covered the render gate. Manual verification: built drydock:dev with the fix, ran with DD_ACTION_DOCKER_LOCAL_AUTO=oninclude, confirmed 23 backend trigger-not-included blockers render as 23 amber "Trigger filtered" pills in the Kind column.
  • Popover positioning coverage. 6 new tests in ContainersView.spec.ts: actions menu and column picker each flip up when the trigger is near viewport bottom; global scroll closes the actions menu, closes the column picker, and is a no-op when nothing is open; scroll listener is removed on unmount. Manual verification: built drydock:dev with the fix, scrolled the last container row to the viewport bottom, opened its More menu — the menu rendered fully within the viewport (anchored bottom: 48px from viewport top) instead of clipping off-screen.

v1.5.0-rc.13

26 Apr 04:03
3b93de8

Choose a tag to compare

v1.5.0-rc.13 Pre-release
Pre-release

v1.5.0-rc.13

[1.5.0-rc.13] — 2026-04-24

Added

  • Update-eligibility blockers on container rows — Backend surfaces 12 structured blocker reasons (maturity-not-reached, container-paused, no-update-detected, etc.) per container, and the Containers list renders them inline so users see why a row isn't updating without opening the detail drawer. Amber for maturity-not-reached (informational — will self-clear at the maturity threshold), red for terminal blockers.
  • GET /update-operations/:id endpoint — Returns the current state of a specific update operation. The UI falls back to this endpoint when the terminal SSE is missed (reverse-proxy reconnect-without-replay), so update toasts and hold release still fire even when the browser never saw the final server-sent event.

Fixed

  • #308 (partial) — Two of the three reporter symptoms are resolved:
    • Row status during scan. Triggering a scan from the row's ... menu no longer shows "Updating" on the row. actionInProgress now tracks the kind of action per container (update / scan / lifecycle / delete) instead of a bare id set, and the status label and row-lock predicates read from the per-kind map so a scan in progress shows "Scanning" and doesn't dim other row actions.
    • Batch-mode triggers on single-container scans. Providers that only listen to emitContainerReports (batch-mode, e.g. Pushover) were silently skipped on single-container scan paths, which only emitted the emitContainerReport singleton. Threaded { emitBatchEvent: true } through the watchContainer call from the single-scan handler so batch-mode triggers fire too.
    • Still open: the empty updateKind.{kind,localValue,remoteValue} fields in simple-template notification email bodies reported by begunfx. The dispatch snapshot shouldn't normally lose updateKind — needs a repro to chase the root cause.
  • #317 — Two fixes around reconnect-without-replay SSE:
    • Rollback actions no longer broadcast spurious container-lifecycle events (dd:sse-container-added/-removed in quick succession) while the store is mid-recreate.
    • When the terminal update-operation SSE is missed, the Containers view's reconciliation pass now releases the display hold and fires the update toast from the reconciled state, matching what would've happened on a live SSE.
  • #318 — Kind and status columns in the Containers list now stay visible at narrow viewports. The container-query thresholds that gated column visibility were tripping too early; adjusted so the columns survive down to the minimum useful table width.
  • #291 (rc.12 follow-up) — Dashboard update flow now shares the same useOperationDisplayHold composable that the Containers view uses, fixing the last two reporter symptoms that survived rc.12 on the dashboard path. (1) The updating row no longer drops to the bottom of the Recent Updates widget mid-update: the backend transiently clears updateDetectedAt while the container recreates, which was sorting the ghost row last — the shared hold now overlays the snapshotted updateDetectedAt so sort position is stable through the whole update window. (2) A dropped terminal SSE (Synology DSM reverse-proxy style reconnect-without-replay) no longer leaves the dashboard silent: the view now reconciles holds against refreshed container data and falls through to GET /update-operations/:id the same way the Containers view did. Both views are now driven by one hold map, one sort-snapshot overlay, and one reconciliation fallback.

Performance

  • #301 (rc.11 follow-up) — Dashboard and Containers view now do dramatically less work on every SSE reconnect. Addresses the residual slow-load reports on those two pages after the rc.10/rc.11 backend fixes.
    • Dashboard reconnect refresh is live-only. On dd:sse-connected the dashboard now refetches only the endpoints that can go stale between frames (/containers, /containers/stats, /containers/recent-status) and TTL-guards the static ones (/server, /agents, /watchers, /registries) for 30s. dd:sse-resync-required (server-signaled state loss) still forces a full 7-endpoint fan-out — the TTL skip only applies to reconnect blips. On a flaky Synology LAN this turns a reconnect storm into a handful of requests instead of 7× that.
    • Dashboard stats read no longer warms Docker stats streams per container. GET /api/v1/containers/stats accepts a new ?touch=false query param; the dashboard uses it so a summary read returns already-cached snapshots without spinning up a per-container Docker stats stream. The Containers view / detail panel still pass the default (touch=true) so streaming stats stay warm where they actually render.
    • Containers list dedup fingerprint is ~30× cheaper. loadContainers() was recursively fingerprinting every field (including details.ports/volumes/env/labels arrays) on both the incoming and current lists to skip redundant reactive reassignment. Replaced with a flat hash of the ~13 scalar fields that actually affect row rendering. Identity-and-tag changes still trigger reassignment; deep-field-only changes that never reach the list render no longer do.
    • SSE lookup-map churn halved. updateLookupMapsForContainer was doing 4 full-map spreads ({ ...map, ...key }) per container SSE event; collapsed to 2. removeLookupMapsForContainer collapsed the same way and skips reassignment entirely when neither the id nor the alias is present.

Docs

  • v1.5.0 deprecation sweep. Migrated every documentation example and test fixture off the v1.5.0-deprecated DD_TRIGGER_* / dd.trigger.* prefixes onto canonical DD_NOTIFICATION_* + dd.notification.* (messaging providers) and DD_ACTION_* + dd.action.* (update executors). Touched 29 files in content/docs/current/**, the in-repo README roadmap and Recent Updates sections, CONTRIBUTING, all QA/CI/demo compose fixtures (except migration-test-compose.yml, which intentionally exercises the legacy prefix), and the apps/web landing-page roadmap. The legacy prefixes still work as aliases through v1.7.0 — this sweep makes every example in the project canonical so new users stop copy-pasting deprecated forms. See DEPRECATIONS.md.
  • Registry credentials: *_TOKEN*_PASSWORD. The Docker Hub and DHI provider docs no longer instruct users to set DD_REGISTRY_HUB_PUBLIC_TOKEN (deprecated) — examples now use DD_REGISTRY_HUB_PUBLIC_PASSWORD, matching the canonical form every other registry provider already documents.

Tests / CI

  • Dashboard helper + computed coverage — Added targeted tests for createRealtimeRefreshScheduler's full-liverefreshFull fallback branch (when no full-live handler is configured) and for the WeakMap cache-hit branch of useDashboardComputed's name-counts memoization. Closes the last two UI coverage gaps left after rc.12.

v1.5.0-rc.12

22 Apr 23:44
a00798b

Choose a tag to compare

v1.5.0-rc.12 Pre-release
Pre-release

v1.5.0-rc.12

[1.5.0-rc.12] — 2026-04-22

Fixed

  • #315 — Self-update now works against private registries whose registry.url is stored as the v2 API base (e.g. https://ghcr.io/v2). resolveHelperImage in the Docker action trigger was building the helper image reference by concatenating registry.url verbatim with name:tag, producing https://ghcr.io/v2/codeswhat/drydock:1.5.0 — which Docker's POST /containers/create rejects with HTTP 400. The fix normalizes the reference to match Registry.getImageFullName (scheme and /v2 stripped) so the self-update helper spawn uses the same image shape as the pull path. No-scheme / no-v2 registries fall back to name:tag.
  • #309 — Status column in the Containers list now shows its label alongside the icon at typical widths. The column was width-capped at 90px which fell below the shared dd-cell-show-80 container-query threshold (80px) once the 10px padding was subtracted — widened to 120px so the label renders whenever the column is visible.
  • #291 (rc.11 follow-up) — Terminal update toasts (Updated: <name> / Update failed: <name> / Rolled back: <name>) now fire after the operation display hold ends instead of on the raw terminal SSE. The container row stays visually in "updating" for another 1.5s (OPERATION_DISPLAY_HOLD_MS) after the terminal event so it settles without flicker, but the toast was announcing the outcome before the UI reflected it. Wrapped the three terminal toast calls in setTimeout(..., OPERATION_DISPLAY_HOLD_MS) in both ContainersView.applyOperationPatch and DashboardView.handleTerminalOperationSse so the row releases first and the toast lands right after.
  • Dashboard fly-in animation on update actions — Three fetchDashboardData() calls in DashboardView (single-update onAccepted, onStale, and the update-all finalizer) were refreshing without { background: true }, which flipped state.loading to true and caused the <GridLayout v-if="!loading"> to unmount and remount — re-triggering grid-layout-plus's initial positioning transitions (the "fly in from the left" rebuild reporters saw after every update). Switched those three calls to the background variant so the grid stays mounted and only the underlying widget data refreshes in place.
  • Pre-push e2e hook timeoutscripts/run-e2e-tests.sh unconditionally restarted Colima before every run, which scaled badly once the dev host accumulated QA fixtures (VM cold-boot with 45+ attached containers exceeded the lefthook 10m timeout before cucumber started). Default DD_E2E_RESTART_COLIMA to auto — skip the restart when docker info already succeeds, and only force one when the engine is actually wedged. Set to true / false to override.

Security

  • fast-xml-parser override 5.5.8 → 5.7.1 — Addresses GHSA-gh4j-gqv2-49f6 / CVE-2026-41650 (XML comment/CDATA injection via unescaped delimiters in XMLBuilder, medium). Vulnerable range ≤ 5.5.12, patched in 5.7.0; bumped both app/ and e2e/ workspace overrides to 5.7.1 (latest).
  • uuid 13.0.0 → 14.0.0 — Addresses GHSA-w5hq-g745-h8pq (missing buffer bounds check in v3/v5/v6 when buf is provided, medium). Vulnerable range ≤ 13.0.0, patched in 14.0.0. Drydock only uses v4 (unaffected by the buffer path) but the scanner flags any vulnerable version in the tree. Bumped the app's direct dep and added uuid: 14.0.0 to both app/ and e2e/ overrides so transitive callers (dockerode, artillery, @azure/msal-node, @ngneat/falso) also resolve the patched version.

Performance

  • Granular SSE container patch — Container list updates triggered by dd:sse-container-added / -updated / -removed now patch the local containers array in place instead of refetching the full list over HTTP. Preserves row identity via a prototype-chain merge so Vue's reactivity system reuses the existing row DOM nodes, eliminating the post-SSE flicker and the polling refetch that the previous debounced-reload path required.
  • shallowRef for heldOperations map — The operation display-hold store swapped from a deep ref(Map) to shallowRef(Map) plus explicit triggerRef on mutation. Avoids Vue walking the entire map on every held-row render in large inventories.

Changed

  • Expand all / Collapse all bulk toggle — Replaced the single chevron toggle in the Containers toolbar with an explicit "Expand all" / "Collapse all" button whose label reflects the current global expand state. Individual stack headers still toggle their own group; this is the bulk shortcut on top.
  • Compact "Suggested" badge — The "Suggested" tag badge on container rows now renders compact (the full suggested tag moves into a tooltip on hover) so the row metadata band stays readable at narrower column widths.

Tests / CI

  • Thin test hardeningapp/agent/index.test.ts grew 5 → 11 tests. Existing coverage verified call counts but not argument correctness, same-instance registration, fire-and-forget client.init() behavior, skip-branch warning messages, or mixed valid/invalid registry state. Added behavioral assertions for each while keeping 100% coverage.
  • SSE / pending-poll / hold-release edge coverage — New tests for useOperationDisplayHold, useContainerActions.pollPendingActionsState, and applyDashboardContainerPatch close the remaining uncovered branches (delete-no-op, in-flight poll guard, removed-kind patches, Object.assign merge, push-new, mapApiContainer throw, container-removed SSE listener).
  • QA compose fixture trims — Shrank test-fixture images so update flows finish in seconds instead of minutes, and swapped timescaledb-ha → non-HA to unblock scan tests that were failing against the HA variant's readiness probe.
  • Release pipeline fragility fixes — Three recurring CI failures in the release cut path addressed (see a1ea1a82 on fix/ci-release-fragility, renamed to feature/v1.5-rc12).

v1.5.0-rc.11

21 Apr 21:34
3ebcae9

Choose a tag to compare

v1.5.0-rc.11 Pre-release
Pre-release

node:unset:2: no such hash table element: node

[1.5.0-rc.11] — 2026-04-21

Added

  • #299Inline update action in Security view. Image rows in the Security view now show an "Update" action button directly next to the vulnerability data when a newer image is available, instead of only offering navigation away to the Containers view. Single-container images open a confirmation dialog in place; multi-container images open a compact chooser popover to pick which instance to update. A secondary "View in Containers" link remains for cases where the user wants to inspect the full container state first. The ContainerUpdateDialog component is extracted as a standalone reusable piece. The Containers view now accepts a ?containerIds=<csv> query parameter to pre-filter to a specific set of containers, with a dismissable filter chip in the toolbar.
  • SSE Last-Event-ID replay (#289) — The server now stamps every broadcast event with a monotonic <bootId>:<counter> id and retains a 5-minute time-bounded ring buffer. Clients reconnecting with a Last-Event-ID header receive every event they missed; if the buffer has evicted the requested id (or the server booted since), the client receives a dd:resync-required event and the UI refetches view state. Replaces the previous best-effort reconnect that could leave container rows stuck in an "updating" state when a terminal SSE was dropped during a watcher-scan window.
  • Dashboard recent-updates row navigation (#311 adjacent) — Clicking a row in the dashboard "Updates Available" widget now navigates to /containers?containerIds=<id>, focusing the Containers view on that single container. Interactive controls inside the row (Update button, Release notes link) retain their own behavior via @click.stop.
  • Expand/Collapse all stacks toggle (Discussion #311) — A single toggle button appears next to the Group-by-Stack icon in the Containers toolbar when Stack view is on. When every stack is collapsed the icon points down and clicking expands all; otherwise it points up and clicking collapses every stack. Individual stack headers still toggle their own group as before — this is purely a bulk shortcut on top of that.

Fixed

  • #305 — Hide Pinned now hides every pinned container again, matching rc.8 behavior and the reporter's expectation when combining Hide Pinned with Has Update. #293 had carved out an exception for pinned rows with a pending update, but that conflated "declutter" with "surface actionable pins" and broke the filter for users who pin infrastructure containers (databases, edge, etc.) and want Hide Pinned to simply remove every pinned row from the list. The pin-to-wait-out-a-regression scenario from #293 is now addressed by simply unchecking Hide Pinned — predictable filter semantics over clever cross-filter logic.
  • #296 (follow-up) — When docker-socket-proxy blocks GET /info (the default — INFO=0), drydock now logs an actionable warning naming the watcher, the error, and the fix (set INFO=1 on your docker-socket-proxy config, or set DD_SERVER_NAME to override) instead of silently falling back to the container short ID. The socket-proxy setup doc and compose examples now include INFO=1 in the required environment variables. Default notification body templates no longer repeat the [server] source prefix that already appears in the title — providers that concatenate title + body (Telegram, Slack, Mattermost, Matrix, Teams, Google Chat, Rocket.Chat) are unaffected because source context is carried by the title.
  • #289 — Container rows no longer drop sort position during an in-flight update, and every terminal outcome (succeeded / failed / rolled-back) now fires a toast. The operation display hold captures a sort-field snapshot at hold start so the row stays pinned through the docker recreate window; the active hold extends to 10 minutes with a reconciliation pass after each container-list refresh that collapses the hold back to the 1.5s settle window for any container whose raw status no longer shows an in-progress update (safety net for missed terminal SSEs).
  • #291 — Dashboard update flow now fires the same toast sequence as the Containers view: "Update started" on click (no more premature "updated" message), then "Updated / Update failed / Rolled back" on the terminal state. Wired through the same SSE-driven operation display hold that the Containers view uses.
  • Concurrent dashboard update queueing — The per-row Update button in the dashboard Recent-Updates widget is no longer disabled by a global isDashboardBulkUpdateLocked state whenever any row is updating; each row's button now only disables for its own in-flight state, matching the Containers view and letting users stack additional updates onto the queue while one is in flight.
  • Dashboard updating/queued badge centering — The Updating/Queued pill on dashboard recent-updates rows now renders as an absolute-positioned row overlay using the shared dd-row-updating / .dd-row-overlay pattern from the Containers view (badge spans horizontally and vertically centered, rest of the row dims to 30%) instead of as an inline badge next to the container name.
  • resultChanged preserved through env redactionclassifyContainerRuntimeEnv previously dropped the non-enumerable resultChanged function when spreading into the redacted object, which produced resultChanged is not a function at watch time. The function is now re-attached via Object.defineProperty after the spread so watcher scans against redacted state continue to work.
  • #310 — Restored the [server] / [agent] prefix on default notification body templates that rc.10 30287c24 had stripped. The original refactor reasoned that title already carried the prefix so body duplication was redundant on chat providers like Slack/Telegram; it did not account for batch email, where Gmail shows one subject ("N updates available") and a bulleted body, and each bullet needs to self-identify its watcher/host. Email correctness beats chat cosmetic duplication — DEFAULT_SIMPLE_BODY_DIGEST, DEFAULT_SIMPLE_BODY_UPDATE, UPDATE_APPLIED_SIMPLE_BODY, UPDATE_FAILED_SIMPLE_BODY, and SECURITY_ALERT_SIMPLE_BODY all lead with container.notificationAgentPrefix again.

v1.5.0-rc.10

20 Apr 00:51
37fe406

Choose a tag to compare

v1.5.0-rc.10 Pre-release
Pre-release

v1.5.0-rc.10

[Unreleased]

Added

  • #299Inline update action in Security view. Image rows in the Security view now show an "Update" action button directly next to the vulnerability data when a newer image is available, instead of only offering navigation away to the Containers view. Single-container images open a confirmation dialog in place; multi-container images open a compact chooser popover to pick which instance to update. A secondary "View in Containers" link remains for cases where the user wants to inspect the full container state first. The ContainerUpdateDialog component is extracted as a standalone reusable piece. The Containers view now accepts a ?containerIds=<csv> query parameter to pre-filter to a specific set of containers, with a dismissable filter chip in the toolbar.

Fixed

  • #305 — Hide Pinned now hides every pinned container again, matching rc.8 behavior and the reporter's expectation when combining Hide Pinned with Has Update. #293 had carved out an exception for pinned rows with a pending update, but that conflated "declutter" with "surface actionable pins" and broke the filter for users who pin infrastructure containers (databases, edge, etc.) and want Hide Pinned to simply remove every pinned row from the list. The pin-to-wait-out-a-regression scenario from #293 is now addressed by simply unchecking Hide Pinned — predictable filter semantics over clever cross-filter logic.
  • #296 (follow-up) — When docker-socket-proxy blocks GET /info (the default — INFO=0), drydock now logs an actionable warning naming the watcher, the error, and the fix (set INFO=1 on your docker-socket-proxy config, or set DD_SERVER_NAME to override) instead of silently falling back to the container short ID. The socket-proxy setup doc and compose examples now include INFO=1 in the required environment variables. Default notification body templates no longer repeat the [server] source prefix that already appears in the title — providers that concatenate title + body (Telegram, Slack, Mattermost, Matrix, Teams, Google Chat, Rocket.Chat) are unaffected because source context is carried by the title.

v1.5.0-rc.9

18 Apr 01:14
b114690

Choose a tag to compare

v1.5.0-rc.9 Pre-release
Pre-release

v1.5.0-rc.9 — 2026-04-17

Added

  • Notification history store — New LokiJS collection (notifications_history) records a per-(trigger, container, event-kind) result hash so once=true dedup survives process restarts and transient changed=false scan cycles. Replaces in-memory notifiedHashes state that used to disappear on restart, and lets the digest channel maintain its own send history independently of simple/batch channels (see #282). (#c0054d25, #f79d9ec6)
  • OIDC redirect target allowlist — Post-login redirects are now validated against the backend's endpoint allowlist (not origin matching) so OIDC flows cannot be hijacked into landing on arbitrary paths. Works alongside the rc.8 strict-origin / authorization-endpoint match. (#02c27294)
  • SPA + hashed-asset cache-control — Static UI assets with hashed filenames are served with immutable long-lived cache headers, while the SPA index.html fallback carries a short revalidation header so deployments are picked up on the next navigation. (#656e502a)
  • Label truncation with tooltips across data surfaces — Long container names, tags, and image references truncate with an ellipsis and surface the full value on hover via the shared tooltip directive (which now falls back to binding text and suppresses the native title attribute to avoid browser-UI duplication). (#dec4ac06, #38530312)
  • Bounded native container table scroll — Replaced the virtualized table implementation with a bounded native scroll region that performs predictably at 10k+ rows without the virtualization layout churn that was flagged in rc.7/rc.8 feedback. (#dfa8ecff)

Changed

  • ♻️ refactor(components) — Component configuration types are now generic across the watcher / registry / trigger / authentication hierarchies, so each component carries its own typed configuration instead of the base Component<T> forcing unknown casts at every call site. (#89abe357)
  • ♻️ refactor(registries) — Public-image credential-fallback logic hoisted into BaseRegistry so every provider shares the same "private token failed → retry as public" flow, closing the per-provider drift that was surfacing in rc.8 registry logs. (#b579d810)
  • ♻️ refactor(triggers)docker and dockercompose triggers share a single compose bind-mount path remapper so the two code paths cannot drift on how host/container paths map across the agent boundary. (#4303d876)
  • 🎨 style(ui) — Updating table rows dim to 30% opacity to match the card-view treatment, keeping the visual language consistent across both layouts while an update is in-flight. (#8bcec369)

Fixed

  • #282batch+digest mode now sends both the immediate batch email and the scheduled morning digest for each detected update, matching the documented semantics. Since rc.7 (982b4d74), the batch path was writing a persistent 'update-available' history hash and evicting the just-buffered container from the digest buffer; on every subsequent scan, handleContainerReportDigest silently short-circuited because the shared once + alreadyNotified check returned true. The morning cron then fired on an empty buffer and logged buffer empty, nothing to send. Fix splits the digest channel off as its own NotificationEventKind ('update-available-digest') so batch-channel and digest-channel dedup are independent, removes the cross-channel eviction in handleContainerReports, teaches seedNotificationHistoryFromStore / handleContainerUpdateAppliedEvent about both kinds, and adds a debug log on the previously-silent skip path so operators can actually see why a container didn't make it into the buffer. (#458030b7)
  • #293 — Hide Pinned filter no longer hides pinned containers that have a pending update. Reporter pinned grafana/grafana:12.3.2 to wait out an upstream regression and expected to see 12.3.3 show up as an available update, but the Containers view and the dashboard Updates Available widget both filtered it out because the classifier treats any 3+ numeric-segment semver as pinned. Filter decluttering is preserved for static pinned containers; only rows with a pending update (newTag truthy) now surface through the filter. Updates the dashboard updatesAvailable stat card and the update breakdown widget to count pinned-with-update rows toward the true total. (#318d97ab)
  • #298 — Remote-agent container updates no longer fail with HTTP 413 Payload Too Large. The controller posted the full Container object to the agent when executing a docker / dockercompose trigger, but the agent's 256kb json body cap (introduced as v1.5.0 DoS hardening) started getting exceeded by common payloads once release-notes bodies + env + labels + image metadata grew across the RC cycle. The agent's update handler only dereferences container.id (for its own store lookup) and container.name (for the rollback-container guard), so the controller now posts just { id, name } for update triggers. Notification triggers still receive the full container so template rendering works. Reporter on Synology DSM 7 saw Error updating container ... (Request failed with status code 413) from agent-client.mediavault. (#5d6c76aa)
  • #296 (reopen) — Controller identity detection now runs for host-based watchers (TCP to a local socket-proxy, the common Synology / Docker Compose pattern), not just pure-socket watchers. Socket-based watchers still take priority, and truly remote watchers can no longer hijack the identity because host-based detection is skipped once a name has already been populated. Reporter saw [baf1154911ce] on rc.8 because their Synology setup reaches the daemon via docker-socket-proxy, which made the previous !watcher.configuration.host gate short-circuit. Workaround for users on rc.8: set DD_SERVER_NAME. (#2143804d)
  • Trivy DB cache race — The on-disk Trivy DB cache is now guarded against stale in-flight overrides during concurrent scans, so two scans starting within the same refresh window cannot leave each other with a half-applied database snapshot. (#89adcc11)
  • Malformed dd.tag.transform regex patterns — The regex-transform label validator now throws at config time for malformed or oversized patterns instead of silently producing broken tag transformations that surface as bogus update detections. (#0a93f113)
  • SSE teardown double-run — Event-stream cleanup listeners register with { once: true } so a client disconnect cannot run teardown twice and briefly emit duplicate metrics / log lines for the same client ID. (#bcaba6e4)
  • Updating badge rendering — Table rows now show a centered Updating badge instead of the tiny inline spinner that was hard to see on dense rows with long container names. (#fa558167)
  • Grouped-row Update All lockout — Starting an update on one container in a stack no longer disables every other row in the same group; only the updating row itself is locked. (#6c4d10ea)
  • Modal backdrop z-indexz-index utility classes are now registered as Tailwind @utility rules so modal backdrops reliably cover the page instead of getting layered below dashboard widgets on certain themes. (#7bb26683)
  • Containers table 70vh cap — Dropped the legacy max-height: 70vh cap on the containers table so the table fills the viewport like every other data surface, eliminating the phantom whitespace at the bottom of the page on tall displays. (#d2fceec7)
  • Grouped containers table 70vh cap (regressed in rc.9) — The bound-scroll fix in rc.9 reintroduced a max-height="70vh" cap on the grouped (Stacks) table; removed so it again fills the page like flat view. The DataViewLayout already provides the page-level scroll surface — no nested scroller needed.
  • Table cell vertical alignment — DataTable cells default to align-middle instead of align-top. Multi-line name+image cells used to push the row taller than the actions column, leaving the actions floating in the vertical middle while every other cell stuck to the top. Centering everything keeps the row reading as a single horizontal band of metadata.
  • Container icon size in table view — Container icons in the grouped/flat table bumped from 20px → 32px and the icon column from 40px → 56px so they read at a glance on dense rows.
  • Sidebar nav top padding — Trimmed the gap between the DRYDOCK brand and the first nav item (Dashboard) from ~14px to ~6px so the brand visually anchors to the nav grid below it instead of floating above empty space.
  • **Notification bell dropdown row...
Read more

v1.5.0-rc.8

13 Apr 23:42
c05d2c7

Choose a tag to compare

v1.5.0-rc.8 Pre-release
Pre-release

v1.5.0-rc.8

[1.5.0-rc.8] - 2026-04-13

Added

  • Backend-driven update queue — Container updates are now queued server-side with per-trigger concurrency limits. UI shows Queued → Updating → Updated state progression with sequence labels (e.g. "Updating 1 of 3"). (#bacbc1c7, #5edc55a2, #7d7cfdfc)
  • Watcher next-run metadata (#288) — Watcher API and Agents view now show when each watcher will next poll for updates. (#6706929e)
  • Notification delivery failure audit entries (#282) — Failed notification deliveries appear in the notification bell dropdown. (#ae5309b3)
  • Container action operations — Container actions (start, stop, restart, etc.) tracked with dashboard updates. (#6615ccbf)
  • Identity-keyed container tracking — Containers tracked by stable identity key (agent::watcher::name) across renames/replacements. Audit events include containerIdentityKey. Recent-status API returns statusesByIdentity for precise per-container status resolution. (#8fb75070, #ea413c34)
  • Trigger buffer retention and capacity limits — Digest and batch-retry buffers now enforce a 5,000-entry cap and 7-day retention window, evicting the oldest entries first and pruning stale entries before each access. Prevents unbounded memory growth on long-running controllers. (#c6a9930c)
  • Update operation recovery phases — New phases (recovered-cleanup-temp, recovered-rollback, recovered-active, recovery-failed, recovery-missing-containers) distinguish operations that completed via the deferred reconciliation recovery path from the primary update flow. (#3ffe2f12)

Changed

  • Auth user cache removed — Replaced TTL-based auth cache with request deduplication only, ensuring logout/session expiry in other tabs is reflected immediately. (#923e0926)
  • ♻️ refactor(ui) — Container update cards now render phase-only queue status, while grouped stack headers own the frozen X of Y done batch progress copy for multi-container updates.

Fixed

  • #296 — Notification server-name prefix no longer renders the container ID on Docker Compose setups. getServerName() precedence is now DD_SERVER_NAME → detected Docker/Podman daemon host name (from dockerApi.info().Name) → os.hostname(). Remote watchers never hijack the controller identity. (#b723369f)
  • #286 — Stack view column shifting fixed by collapsing all stacks into a single table. (#2f511d33)
  • #283 — Duplicate server name in notification prefix and suffix suppressed. (#aaf9962d)
  • #270 — Hide-pinned filter now uses computed tagPinned property instead of stale stored field. Unconditional startup repair ensures tagPrecision data is always correct. (#c7ecceef, #0949a142)
  • #291 — Dashboard update widget now uses the same update-start semantics as containers view (shows "Updating" toast, not "Updated"). (#c9f21a7b)
  • #290 — Update-applied success events preserved across Docker container rename race. (#6b5c1f72)
  • #289 — Standalone (non-queued) update state transitions restored after queue-aware changes broke them in rc.7. (#2b00c4b8)
  • #287 — Custom healthcheck backward compatibility restored. The built-in /bin/healthcheck remains the default image probe and now handles TLS backends, while curl is again present in the Docker image for user-defined custom healthcheck: overrides during the v1.5.x deprecation window. v1.6.0 is the final warning release, and removal is now scheduled for v1.7.0. (#414f0170)
  • #282 — Digest buffer evicted after batch send; watcher events awaited for handler ordering; batch send retry with delivery failure audit; agent remote report events awaited. (#982b4d74, #0594e971, #c31f78eb, #1ac7c36d)
  • #276 — Dashboard update tracking keyed by container ID instead of name. (#c81e25a8)
  • #256 — Pending update state scoped by stable container identity, preventing cross-contamination between same-name containers on different hosts. (#05c023fe)
  • #253 — Shorthand trigger references resolved in notification rule matching; notification buffering keys stabilized; debug logging added to every silent filter path. (#ba6341b4, #d475d33c, #bb1550e4)
  • #248 — API guard against duplicate container updates (409 conflict). (#110aae36)
  • #217 — Vulnerability rows top-aligned in detail panels. (#431be5ea)
  • Expired update operations — Executor revives expired pre-created operations instead of inserting duplicate rows. (#04e847ef)
  • Static asset throttling — SPA fallback rate limiter no longer throttles static assets. (#bfe52038)
  • Compound rolling tag aliases misclassified as pinnedisTagPinned now treats aliases like latest-alpine, stable_arm64, and dev.build as floating even when their suffixes contain digits. (#da613f70)

Performance

  • Virtual scrolling for grouped containers table — Grouped container tables no longer render every row eagerly, keeping the DOM light on deployments with many containers. (#606d5cc0)

Security

  • Axios CVE-2025-62718 — Updated axios 1.13.6 → 1.15.0. (#c4af5e4a)
  • Healthcheck HTTPS probe hardening/bin/healthcheck no longer uses popen() with shell command interpolation to invoke openssl. The probe now locates the openssl binary explicitly, fork/execs it with pipes, and uses poll-driven I/O with SIGPIPE handling — eliminating any shell injection surface. (#0173d7ed)
  • SSE log IP hashing with opt-in raw mode — SSE connect/disconnect lines and per-IP rate-limit warnings now log the internal client ID plus a salted hash of the source IP (h:xxxxxxxx) by default. The hash salt rotates on every process start, so hashed identifiers cannot be correlated across restarts and raw addresses never touch the log. Operators troubleshooting a specific connection issue can set DD_SSE_DEBUG_LOG_IP=true to temporarily log raw IPs. (#9e236745)
  • HTTP trigger proxy URL scheme validation — HTTP trigger proxy URLs must now be http:// or https:// schemes. Invalid schemes are rejected at config-validation time and fail closed at runtime instead of silently constructing a broken proxy. (#981f7f8e)
  • Vulnerability CSV export escape hardening — Every CSV field (including column headers) is now quoted unconditionally, and tab/CR leading characters are escaped alongside =+-@ to fully close the CSV formula-injection surface. (#95de9f0e)
  • Snyk policy file — Added a repo-level .snyk file so reviewed false-positive Snyk Code findings are silenced with a mandatory reason and expiry date alongside the code, instead of in PR comments. Initial entries cover the ephemeral session-secret generator and the UI static file sink flagged via process.argv[1] taint. (#61f49606)
  • Supply-chain toolchain refresh — Bumped pinned Alpine edge/testing package versions for cosign (2.4.3-r12) and trivy (0.69.3-r2) in the Dockerfile to track upstrea...
Read more

v1.5.0-rc.7

09 Apr 00:29

Choose a tag to compare

v1.5.0-rc.7 Pre-release
Pre-release

v1.5.0-rc.7

[1.5.0-rc.7] - 2026-04-08

Added

  • Multi-server notification identification (#283) — Notifications automatically include a [server-name] prefix when agents are registered, identifying which server (controller or agent) each update comes from. Controller name configurable via DD_SERVER_NAME (defaults to os.hostname()). Custom templates can use container.notificationServerName and container.notificationAgentPrefix. (#14365870, #5880b4c8)
  • Infrastructure update modedd.update.mode=infrastructure label for socket proxy containers enables helper-swap update path bypassing the socket proxy. (#0e8f620d)

Changed

  • SSE debug logging — Connect/disconnect log messages now include client ID and IP address. (#12942ee4)

Security

v1.5.0-rc.6

06 Apr 02:28

Choose a tag to compare

v1.5.0-rc.6 Pre-release
Pre-release

v1.5.0-rc.6

[1.5.0-rc.6] - 2026-04-05

Added

  • Rollback shortcut in container actions menu — Quick rollback option directly from the container row actions dropdown. (#5137b99a)
  • Clear all button in notification bell — One-click dismiss of all notification bell entries. (#6785e2a4)
  • Info toast when no pending update — Container update check now shows an informational toast when the container is already at its latest version. (#08864936)
  • Typed notification event templates — Every event type (update-available, update-applied, update-failed, security-alert, agent-reconnect) now has a dedicated default template with type-safe variable placeholders. (#69f85c36)
  • Digest-aware default notification templates — Default templates now indicate when a notification was delivered via digest accumulation. (#f7668d44)
  • Structured dispatch decisions with digest routing — Trigger dispatch returns structured decision objects with digest routing warnings for debugging notification flow. (#fd82adc8)
  • Agent SSE event relay — Controller now relays update-applied, update-failed, and security-alert events from remote agents over SSE to connected UI clients. (#f497ac75)
  • Implicit all-triggers semantics label — Notification rule editor shows helper text when an update-available rule implicitly applies to all triggers. (#ba43a870)

Changed

  • Responsive dashboard layout persistence — Dashboard widget bounds and layout are now breakpoint-aware, persisting separate layouts per viewport tier. (#d16a3d0f, #2deac1c3)
  • Tag precision module consolidationTagPrecision type and numeric tag shape parser deduplicated into tag/precision module. (#857fcb7a, #f2cc81e0)
  • Trigger notification internals — Extracted notification event helpers, hoisted default templates, type-safe container access in store. (#3e81f378, #42f5c7aa)
  • Outlined button styling — Filled background and improved contrast for outlined action buttons across all views. (#b901295e, #898b6195)
  • Axios supply chain advisory retired — Removed from README and website security documentation. (#c782d3dc)

Fixed

  • #270 — Hide-pinned filter now applies to dashboard update widgets; tagPrecision backfilled for existing containers on migration. (#f2e36ce4, #972f7af1)
  • #271 — Log sort order persists across navigation. (#dbc42186)
  • #265 — Stale update detection suppressed from pre-clear watcher scans. (#06e8a4ee)
  • #217 — Data rows top-aligned with scroll containment for container views. (#a97bd0a9)
  • Dashboard mobile responsive layout — Recent updates widget layout fixed on mobile viewports. (#e686bdef)
  • Dashboard layout hardening — Unknown widgets handled gracefully; unnecessary re-renders suppressed. (#37a33a80)
  • Preference persistence on page exit — Pending dashboard layout and preference writes flushed on pagehide and visibilitychange. (#554b7c37, #1cffd21e)
  • Disabled button hover affordance — Disabled outlined buttons no longer show hover state. (#89ca53d3)
  • Tag precision backfill guardtagPrecision backfill skipped when upgrading past v1.5.0. (#9deb3ba1)
  • Tag shape parser crashgetNumericTagShape guarded against undefined transformTag result. (#eedc01bf)
  • Digest buffer stale entries — Container name canonicalized before digest buffer eviction; stale entries evicted before flush. (#547a3f15, #2776a2d6)
  • Digest buffer type safetyflushDigestBuffer store container map typed as Container. (#d256e3af)

Performance

  • Log viewer cache — Incremental newest-first cache for log viewer avoids full re-sort on new entries. (#3340b7c1)
  • Vulnerability list URL memoization — Safe URL computation memoized to avoid redundant parsing. (#d9bcbe5d)

Security

  • Vulnerability URL and CSV sanitization — Vulnerability URLs validated before rendering; CSV export fields sanitized against formula injection. (#c9ecf1e6)