Skip to content

Releases: CodesWhat/drydock

v1.5.0-rc.37

15 Jun 20:43
25b3df8

Choose a tag to compare

v1.5.0-rc.37 Pre-release
Pre-release

v1.5.0-rc.37

Full Changelog: v1.5.0-rc.36...v1.5.0-rc.37

[1.5.0-rc.37] — 2026-06-15

Security

  • Patched a batch of newly-disclosed transitive CVEs across every workspace. osv-scanner flagged advisories disclosed 2026-06-15 in build- and test-time dependencies: vite (CVE-2026-53571, CVE-2026-53632), @babel/core (CVE-2026-49356), form-data (CVE-2026-12143), protobufjs (CVE-2026-54269), and ws (CVE-2026-48779). Each is pinned to a fixed version via an override (or a direct bump where the dependency is direct). js-yaml@3.14.2, reachable only through artillery's test-only load-test harness, is triaged as unreachable: its sole fix removes the safeLoad() API artillery still calls, and it parses only trusted in-repo configs.

Changed

  • Registry rate-limiter burst raised from 5 to 10 for ghcr.io and Docker Hub. The conservative burst allowance was tripping the limiter during legitimate request spikes (enumerating tags across many containers at once); the sustained rate (2 req/s) is unchanged.

  • Hardened the E2E/CI suite against transient flakes. Crash-prone real-application e2e fixtures (Home Assistant, Radarr) now run a keep-alive entrypoint so the watcher consistently discovers the full container set instead of intermittently seeing one short; the test-bootstrap readiness count is now exact and strict; and the Playwright container-detail helpers wait on real conditions rather than fixed timeouts. No shipped runtime behavior changes from this item.

Warning

Upgrade notes — behavioral changes, please read before updating. Releases 1.4.6 and the entire 1.5 line ship security-hardening fixes that change runtime behavior. These are not deprecations: there is no compatibility shim or grace period, so a previously-working deployment can change behavior on upgrade.

  1. OIDC login now requires authorization_endpoint in your provider's discovery metadata. The authorization-redirect allowlist no longer falls back to a broad same-origin match. Mainstream identity providers (Keycloak, Authentik, Authelia, Okta, Google, Entra/Azure AD, Zitadel, …) publish this field and are unaffected. If your /.well-known/openid-configuration does not advertise authorization_endpoint, OIDC sign-in will now fail closed — make sure the discovery document exposes it.
  2. Unauthenticated rate-limit buckets now key on the TCP peer address instead of X-Forwarded-For. Behind a reverse proxy (nginx / Traefik / Caddy), all unauthenticated clients now share a single bucket (the proxy's address), regardless of DD_SERVER_TRUSTPROXY. Internet-facing or multi-user instances may begin to see unexpected 429 Too Many Requests on unauthenticated endpoints. Authenticated requests are keyed per session and are unaffected.
  3. HTTP-trigger proxy URLs must now use the http:// or https:// scheme. Any other scheme (e.g. socks5://) is rejected at config load. Such values were previously accepted but only ever treated as an HTTP proxy — switch to an http(s):// proxy URL.

v1.5.0-rc.36

15 Jun 15:51
423b6b0

Choose a tag to compare

v1.5.0-rc.36 Pre-release
Pre-release

v1.5.0-rc.36

Full Changelog: v1.4.6...v1.5.0-rc.36

[1.5.0-rc.36] — 2026-06-15

Added

  • Experimental Portwing edge-agent mode — agents behind NAT or firewalls can now dial OUT to drydock over a persistent wss:// WebSocket instead of waiting for an inbound controller connection (PR #429, M5). The feature is experimental and opt-in: set DD_EXPERIMENTAL_PORTWING=true to enable it. When disabled, the endpoint is not mounted and the feature has zero runtime footprint. Once enabled, agents connect to WS /api/v1/portwing/ws using the portwing/1.0 subprotocol. Authentication is Ed25519 public-key challenge-response with timestamp + nonce replay protection (±60 s clock-skew window, 16 MB maximum frame size). Operator key management is exposed through a REST registry at /api/v1/portwing/keys (list, register, revoke). Because the feature is experimental the protocol and API surface may change in a future release without a deprecation notice.

  • Remote-agent runtime info now carries logLevel and pollInterval in the acknowledgement payload (PR #430, M4). Drydock threads these fields through buildRuntimeInfoFromAck and surfaces them in the Agents view alongside the existing runtime metadata.

Fixed

  • Containers pinned to a fully-specified semver tag (e.g. image:v1.13.3, 3+ numeric segments) no longer climb to newer versions by default (#321). Tags classified as specific precision are now treated as digest-only by default — getTagCandidates returns an empty tag list so updates track digest changes only, not semver version bumps. Opt in to semver climbing by setting dd.tag.include (restricts climbing to matching tags) or dd.tag.family=loose (unrestricted climbing as before). Floating tags (latest, 16-alpine, etc.) and 1–2-segment partial versions are unaffected.

  • Maintenance window now gates when auto-updates are applied, not just when update checks run (#321). Previously watchFromCron respected the window, but maybeFastResyncAfterUpdate (the post-update fast resync) called watchContainer unconditionally — allowing a triggered resync to detect a new image and dispatch an update outside the window. The fast resync now mirrors the same maintenance-window guard used by watchFromCron. Additionally, computeUpdateEligibility gains a maintenanceWindowOpen context field: when false, a soft maintenance-window-closed blocker is recorded in the eligibility result. Manual UI/API-triggered updates pass undefined and remain ungated.

  • Self-update overlay no longer flickers — the UI holds the "Applying Update" screen until the swap is actually complete. Three compounding defects made the self-update experience look broken: (1) the UI's connectivity probe treated any successful /auth/user response as "update finished", but the old server keeps answering during the image pull — so the overlay dismissed almost immediately, then the page died again when the old container actually stopped; (2) the in-progress self-update operation could never be recorded as completed: the finalize callback secret was regenerated per-process (the helper's POST always failed with 403 after the restart) and startup reconciliation expired the operation before the helper could finalize it; and (3) the transient drydock-self-update-<timestamp> helper container carried no watch-exclusion label, so it flashed into the container list with watch-by-default enabled. The finalize secret is now per-operation, with its SHA-256 hash persisted on the operation row so the restarted process can validate the helper's callback; startup reconciliation grants fresh in-progress self-update operations a 10-minute grace window (with expiry as the bounded fallback if the helper dies); a new unauthenticated GET /api/v1/self-update/{operationId}/status endpoint reports the operation state while the session is unavailable mid-restart; the UI polls that endpoint during a self-update and only reloads once the operation reaches a terminal state (succeeded, rolled-back, failed, or expired); and the helper container is labeled dd.watch=false so it never appears in the watcher's container list. The UI also closes its SSE connection when self-update mode begins (after the ack is delivered) — the browser's built-in EventSource retry would otherwise reconnect to the new server, clear the overlay before the swap was committed, and leave the SPA running stale pre-update assets. Dry-run mode no longer shows the overlay at all: the UI notification is skipped and the no-op operation is marked terminal immediately instead of lingering in-progress.

Warning

Upgrade notes — behavioral changes, please read before updating. Releases 1.4.6 and the entire 1.5 line ship security-hardening fixes that change runtime behavior. These are not deprecations: there is no compatibility shim or grace period, so a previously-working deployment can change behavior on upgrade.

  1. OIDC login now requires authorization_endpoint in your provider's discovery metadata. The authorization-redirect allowlist no longer falls back to a broad same-origin match. Mainstream identity providers (Keycloak, Authentik, Authelia, Okta, Google, Entra/Azure AD, Zitadel, …) publish this field and are unaffected. If your /.well-known/openid-configuration does not advertise authorization_endpoint, OIDC sign-in will now fail closed — make sure the discovery document exposes it.
  2. Unauthenticated rate-limit buckets now key on the TCP peer address instead of X-Forwarded-For. Behind a reverse proxy (nginx / Traefik / Caddy), all unauthenticated clients now share a single bucket (the proxy's address), regardless of DD_SERVER_TRUSTPROXY. Internet-facing or multi-user instances may begin to see unexpected 429 Too Many Requests on unauthenticated endpoints. Authenticated requests are keyed per session and are unaffected.
  3. HTTP-trigger proxy URLs must now use the http:// or https:// scheme. Any other scheme (e.g. socks5://) is rejected at config load. Such values were previously accepted but only ever treated as an HTTP proxy — switch to an http(s):// proxy URL.

v1.4.6

12 Jun 17:08

Choose a tag to compare

Warning

Upgrade notes — behavioral changes, please read before updating. These are security hardening fixes, not deprecations: there is no compatibility shim or grace period, so a previously-working deployment can change behavior on upgrade.

  1. OIDC login now requires authorization_endpoint in your provider's discovery metadata. The authorization-redirect allowlist no longer falls back to a broad same-origin match. Mainstream identity providers (Keycloak, Authentik, Authelia, Okta, Google, Entra/Azure AD, Zitadel, …) publish this field and are unaffected. If your /.well-known/openid-configuration does not advertise authorization_endpoint, OIDC sign-in will now fail closed — make sure the discovery document exposes it.
  2. Unauthenticated rate-limit buckets now key on the TCP peer address instead of X-Forwarded-For. Behind a reverse proxy (nginx / Traefik / Caddy), all unauthenticated clients now share a single bucket (the proxy's address), regardless of DD_SERVER_TRUSTPROXY. Internet-facing or multi-user instances may begin to see unexpected 429 Too Many Requests on unauthenticated endpoints. Authenticated requests are keyed per session and are unaffected.
  3. HTTP-trigger proxy URLs must now use the http:// or https:// scheme. Any other scheme (e.g. socks5://) is rejected at config load. Such values were previously accepted but only ever treated as an HTTP proxy — switch to an http(s):// proxy URL.

[1.4.6] — 2026-06-12

Security maintenance release for the 1.4.x line.

Security

  • Dependency security updates — Cleared all known advisories in the shipped backend (app) and dashboard (ui) dependencies, including a critical arbitrary-code-execution issue in protobufjs and high-severity SSRF / prototype-pollution / credential-leak issues in axios, plus @grpc/grpc-js, fast-uri, fast-xml-parser, path-to-regexp, qs, and others. Both workspaces now report zero npm audit vulnerabilities. No major dependency upgrades were needed — the Docker watcher stays on dockerode 4.x (the flagged transitive uuid advisory affects v3/v5/v6 with a buf argument and is not reachable through dockerode's v4() usage; it is pinned to a fixed release via an override).
  • OIDC authorization-redirect hardening — The OIDC provider now requires a strict authorization-endpoint match and no longer falls back to a broad same-origin allowlist when discovery metadata lacks an authorization_endpoint. This prevents an attacker who controls a different path under a shared-origin identity provider from steering the authorization redirect to an attacker-controlled endpoint.
  • HTTP trigger SSRF guard — Proxy URLs configured for the HTTP trigger are now restricted to http/https schemes, validated both at configuration time (schema) and at runtime, failing closed on any other scheme.
  • Rate-limit key spoofing fix — Unauthenticated rate-limit keys are now derived from the TCP peer address (socket.remoteAddress) instead of request.ip, so a client behind a trusted proxy cannot spoof X-Forwarded-For to evade per-IP rate limits.

v1.5.0-rc.35

10 Jun 20:20
e657dda

Choose a tag to compare

v1.5.0-rc.35 Pre-release
Pre-release

v1.5.0-rc.35

Full Changelog: v1.5.0-rc.34...v1.5.0-rc.35

[1.5.0-rc.35] — 2026-06-10

Fixed

  • False "update failed" notification when concurrent update requests race (#421). When a duplicate update request was rejected with HTTP 409 ("update already in progress") while the winning update was still in flight, the controller classified the conflict as a genuine failure — firing an "update failed" notification immediately followed by "updated successfully". The duplicate classifier now recognizes three benign signals instead of one: a recently-succeeded operation (as before), a 409 response whose body explicitly carries the active-update lock message ("Container update already queued/in progress" — authoritative even before the winner's state has propagated from a remote agent over SSE), and another active (queued or in-progress) operation for the same container and agent+watcher identity. The same reclassification now also covers the Docker-native rename path in ContainerUpdateExecutor, which previously marked the duplicate failed before the outer classifier could run.

  • Update operations could hang in-progress forever when deferred rollback reconciliation failed. The deferred reconciliation callback only logged a warning on error, leaving the operation permanently active and blocking all future updates for that container until restart. The operation is now terminalized as failed (self-update and already-terminal operations excluded, as elsewhere).

  • Spurious "update available" notifications around just-updated containers (#408 hardening). Three escape hatches in the post-update suppression mechanism are closed: suppression now keys on both the container ID and the watcher-scoped name, so the recreated container's new Docker ID can no longer dodge the check; the batch retry buffer consults suppression before re-queuing; and on startup the suppression set is re-seeded from update operations that succeeded within the last hour, so a controller restart between an update and the watcher's confirming scan no longer re-fires a stale notification. Suppression entries now also expire after one hour and are cleared on trigger deregistration, so containers deleted outright (or agents that never reconnect) can no longer leak entries for the life of the process.

  • Live log viewer returned 403 behind TLS-terminating reverse proxies even with DD_SERVER_TRUSTPROXY set. WebSocket upgrades bypass Express, so the log-stream origin check never honored trust-proxy. It now compares the browser origin against X-Forwarded-Host/X-Forwarded-Proto (first hop) when trust proxy is enabled — and remains byte-for-byte strict when it is not.

  • Containers of permanently removed agents lingered in the store forever. Startup now prunes container rows whose agent no longer matches any registered agent component. Rows of registered-but-currently-disconnected agents are untouched.

  • "Updated successfully" toast fired while the new container was still starting (#290 follow-up). The toast settled on the old container's removal event during a recreate; it now waits for the replacement container's arrival (replacementExpected removals are skipped, and the new container's ID rides the dd:update-applied payload as newContainerId), closing the status gap between the last update activity and the success notification. The toast dedup TTL also gained a 20% margin over the server's SSE replay buffer to prevent a boundary duplicate on reconnect.

  • Watcher enrichment failures that threw non-Error values leaked malformed entries into the container snapshot. Thrown non-Error values are now wrapped, counted as enrichment errors, and excluded.

  • GCR registry reported anonymous configurations as authenticated. Gcr.getAuthPull() now returns undefined without credentials, matching the other providers. GHCR 404 detection now checks the axios response status instead of matching error-message strings.

Security

  • Command trigger no longer inherits the full process environment. User-authored command scripts previously received every DD_* secret (registry tokens, notification tokens, agent secrets) via process.env. The child environment is now built from a fixed allowlist (PATH, HOME, SHELL, USER, LANG, LC_ALL, TZ, TMPDIR, TMP, TEMP) plus the drydock-provided container variables. Scripts that legitimately need more can name additional variables with the new DD_ACTION_COMMAND_{name}_ENV option (comma-separated).

  • Hook commands can be restricted to an allowlist of binaries (DD_HOOKS_ALLOWED_COMMANDS). With hooks enabled, dd.hook.pre/dd.hook.post labels could invoke any binary on the image. The new comma-separated allowlist matches the hook command's first token (basename, or exact path for entries containing /); when unset, behavior is unchanged and a one-time warning recommends configuring it.

  • HTTP trigger blocks cloud metadata endpoints. Requests resolving to link-local ranges (169.254.0.0/16 including 169.254.169.254, fe80::/10, fd00:ec2::254) are rejected before sending — including IPv4-mapped and IPv4-compatible IPv6 spellings of those ranges (::ffff:169.254.169.254, ::ffff:a9fe:a9fe, ::169.254.169.254), which would otherwise slip past the literal-IP check. Private-network and localhost targets remain fully supported — they are the normal self-hosted case. The rare legitimate link-local target can opt out via DD_NOTIFICATION_HTTP_{name}_ALLOWMETADATA=true.

Performance

  • Tag transform patterns are no longer recompiled in the sort hot path. Compiled RE2 transform patterns are cached per formula and tag candidates are transformed once before sorting instead of twice per comparison — previously ~3,000 compilations per 300-tag container per watch cycle.

  • Container normalization no longer deep-clones the entire container per registry call. The watcher now copies only the image/registry fields it mutates instead of structuredClone of labels and environment for every container every cycle.

  • Update-operation retention pruning uses indexed status queries instead of materializing the whole collection every 100th mutation, and the default rejected-credential pattern in BaseRegistry is compiled once at module load. An unused updatedAt collection index no longer taxes every mutation.

Changed

  • Trigger providers must implement trigger()/triggerBatch(). The base implementations now throw instead of silently doing nothing, so a provider that forgets to override fails loudly. All 22 bundled providers already comply; this only affects out-of-tree forks.

v1.5.0-rc.34

08 Jun 03:40
36bd81c

Choose a tag to compare

v1.5.0-rc.34 Pre-release
Pre-release

v1.5.0-rc.34

Full Changelog: v1.5.0-rc.33...v1.5.0-rc.34

[1.5.0-rc.34] — 2026-06-07

Added

  • Trigger environment variable taxonomy split — DD_ACTION_* and DD_NOTIFICATION_* prefixes. Action triggers (Docker, Docker Compose, Command) are now configured with DD_ACTION_* and dd.action.* labels; notification/messaging triggers (Slack, SMTP, Discord, Telegram, ntfy, Pushover, and all others) are configured with DD_NOTIFICATION_* and dd.notification.* labels. All three prefix families (DD_ACTION_*, DD_NOTIFICATION_*, DD_TRIGGER_*) are interchangeable at runtime — merge priority is DD_NOTIFICATION_* > DD_ACTION_* > DD_TRIGGER_*. A migration CLI (drydock config migrate --source trigger) rewrites DD_TRIGGER_*, dd.trigger.include, and dd.trigger.exclude to action-prefixed aliases automatically; use --dry-run to preview changes before applying.

  • Per-agent Home Assistant MQTT topic segmentation (DD_NOTIFICATION_MQTT_<name>_HASS_AGENTTOPICSEGMENT, default false). When enabled, Drydock inserts an agent/<name> segment into every Home Assistant MQTT topic — per-container state topics, watcher-level count/update sensors, and watcher running-status sensors — for containers owned by a remote agent, so two agents that both use the default watcher name local no longer publish to (and overwrite) the same topics. Enabling it also scopes the watcher-level sensor counts and the discovery-entity cleanup per agent, fixing the Home Assistant facet of #386. Controller-local container topics are unchanged. Because it changes the Home Assistant entity IDs for agent-owned containers, it is opt-in for the v1.5.x line and targeted to become the default in v1.7.0 — see Deprecated.

  • Up-to-date and pinned badges in Kind column — Containers table now shows a green check-circle badge ("Up to date") for containers at their latest version, and a green pin badge ("Pinned") for containers with skipped updates, replacing the previous dash placeholder.

  • Show/hide toggle on the login password field (commit e086c5bc). The sign-in password input now has an eye / eye-slash button to reveal or mask what was typed, with an accessible label and type="button" so it never submits the form.

  • Real-time container log viewer — WebSocket-based live log streaming from Docker containers directly in the UI. Features ANSI color rendering, automatic JSON log detection with syntax-highlighted pretty-printing, free-text and regex search with match navigation, stdout/stderr stream filtering, log level filtering for structured logs, copy to clipboard, and gzip-compressed download. Available in both the container detail panel and a dedicated full-page view at /containers/:id/logs. (Phase 4.2)

  • Diagnostic debug dump — One-click export of redacted system state from Configuration > Diagnostics. Collects runtime metadata, component state (watchers, registries, triggers, agents), Docker API diagnostics, MQTT Home Assistant sensors, recent Docker events, store stats, and DD_* environment variables. Sensitive values matching password|token|secret|key|hash are automatically redacted. Configurable time window (1–1440 minutes). (Phase 4.14)

  • Container log streaming APIWS /api/v1/containers/:id/logs/stream endpoint with Docker binary stream demultiplexing, session-based authentication on WebSocket upgrade, and fixed-window rate limiting (1,000 connections per 15 minutes).

  • Container log download APIGET /api/v1/containers/:id/logs endpoint with gzip compression support, stdout/stderr filtering, configurable tail size, and timestamp-based since filtering.

  • Debug dump APIGET /api/v1/debug/dump endpoint with configurable minutes query parameter for time-windowed event collection.

  • Dashboard customization — Customizable grid layout with drag-to-reorder, resize, and per-widget visibility toggles using grid-layout-plus. Edit mode via pencil icon in breadcrumb header. Customize panel with checkboxes and S/M/L size badges. All widgets progressively collapse content based on container height.

  • Resource usage dashboard widget — CPU and memory usage bars with top-N resource consumers, progressive detail at different widget sizes.

  • Fleet-aggregate stats subsystem (commits feature/v1.5-rc17). New ContainerStatsAggregator polls watched local and agent-owned containers once per tick (default 10 s) and computes a fleet-wide ContainerStatsSummary (total CPU%, total memory, top-N rows). Two new endpoints — GET /api/v1/stats/summary and GET /api/v1/stats/summary/stream — expose the current snapshot and a live SSE feed; the dashboard Resource Usage widget now consumes the SSE stream directly, fixing the regression (introduced in rc.13 by the ?touch=false workaround) where the widget showed zeros because the per-container cache was never warmed. The legacy GET /api/v1/containers/stats endpoint and the client-side summarizeContainerResourceUsage rollup have been removed.

  • Per-container update locks (commit 761fb834). New keyed LockManager primitive in app/updates/lock-primitives.ts replaces the module-level pLimit(1) that was serialising every container update across the entire process. Lock keys are derived per container (and per compose project for Dockercompose), so two unrelated containers can now pull and recreate concurrently while two services in the same compose project still serialise correctly.

  • Restart recovery for queued and pulling updates (commit 00788b13). Startup reconciliation in app/store/update-operation.ts is now selective: status=queued operations stay queued for the recovery dispatcher to pick up, and phase=pulling rows are reset to queued (pull is idempotent). A new app/updates/recovery.ts module runs once after registry.init(), re-resolves trigger and container for each queued operation, and dispatches them through the existing fire-and-forget pipeline.

  • Notification outbox with retry and dead-letter queue (commits a9561d93, 7d2ef6eb, b215d295, ce26bece). New notificationOutbox LokiJS collection and app/notifications/outbox-worker.ts background worker provide durable retry semantics for notification dispatch. On failure, the delivery intent is persisted to the outbox and the worker retries on a periodic drain with exponential backoff + jitter. After a configurable number of failed attempts (default 5) entries transition to the dead-letter queue; delivered and dead-letter entries are auto-purged past TTL (default 30 days). New /api/notifications/outbox REST surface lets operators list entries, retry from the DLQ, or discard.

  • Notification outbox UI (commit feature/v1.5-rc17). New Notification outbox page (route /notifications/outbox, nav under Settings) with status tabs (Dead-letter / Pending / Delivered), retry and discard actions.

  • Cancel queued or in-flight updates (commits 4b79e3ac, 79487115). POST /api/operations/:id/cancel now accepts both queued and in-progress operations. Queued ops are marked failed immediately; in-progress ops are flagged via a cancelRequested field and the lifecycle observes the flag at three safe checkpoints.

  • Global concurrent-update cap (DD_UPDATE_MAX_CONCURRENT). New counting semaphore provides a configurable global gate on how many update lifecycles run simultaneously. Default 0 = unlimited. Positive integer N means at most N updates run concurrently. Self-update operations bypass the global cap.

  • Health-gate SSE heartbeat (DD_UPDATE_HEALTH_GATE_HEARTBEAT_MS). While drydock waits for a new container to pass its health gate, a periodic heartbeat re-emits phase: 'health-gate' at a configurable interval (default 10 s). DD_UPDATE_HEALTH_GATE_HEARTBEAT_MS=0 disables heartbeats; values below 1000 ms or non-integers fail fast at startup.

  • Post-start liveness grace window (DD_UPDATE_POST_START_LIVENESS_GRACE_MS). After Docker start() returns, Drydock waits this many milliseconds and then re-inspects the new container. If the container has already exited, the lifecycle throws and the existing rollback machinery takes over — catching containers that exit immediately after an update (bad command, broken entrypoint, missing dependency) that would otherwise be recorded as a successful update. Default 2000 ms. Set to 0 to disable the check entirely. Values between 1 and 99 ms are rejected at startup; the minimum non-zero value is 100 ms.

  • Recovery-boot concurrency cap (DD_UPDATE_RECOVERY_BOOT_CONCURRENCY). When Drydock restarts after a crash it finds queued update operations left from the previous run and resumes them. This variable bounds how many are dispatched in parallel during that recovery sweep. Default 4. Values of 0 are rejected at startup (minimum is 1).

  • Self-update now works when Drydock reaches the Docker daemon over a TCP host, not only through a bind-mounted /var/run/docker.sock (commit fc34ffb9). resolveHelperDockerConnection now inspects the watcher's Dockerode connection: a TCP host produces a TCP helper attached to Drydock's own Docker network. The bind-mounted-socket path is unchanged.

  • **The per-container Update button is locked with a Self-update unavailable indicator when Drydock cannot update it...

Read more

v1.5.0-rc.33

06 Jun 20:38
626f0a9

Choose a tag to compare

v1.5.0-rc.33 Pre-release
Pre-release

v1.5.0-rc.33

Full Changelog: v1.5.0-rc.32...v1.5.0-rc.33

[1.5.0-rc.33] — 2026-06-06

Added

  • Trigger environment variable taxonomy split — DD_ACTION_* and DD_NOTIFICATION_* prefixes. Action triggers (Docker, Docker Compose, Command) are now configured with DD_ACTION_* and dd.action.* labels; notification/messaging triggers (Slack, SMTP, Discord, Telegram, ntfy, Pushover, and all others) are configured with DD_NOTIFICATION_* and dd.notification.* labels. All three prefix families (DD_ACTION_*, DD_NOTIFICATION_*, DD_TRIGGER_*) are interchangeable at runtime — merge priority is DD_NOTIFICATION_* > DD_ACTION_* > DD_TRIGGER_*. A migration CLI (drydock config migrate --source trigger) rewrites DD_TRIGGER_*, dd.trigger.include, and dd.trigger.exclude to action-prefixed aliases automatically; use --dry-run to preview changes before applying.

  • Per-agent Home Assistant MQTT topic segmentation (DD_NOTIFICATION_MQTT_<name>_HASS_AGENTTOPICSEGMENT, default false). When enabled, Drydock inserts an agent/<name> segment into every Home Assistant MQTT topic — per-container state topics and watcher-level sensor topics — for containers owned by a remote agent, so two agents that both use the default watcher name local no longer publish to (and overwrite) the same topics. Enabling it also scopes the watcher-level sensor counts and the discovery-entity cleanup per agent, fixing the Home Assistant facet of #386. Controller-local container topics are unchanged. Because it changes the Home Assistant entity IDs for agent-owned containers, it is opt-in for the v1.5.x line and targeted to become the default in v1.7.0 — see Deprecated.

  • Up-to-date and pinned badges in Kind column — Containers table now shows a green check-circle badge ("Up to date") for containers at their latest version, and a green pin badge ("Pinned") for containers with skipped updates, replacing the previous dash placeholder.

  • Show/hide toggle on the login password field (commit e086c5bc). The sign-in password input now has an eye / eye-slash button to reveal or mask what was typed, with an accessible label and type="button" so it never submits the form.

  • Real-time container log viewer — WebSocket-based live log streaming from Docker containers directly in the UI. Features ANSI color rendering, automatic JSON log detection with syntax-highlighted pretty-printing, free-text and regex search with match navigation, stdout/stderr stream filtering, log level filtering for structured logs, copy to clipboard, and gzip-compressed download. Available in both the container detail panel and a dedicated full-page view at /containers/:id/logs. (Phase 4.2)

  • Diagnostic debug dump — One-click export of redacted system state from Configuration > Diagnostics. Collects runtime metadata, component state (watchers, registries, triggers, agents), Docker API diagnostics, MQTT Home Assistant sensors, recent Docker events, store stats, and DD_* environment variables. Sensitive values matching password|token|secret|key|hash are automatically redacted. Configurable time window (1–1440 minutes). (Phase 4.14)

  • Container log streaming APIWS /api/v1/containers/:id/logs/stream endpoint with Docker binary stream demultiplexing, session-based authentication on WebSocket upgrade, and fixed-window rate limiting (1,000 connections per 15 minutes).

  • Container log download APIGET /api/v1/containers/:id/logs endpoint with gzip compression support, stdout/stderr filtering, configurable tail size, and timestamp-based since filtering.

  • Debug dump APIGET /api/v1/debug/dump endpoint with configurable minutes query parameter for time-windowed event collection.

  • Dashboard customization — Customizable grid layout with drag-to-reorder, resize, and per-widget visibility toggles using grid-layout-plus. Edit mode via pencil icon in breadcrumb header. Customize panel with checkboxes and S/M/L size badges. All widgets progressively collapse content based on container height.

  • Resource usage dashboard widget — CPU and memory usage bars with top-N resource consumers, progressive detail at different widget sizes.

  • Fleet-aggregate stats subsystem (commits feature/v1.5-rc17). New ContainerStatsAggregator polls each locally-monitored container once per tick (default 10 s) and computes a fleet-wide ContainerStatsSummary (total CPU%, total memory, top-N rows). Two new endpoints — GET /api/v1/stats/summary and GET /api/v1/stats/summary/stream — expose the current snapshot and a live SSE feed; the dashboard Resource Usage widget now consumes the SSE stream directly, fixing the regression (introduced in rc.13 by the ?touch=false workaround) where the widget showed zeros because the per-container cache was never warmed. The legacy GET /api/v1/containers/stats endpoint and the client-side summarizeContainerResourceUsage rollup have been removed.

  • Per-container update locks (commit 761fb834). New keyed LockManager primitive in app/updates/lock-primitives.ts replaces the module-level pLimit(1) that was serialising every container update across the entire process. Lock keys are derived per container (and per compose project for Dockercompose), so two unrelated containers can now pull and recreate concurrently while two services in the same compose project still serialise correctly.

  • Restart recovery for queued and pulling updates (commit 00788b13). Startup reconciliation in app/store/update-operation.ts is now selective: status=queued operations stay queued for the recovery dispatcher to pick up, and phase=pulling rows are reset to queued (pull is idempotent). A new app/updates/recovery.ts module runs once after registry.init(), re-resolves trigger and container for each queued operation, and dispatches them through the existing fire-and-forget pipeline.

  • Notification outbox with retry and dead-letter queue (commits a9561d93, 7d2ef6eb, b215d295, ce26bece). New notificationOutbox LokiJS collection and app/notifications/outbox-worker.ts background worker provide durable retry semantics for notification dispatch. On failure, the delivery intent is persisted to the outbox and the worker retries on a periodic drain with exponential backoff + jitter. After a configurable number of failed attempts (default 5) entries transition to the dead-letter queue; delivered and dead-letter entries are auto-purged past TTL (default 30 days). New /api/notifications/outbox REST surface lets operators list entries, retry from the DLQ, or discard.

  • Notification outbox UI (commit feature/v1.5-rc17). New Notification outbox page (route /notifications/outbox, nav under Settings) with status tabs (Dead-letter / Pending / Delivered), retry and discard actions.

  • Cancel queued or in-flight updates (commits 4b79e3ac, 79487115). POST /api/operations/:id/cancel now accepts both queued and in-progress operations. Queued ops are marked failed immediately; in-progress ops are flagged via a cancelRequested field and the lifecycle observes the flag at three safe checkpoints.

  • Global concurrent-update cap (DD_UPDATE_MAX_CONCURRENT). New counting semaphore provides a configurable global gate on how many update lifecycles run simultaneously. Default 0 = unlimited. Positive integer N means at most N updates run concurrently. Self-update operations bypass the global cap.

  • Health-gate SSE heartbeat (DD_UPDATE_HEALTH_GATE_HEARTBEAT_MS). While drydock waits for a new container to pass its health gate, a periodic heartbeat re-emits phase: 'health-gate' at a configurable interval (default 10 s). DD_UPDATE_HEALTH_GATE_HEARTBEAT_MS=0 disables heartbeats; values below 1000 ms or non-integers fail fast at startup.

  • Post-start liveness grace window (DD_UPDATE_POST_START_LIVENESS_GRACE_MS). After Docker start() returns, Drydock waits this many milliseconds and then re-inspects the new container. If the container has already exited, the lifecycle throws and the existing rollback machinery takes over — catching containers that exit immediately after an update (bad command, broken entrypoint, missing dependency) that would otherwise be recorded as a successful update. Default 2000 ms. Set to 0 to disable the check entirely. Values between 1 and 99 ms are rejected at startup; the minimum non-zero value is 100 ms.

  • Recovery-boot concurrency cap (DD_UPDATE_RECOVERY_BOOT_CONCURRENCY). When Drydock restarts after a crash it finds queued update operations left from the previous run and resumes them. This variable bounds how many are dispatched in parallel during that recovery sweep. Default 4. Values of 0 are rejected at startup (minimum is 1).

  • Self-update now works when Drydock reaches the Docker daemon over a TCP host, not only through a bind-mounted /var/run/docker.sock (commit fc34ffb9). resolveHelperDockerConnection now inspects the watcher's Dockerode connection: a TCP host produces a TCP helper attached to Drydock's own Docker network. The bind-mounted-socket path is unchanged.

  • **The per-container Update button is locked with a Self-update unavailable indicator when Drydock cannot update itself in the current deployment (commit [`cf77728...

Read more

v1.5.0-rc.32

06 Jun 02:42
3ea42da

Choose a tag to compare

v1.5.0-rc.32 Pre-release
Pre-release

v1.5.0-rc.32

Full Changelog: v1.5.0-rc.31...v1.5.0-rc.32

[1.5.0-rc.32] — 2026-06-06

Added

  • Trigger environment variable taxonomy split — DD_ACTION_* and DD_NOTIFICATION_* prefixes. Action triggers (Docker, Docker Compose, Command) are now configured with DD_ACTION_* and dd.action.* labels; notification/messaging triggers (Slack, SMTP, Discord, Telegram, ntfy, Pushover, and all others) are configured with DD_NOTIFICATION_* and dd.notification.* labels. All three prefix families (DD_ACTION_*, DD_NOTIFICATION_*, DD_TRIGGER_*) are interchangeable at runtime — merge priority is DD_NOTIFICATION_* > DD_ACTION_* > DD_TRIGGER_*. A migration CLI (drydock config migrate --source trigger) rewrites DD_TRIGGER_*, dd.trigger.include, and dd.trigger.exclude to action-prefixed aliases automatically; use --dry-run to preview changes before applying.

  • Per-agent Home Assistant MQTT topic segmentation (DD_NOTIFICATION_MQTT_<name>_HASS_AGENTTOPICSEGMENT, default false). When enabled, Drydock inserts an agent/<name> segment into every Home Assistant MQTT topic — per-container state topics and watcher-level sensor topics — for containers owned by a remote agent, so two agents that both use the default watcher name local no longer publish to (and overwrite) the same topics. Enabling it also scopes the watcher-level sensor counts and the discovery-entity cleanup per agent, fixing the Home Assistant facet of #386. Controller-local container topics are unchanged. Because it changes the Home Assistant entity IDs for agent-owned containers, it is opt-in for the v1.5.x line and targeted to become the default in v1.7.0 — see Deprecated.

  • Up-to-date and pinned badges in Kind column — Containers table now shows a green check-circle badge ("Up to date") for containers at their latest version, and a green pin badge ("Pinned") for containers with skipped updates, replacing the previous dash placeholder.

  • Show/hide toggle on the login password field (commit e086c5bc). The sign-in password input now has an eye / eye-slash button to reveal or mask what was typed, with an accessible label and type="button" so it never submits the form.

  • Real-time container log viewer — WebSocket-based live log streaming from Docker containers directly in the UI. Features ANSI color rendering, automatic JSON log detection with syntax-highlighted pretty-printing, free-text and regex search with match navigation, stdout/stderr stream filtering, log level filtering for structured logs, copy to clipboard, and gzip-compressed download. Available in both the container detail panel and a dedicated full-page view at /containers/:id/logs. (Phase 4.2)

  • Diagnostic debug dump — One-click export of redacted system state from Configuration > Diagnostics. Collects runtime metadata, component state (watchers, registries, triggers, agents), Docker API diagnostics, MQTT Home Assistant sensors, recent Docker events, store stats, and DD_* environment variables. Sensitive values matching password|token|secret|key|hash are automatically redacted. Configurable time window (1–1440 minutes). (Phase 4.14)

  • Container log streaming APIWS /api/v1/containers/:id/logs/stream endpoint with Docker binary stream demultiplexing, session-based authentication on WebSocket upgrade, and fixed-window rate limiting (1,000 connections per 15 minutes).

  • Container log download APIGET /api/v1/containers/:id/logs endpoint with gzip compression support, stdout/stderr filtering, configurable tail size, and timestamp-based since filtering.

  • Debug dump APIGET /api/v1/debug/dump endpoint with configurable minutes query parameter for time-windowed event collection.

  • Dashboard customization — Customizable grid layout with drag-to-reorder, resize, and per-widget visibility toggles using grid-layout-plus. Edit mode via pencil icon in breadcrumb header. Customize panel with checkboxes and S/M/L size badges. All widgets progressively collapse content based on container height.

  • Resource usage dashboard widget — CPU and memory usage bars with top-N resource consumers, progressive detail at different widget sizes.

  • Fleet-aggregate stats subsystem (commits feature/v1.5-rc17). New ContainerStatsAggregator polls each locally-monitored container once per tick (default 10 s) and computes a fleet-wide ContainerStatsSummary (total CPU%, total memory, top-N rows). Two new endpoints — GET /api/v1/stats/summary and GET /api/v1/stats/summary/stream — expose the current snapshot and a live SSE feed; the dashboard Resource Usage widget now consumes the SSE stream directly, fixing the regression (introduced in rc.13 by the ?touch=false workaround) where the widget showed zeros because the per-container cache was never warmed. The legacy GET /api/v1/containers/stats endpoint and the client-side summarizeContainerResourceUsage rollup have been removed.

  • Per-container update locks (commit 761fb834). New keyed LockManager primitive in app/updates/lock-primitives.ts replaces the module-level pLimit(1) that was serialising every container update across the entire process. Lock keys are derived per container (and per compose project for Dockercompose), so two unrelated containers can now pull and recreate concurrently while two services in the same compose project still serialise correctly.

  • Restart recovery for queued and pulling updates (commit 00788b13). Startup reconciliation in app/store/update-operation.ts is now selective: status=queued operations stay queued for the recovery dispatcher to pick up, and phase=pulling rows are reset to queued (pull is idempotent). A new app/updates/recovery.ts module runs once after registry.init(), re-resolves trigger and container for each queued operation, and dispatches them through the existing fire-and-forget pipeline.

  • Notification outbox with retry and dead-letter queue (commits a9561d93, 7d2ef6eb, b215d295, ce26bece). New notificationOutbox LokiJS collection and app/notifications/outbox-worker.ts background worker provide durable retry semantics for notification dispatch. On failure, the delivery intent is persisted to the outbox and the worker retries on a periodic drain with exponential backoff + jitter. After a configurable number of failed attempts (default 5) entries transition to the dead-letter queue; delivered and dead-letter entries are auto-purged past TTL (default 30 days). New /api/notifications/outbox REST surface lets operators list entries, retry from the DLQ, or discard.

  • Notification outbox UI (commit feature/v1.5-rc17). New Notification outbox page (route /notifications/outbox, nav under Settings) with status tabs (Dead-letter / Pending / Delivered), retry and discard actions.

  • Cancel queued or in-flight updates (commits 4b79e3ac, 79487115). POST /api/operations/:id/cancel now accepts both queued and in-progress operations. Queued ops are marked failed immediately; in-progress ops are flagged via a cancelRequested field and the lifecycle observes the flag at three safe checkpoints.

  • Global concurrent-update cap (DD_UPDATE_MAX_CONCURRENT). New counting semaphore provides a configurable global gate on how many update lifecycles run simultaneously. Default 0 = unlimited. Positive integer N means at most N updates run concurrently. Self-update operations bypass the global cap.

  • Health-gate SSE heartbeat (DD_UPDATE_HEALTH_GATE_HEARTBEAT_MS). While drydock waits for a new container to pass its health gate, a periodic heartbeat re-emits phase: 'health-gate' at a configurable interval (default 10 s). DD_UPDATE_HEALTH_GATE_HEARTBEAT_MS=0 disables heartbeats; values below 1000 ms or non-integers fail fast at startup.

  • Post-start liveness grace window (DD_UPDATE_POST_START_LIVENESS_GRACE_MS). After Docker start() returns, Drydock waits this many milliseconds and then re-inspects the new container. If the container has already exited, the lifecycle throws and the existing rollback machinery takes over — catching containers that exit immediately after an update (bad command, broken entrypoint, missing dependency) that would otherwise be recorded as a successful update. Default 2000 ms. Set to 0 to disable the check entirely. Values between 1 and 99 ms are rejected at startup; the minimum non-zero value is 100 ms.

  • Recovery-boot concurrency cap (DD_UPDATE_RECOVERY_BOOT_CONCURRENCY). When Drydock restarts after a crash it finds queued update operations left from the previous run and resumes them. This variable bounds how many are dispatched in parallel during that recovery sweep. Default 4. Values of 0 are rejected at startup (minimum is 1).

  • Self-update now works when Drydock reaches the Docker daemon over a TCP host, not only through a bind-mounted /var/run/docker.sock (commit fc34ffb9). resolveHelperDockerConnection now inspects the watcher's Dockerode connection: a TCP host produces a TCP helper attached to Drydock's own Docker network. The bind-mounted-socket path is unchanged.

  • **The per-container Update button is locked with a Self-update unavailable indicator when Drydock cannot update itself in the current deployment (commit [`cf77728...

Read more

v1.5.0-rc.31

05 Jun 14:50
32864f7

Choose a tag to compare

v1.5.0-rc.31 Pre-release
Pre-release

v1.5.0-rc.31

Full Changelog: v1.5.0-rc.30...v1.5.0-rc.31

[1.5.0-rc.31] — 2026-06-05

Added

  • Trigger environment variable taxonomy split — DD_ACTION_* and DD_NOTIFICATION_* prefixes. Action triggers (Docker, Docker Compose, Command) are now configured with DD_ACTION_* and dd.action.* labels; notification/messaging triggers (Slack, SMTP, Discord, Telegram, ntfy, Pushover, and all others) are configured with DD_NOTIFICATION_* and dd.notification.* labels. All three prefix families (DD_ACTION_*, DD_NOTIFICATION_*, DD_TRIGGER_*) are interchangeable at runtime — merge priority is DD_NOTIFICATION_* > DD_ACTION_* > DD_TRIGGER_*. A migration CLI (drydock config migrate --source trigger) rewrites DD_TRIGGER_*, dd.trigger.include, and dd.trigger.exclude to action-prefixed aliases automatically; use --dry-run to preview changes before applying.

  • Per-agent Home Assistant MQTT topic segmentation (DD_NOTIFICATION_MQTT_<name>_HASS_AGENTTOPICSEGMENT, default false). When enabled, Drydock inserts an agent/<name> segment into every Home Assistant MQTT topic — per-container state topics and watcher-level sensor topics — for containers owned by a remote agent, so two agents that both use the default watcher name local no longer publish to (and overwrite) the same topics. Enabling it also scopes the watcher-level sensor counts and the discovery-entity cleanup per agent, fixing the Home Assistant facet of #386. Controller-local container topics are unchanged. Because it changes the Home Assistant entity IDs for agent-owned containers, it is opt-in for the v1.5.x line and targeted to become the default in v1.7.0 — see Deprecated.

  • Up-to-date and pinned badges in Kind column — Containers table now shows a green check-circle badge ("Up to date") for containers at their latest version, and a green pin badge ("Pinned") for containers with skipped updates, replacing the previous dash placeholder.

  • Show/hide toggle on the login password field (commit e086c5bc). The sign-in password input now has an eye / eye-slash button to reveal or mask what was typed, with an accessible label and type="button" so it never submits the form.

  • Real-time container log viewer — WebSocket-based live log streaming from Docker containers directly in the UI. Features ANSI color rendering, automatic JSON log detection with syntax-highlighted pretty-printing, free-text and regex search with match navigation, stdout/stderr stream filtering, log level filtering for structured logs, copy to clipboard, and gzip-compressed download. Available in both the container detail panel and a dedicated full-page view at /containers/:id/logs. (Phase 4.2)

  • Diagnostic debug dump — One-click export of redacted system state from Configuration > Diagnostics. Collects runtime metadata, component state (watchers, registries, triggers, agents), Docker API diagnostics, MQTT Home Assistant sensors, recent Docker events, store stats, and DD_* environment variables. Sensitive values matching password|token|secret|key|hash are automatically redacted. Configurable time window (1–1440 minutes). (Phase 4.14)

  • Container log streaming APIWS /api/v1/containers/:id/logs/stream endpoint with Docker binary stream demultiplexing, session-based authentication on WebSocket upgrade, and fixed-window rate limiting (1,000 connections per 15 minutes).

  • Container log download APIGET /api/v1/containers/:id/logs endpoint with gzip compression support, stdout/stderr filtering, configurable tail size, and timestamp-based since filtering.

  • Debug dump APIGET /api/v1/debug/dump endpoint with configurable minutes query parameter for time-windowed event collection.

  • Dashboard customization — Customizable grid layout with drag-to-reorder, resize, and per-widget visibility toggles using grid-layout-plus. Edit mode via pencil icon in breadcrumb header. Customize panel with checkboxes and S/M/L size badges. All widgets progressively collapse content based on container height.

  • Resource usage dashboard widget — CPU and memory usage bars with top-N resource consumers, progressive detail at different widget sizes.

  • Fleet-aggregate stats subsystem (commits feature/v1.5-rc17). New ContainerStatsAggregator polls each locally-monitored container once per tick (default 10 s) and computes a fleet-wide ContainerStatsSummary (total CPU%, total memory, top-N rows). Two new endpoints — GET /api/v1/stats/summary and GET /api/v1/stats/summary/stream — expose the current snapshot and a live SSE feed; the dashboard Resource Usage widget now consumes the SSE stream directly, fixing the regression (introduced in rc.13 by the ?touch=false workaround) where the widget showed zeros because the per-container cache was never warmed. The legacy GET /api/v1/containers/stats endpoint and the client-side summarizeContainerResourceUsage rollup have been removed.

  • Per-container update locks (commit 761fb834). New keyed LockManager primitive in app/updates/lock-primitives.ts replaces the module-level pLimit(1) that was serialising every container update across the entire process. Lock keys are derived per container (and per compose project for Dockercompose), so two unrelated containers can now pull and recreate concurrently while two services in the same compose project still serialise correctly.

  • Restart recovery for queued and pulling updates (commit 00788b13). Startup reconciliation in app/store/update-operation.ts is now selective: status=queued operations stay queued for the recovery dispatcher to pick up, and phase=pulling rows are reset to queued (pull is idempotent). A new app/updates/recovery.ts module runs once after registry.init(), re-resolves trigger and container for each queued operation, and dispatches them through the existing fire-and-forget pipeline.

  • Notification outbox with retry and dead-letter queue (commits a9561d93, 7d2ef6eb, b215d295, ce26bece). New notificationOutbox LokiJS collection and app/notifications/outbox-worker.ts background worker provide durable retry semantics for notification dispatch. On failure, the delivery intent is persisted to the outbox and the worker retries on a periodic drain with exponential backoff + jitter. After a configurable number of failed attempts (default 5) entries transition to the dead-letter queue; delivered and dead-letter entries are auto-purged past TTL (default 30 days). New /api/notifications/outbox REST surface lets operators list entries, retry from the DLQ, or discard.

  • Notification outbox UI (commit feature/v1.5-rc17). New Notification outbox page (route /notifications/outbox, nav under Settings) with status tabs (Dead-letter / Pending / Delivered), retry and discard actions.

  • Cancel queued or in-flight updates (commits 4b79e3ac, 79487115). POST /api/operations/:id/cancel now accepts both queued and in-progress operations. Queued ops are marked failed immediately; in-progress ops are flagged via a cancelRequested field and the lifecycle observes the flag at three safe checkpoints.

  • Global concurrent-update cap (DD_UPDATE_MAX_CONCURRENT). New counting semaphore provides a configurable global gate on how many update lifecycles run simultaneously. Default 0 = unlimited. Positive integer N means at most N updates run concurrently. Self-update operations bypass the global cap.

  • Health-gate SSE heartbeat (DD_UPDATE_HEALTH_GATE_HEARTBEAT_MS). While drydock waits for a new container to pass its health gate, a periodic heartbeat re-emits phase: 'health-gate' at a configurable interval (default 10 s). DD_UPDATE_HEALTH_GATE_HEARTBEAT_MS=0 disables heartbeats; values below 1000 ms or non-integers fail fast at startup.

  • Post-start liveness grace window (DD_UPDATE_POST_START_LIVENESS_GRACE_MS). After Docker start() returns, Drydock waits this many milliseconds and then re-inspects the new container. If the container has already exited, the lifecycle throws and the existing rollback machinery takes over — catching containers that exit immediately after an update (bad command, broken entrypoint, missing dependency) that would otherwise be recorded as a successful update. Default 2000 ms. Set to 0 to disable the check entirely. Values between 1 and 99 ms are rejected at startup; the minimum non-zero value is 100 ms.

  • Recovery-boot concurrency cap (DD_UPDATE_RECOVERY_BOOT_CONCURRENCY). When Drydock restarts after a crash it finds queued update operations left from the previous run and resumes them. This variable bounds how many are dispatched in parallel during that recovery sweep. Default 4. Values of 0 are rejected at startup (minimum is 1).

  • Self-update now works when Drydock reaches the Docker daemon over a TCP host, not only through a bind-mounted /var/run/docker.sock (commit fc34ffb9). resolveHelperDockerConnection now inspects the watcher's Dockerode connection: a TCP host produces a TCP helper attached to Drydock's own Docker network. The bind-mounted-socket path is unchanged.

  • **The per-container Update button is locked with a Self-update unavailable indicator when Drydock cannot update itself in the current deployment (commit [`cf77728...

Read more

v1.5.0-rc.30

05 Jun 01:11
14f6809

Choose a tag to compare

v1.5.0-rc.30 Pre-release
Pre-release

v1.5.0-rc.30

Full Changelog: v1.5.0-rc.29...v1.5.0-rc.30

[1.5.0-rc.30] — 2026-06-05

Added

  • Trigger environment variable taxonomy split — DD_ACTION_* and DD_NOTIFICATION_* prefixes. Action triggers (Docker, Docker Compose, Command) are now configured with DD_ACTION_* and dd.action.* labels; notification/messaging triggers (Slack, SMTP, Discord, Telegram, ntfy, Pushover, and all others) are configured with DD_NOTIFICATION_* and dd.notification.* labels. All three prefix families (DD_ACTION_*, DD_NOTIFICATION_*, DD_TRIGGER_*) are interchangeable at runtime — merge priority is DD_NOTIFICATION_* > DD_ACTION_* > DD_TRIGGER_*. A migration CLI (drydock config migrate --source trigger) rewrites DD_TRIGGER_*, dd.trigger.include, and dd.trigger.exclude to action-prefixed aliases automatically; use --dry-run to preview changes before applying.

  • Per-agent Home Assistant MQTT topic segmentation (DD_NOTIFICATION_MQTT_<name>_HASS_AGENTTOPICSEGMENT, default false). When enabled, Drydock inserts an agent/<name> segment into every Home Assistant MQTT topic — per-container state topics and watcher-level sensor topics — for containers owned by a remote agent, so two agents that both use the default watcher name local no longer publish to (and overwrite) the same topics. Enabling it also scopes the watcher-level sensor counts and the discovery-entity cleanup per agent, fixing the Home Assistant facet of #386. Controller-local container topics are unchanged. Because it changes the Home Assistant entity IDs for agent-owned containers, it is opt-in for the v1.5.x line and targeted to become the default in v1.7.0 — see Deprecated.

  • Up-to-date and pinned badges in Kind column — Containers table now shows a green check-circle badge ("Up to date") for containers at their latest version, and a green pin badge ("Pinned") for containers with skipped updates, replacing the previous dash placeholder.

  • Show/hide toggle on the login password field (commit e086c5bc). The sign-in password input now has an eye / eye-slash button to reveal or mask what was typed, with an accessible label and type="button" so it never submits the form.

  • Real-time container log viewer — WebSocket-based live log streaming from Docker containers directly in the UI. Features ANSI color rendering, automatic JSON log detection with syntax-highlighted pretty-printing, free-text and regex search with match navigation, stdout/stderr stream filtering, log level filtering for structured logs, copy to clipboard, and gzip-compressed download. Available in both the container detail panel and a dedicated full-page view at /containers/:id/logs. (Phase 4.2)

  • Diagnostic debug dump — One-click export of redacted system state from Configuration > Diagnostics. Collects runtime metadata, component state (watchers, registries, triggers, agents), Docker API diagnostics, MQTT Home Assistant sensors, recent Docker events, store stats, and DD_* environment variables. Sensitive values matching password|token|secret|key|hash are automatically redacted. Configurable time window (1–1440 minutes). (Phase 4.14)

  • Container log streaming APIWS /api/v1/containers/:id/logs/stream endpoint with Docker binary stream demultiplexing, session-based authentication on WebSocket upgrade, and fixed-window rate limiting (1,000 connections per 15 minutes).

  • Container log download APIGET /api/v1/containers/:id/logs endpoint with gzip compression support, stdout/stderr filtering, configurable tail size, and timestamp-based since filtering.

  • Debug dump APIGET /api/v1/debug/dump endpoint with configurable minutes query parameter for time-windowed event collection.

  • Dashboard customization — Customizable grid layout with drag-to-reorder, resize, and per-widget visibility toggles using grid-layout-plus. Edit mode via pencil icon in breadcrumb header. Customize panel with checkboxes and S/M/L size badges. All widgets progressively collapse content based on container height.

  • Resource usage dashboard widget — CPU and memory usage bars with top-N resource consumers, progressive detail at different widget sizes.

  • Fleet-aggregate stats subsystem (commits feature/v1.5-rc17). New ContainerStatsAggregator polls each locally-monitored container once per tick (default 10 s) and computes a fleet-wide ContainerStatsSummary (total CPU%, total memory, top-N rows). Two new endpoints — GET /api/v1/stats/summary and GET /api/v1/stats/summary/stream — expose the current snapshot and a live SSE feed; the dashboard Resource Usage widget now consumes the SSE stream directly, fixing the regression (introduced in rc.13 by the ?touch=false workaround) where the widget showed zeros because the per-container cache was never warmed. The legacy GET /api/v1/containers/stats endpoint and the client-side summarizeContainerResourceUsage rollup have been removed.

  • Per-container update locks (commit 761fb834). New keyed LockManager primitive in app/updates/lock-primitives.ts replaces the module-level pLimit(1) that was serialising every container update across the entire process. Lock keys are derived per container (and per compose project for Dockercompose), so two unrelated containers can now pull and recreate concurrently while two services in the same compose project still serialise correctly.

  • Restart recovery for queued and pulling updates (commit 00788b13). Startup reconciliation in app/store/update-operation.ts is now selective: status=queued operations stay queued for the recovery dispatcher to pick up, and phase=pulling rows are reset to queued (pull is idempotent). A new app/updates/recovery.ts module runs once after registry.init(), re-resolves trigger and container for each queued operation, and dispatches them through the existing fire-and-forget pipeline.

  • Notification outbox with retry and dead-letter queue (commits a9561d93, 7d2ef6eb, b215d295, ce26bece). New notificationOutbox LokiJS collection and app/notifications/outbox-worker.ts background worker provide durable retry semantics for notification dispatch. On failure, the delivery intent is persisted to the outbox and the worker retries on a periodic drain with exponential backoff + jitter. After a configurable number of failed attempts (default 5) entries transition to the dead-letter queue; delivered and dead-letter entries are auto-purged past TTL (default 30 days). New /api/notifications/outbox REST surface lets operators list entries, retry from the DLQ, or discard.

  • Notification outbox UI (commit feature/v1.5-rc17). New Notification outbox page (route /notifications/outbox, nav under Settings) with status tabs (Dead-letter / Pending / Delivered), retry and discard actions.

  • Cancel queued or in-flight updates (commits 4b79e3ac, 79487115). POST /api/operations/:id/cancel now accepts both queued and in-progress operations. Queued ops are marked failed immediately; in-progress ops are flagged via a cancelRequested field and the lifecycle observes the flag at three safe checkpoints.

  • Global concurrent-update cap (DD_UPDATE_MAX_CONCURRENT). New counting semaphore provides a configurable global gate on how many update lifecycles run simultaneously. Default 0 = unlimited. Positive integer N means at most N updates run concurrently. Self-update operations bypass the global cap.

  • Health-gate SSE heartbeat (DD_UPDATE_HEALTH_GATE_HEARTBEAT_MS). While drydock waits for a new container to pass its health gate, a periodic heartbeat re-emits phase: 'health-gate' at a configurable interval (default 10 s). DD_UPDATE_HEALTH_GATE_HEARTBEAT_MS=0 disables heartbeats; values below 1000 ms or non-integers fail fast at startup.

  • Post-start liveness grace window (DD_UPDATE_POST_START_LIVENESS_GRACE_MS). After Docker start() returns, Drydock waits this many milliseconds and then re-inspects the new container. If the container has already exited, the lifecycle throws and the existing rollback machinery takes over — catching containers that exit immediately after an update (bad command, broken entrypoint, missing dependency) that would otherwise be recorded as a successful update. Default 2000 ms. Set to 0 to disable the check entirely. Values between 1 and 99 ms are rejected at startup; the minimum non-zero value is 100 ms.

  • Recovery-boot concurrency cap (DD_UPDATE_RECOVERY_BOOT_CONCURRENCY). When Drydock restarts after a crash it finds queued update operations left from the previous run and resumes them. This variable bounds how many are dispatched in parallel during that recovery sweep. Default 4. Values of 0 are rejected at startup (minimum is 1).

  • Self-update now works when Drydock reaches the Docker daemon over a TCP host, not only through a bind-mounted /var/run/docker.sock (commit fc34ffb9). resolveHelperDockerConnection now inspects the watcher's Dockerode connection: a TCP host produces a TCP helper attached to Drydock's own Docker network. The bind-mounted-socket path is unchanged.

  • **The per-container Update button is locked with a Self-update unavailable indicator when Drydock cannot update itself in the current deployment (commit [`cf77728...

Read more

v1.5.0-rc.29

31 May 03:05
ed558e6

Choose a tag to compare

v1.5.0-rc.29 Pre-release
Pre-release

v1.5.0-rc.29

Full Changelog: v1.5.0-rc.28...v1.5.0-rc.29

[1.5.0-rc.29] — 2026-05-31

Added

  • Trigger environment variable taxonomy split — DD_ACTION_* and DD_NOTIFICATION_* prefixes. Action triggers (Docker, Docker Compose, Command) are now configured with DD_ACTION_* and dd.action.* labels; notification/messaging triggers (Slack, SMTP, Discord, Telegram, ntfy, Pushover, and all others) are configured with DD_NOTIFICATION_* and dd.notification.* labels. All three prefix families (DD_ACTION_*, DD_NOTIFICATION_*, DD_TRIGGER_*) are interchangeable at runtime — merge priority is DD_NOTIFICATION_* > DD_ACTION_* > DD_TRIGGER_*. A migration CLI (drydock config migrate --source trigger) rewrites DD_TRIGGER_*, dd.trigger.include, and dd.trigger.exclude to action-prefixed aliases automatically; use --dry-run to preview changes before applying.

  • Up-to-date and pinned badges in Kind column — Containers table now shows a green check-circle badge ("Up to date") for containers at their latest version, and a green pin badge ("Pinned") for containers with skipped updates, replacing the previous dash placeholder.

  • Real-time container log viewer — WebSocket-based live log streaming from Docker containers directly in the UI. Features ANSI color rendering, automatic JSON log detection with syntax-highlighted pretty-printing, free-text and regex search with match navigation, stdout/stderr stream filtering, log level filtering for structured logs, copy to clipboard, and gzip-compressed download. Available in both the container detail panel and a dedicated full-page view at /containers/:id/logs. (Phase 4.2)

  • Diagnostic debug dump — One-click export of redacted system state from Configuration > Diagnostics. Collects runtime metadata, component state (watchers, registries, triggers, agents), Docker API diagnostics, MQTT Home Assistant sensors, recent Docker events, store stats, and DD_* environment variables. Sensitive values matching password|token|secret|key|hash are automatically redacted. Configurable time window (1–1440 minutes). (Phase 4.14)

  • Container log streaming APIWS /api/v1/containers/:id/logs/stream endpoint with Docker binary stream demultiplexing, session-based authentication on WebSocket upgrade, and fixed-window rate limiting (1,000 connections per 15 minutes).

  • Container log download APIGET /api/v1/containers/:id/logs endpoint with gzip compression support, stdout/stderr filtering, configurable tail size, and timestamp-based since filtering.

  • Debug dump APIGET /api/v1/debug/dump endpoint with configurable minutes query parameter for time-windowed event collection.

  • Dashboard customization — Customizable grid layout with drag-to-reorder, resize, and per-widget visibility toggles using grid-layout-plus. Edit mode via pencil icon in breadcrumb header. Customize panel with checkboxes and S/M/L size badges. All widgets progressively collapse content based on container height.

  • Resource usage dashboard widget — CPU and memory usage bars with top-N resource consumers, progressive detail at different widget sizes.

  • Fleet-aggregate stats subsystem (commits feature/v1.5-rc17). New ContainerStatsAggregator polls each locally-monitored container once per tick (default 10 s) and computes a fleet-wide ContainerStatsSummary (total CPU%, total memory, top-N rows). Two new endpoints — GET /api/v1/stats/summary and GET /api/v1/stats/summary/stream — expose the current snapshot and a live SSE feed; the dashboard Resource Usage widget now consumes the SSE stream directly, fixing the regression (introduced in rc.13 by the ?touch=false workaround) where the widget showed zeros because the per-container cache was never warmed. The legacy GET /api/v1/containers/stats endpoint and the client-side summarizeContainerResourceUsage rollup have been removed.

  • Per-container update locks (commit 761fb834). New keyed LockManager primitive in app/updates/lock-primitives.ts replaces the module-level pLimit(1) that was serialising every container update across the entire process. Lock keys are derived per container (and per compose project for Dockercompose), so two unrelated containers can now pull and recreate concurrently while two services in the same compose project still serialise correctly.

  • Restart recovery for queued and pulling updates (commit 00788b13). Startup reconciliation in app/store/update-operation.ts is now selective: status=queued operations stay queued for the recovery dispatcher to pick up, and phase=pulling rows are reset to queued (pull is idempotent). A new app/updates/recovery.ts module runs once after registry.init(), re-resolves trigger and container for each queued operation, and dispatches them through the existing fire-and-forget pipeline.

  • Notification outbox with retry and dead-letter queue (commits a9561d93, 7d2ef6eb, b215d295, ce26bece). New notificationOutbox LokiJS collection and app/notifications/outbox-worker.ts background worker provide durable retry semantics for notification dispatch. On failure, the delivery intent is persisted to the outbox and the worker retries on a periodic drain with exponential backoff + jitter. After a configurable number of failed attempts (default 5) entries transition to the dead-letter queue; delivered and dead-letter entries are auto-purged past TTL (default 30 days). New /api/notifications/outbox REST surface lets operators list entries, retry from the DLQ, or discard.

  • Notification outbox UI (commit feature/v1.5-rc17). New Notification outbox page (route /notifications/outbox, nav under Settings) with status tabs (Dead-letter / Pending / Delivered), retry and discard actions.

  • Cancel queued or in-flight updates (commits 4b79e3ac, 79487115). POST /api/operations/:id/cancel now accepts both queued and in-progress operations. Queued ops are marked failed immediately; in-progress ops are flagged via a cancelRequested field and the lifecycle observes the flag at three safe checkpoints.

  • Global concurrent-update cap (DD_UPDATE_MAX_CONCURRENT). New counting semaphore provides a configurable global gate on how many update lifecycles run simultaneously. Default 0 = unlimited. Positive integer N means at most N updates run concurrently. Self-update operations bypass the global cap.

  • Health-gate SSE heartbeat (DD_UPDATE_HEALTH_GATE_HEARTBEAT_MS). While drydock waits for a new container to pass its health gate, a periodic heartbeat re-emits phase: 'health-gate' at a configurable interval (default 10 s). DD_UPDATE_HEALTH_GATE_HEARTBEAT_MS=0 disables heartbeats; values below 1000 ms or non-integers fail fast at startup.

  • Self-update now works when Drydock reaches the Docker daemon over a TCP host, not only through a bind-mounted /var/run/docker.sock (commit fc34ffb9). resolveHelperDockerConnection now inspects the watcher's Dockerode connection: a TCP host produces a TCP helper attached to Drydock's own Docker network. The bind-mounted-socket path is unchanged.

  • The per-container Update button is locked with a Self-update unavailable indicator when Drydock cannot update itself in the current deployment (commit cf777280). A new hard self-update-unavailable update-eligibility blocker is raised when self-update cannot run over either a bind-mounted socket or a TCP host.

  • i18n coverage extended to the remaining hardcoded UI strings across 28 components (discussion #329). All 16 non-English locales now have full key parity with the English source. 17 locales ship in the picker: de, es, fr, it, nl, pl, pt-BR, tr, zh-CN, zh-TW, ar, ja, ko, ru, uk, vi, plus English.

  • DD_AGENT_ALLOW_INSECURE_SECRET escape hatch for closed-LAN deployments. rc.20 tightened the agent-secret-over-HTTP check to a hard error. rc.21 introduces DD_AGENT_ALLOW_INSECURE_SECRET=true as an explicit controller-side opt-in for environments where the operator accepts that the agent secret travels in cleartext. Default behavior is unchanged.

  • Security scan digest mode. Every scan cycle now carries a stable cycleId (UUID v7) and emits a security-scan-cycle-complete event. Triggers can configure SECURITYMODE=digest (or batch+digest) to receive one summary per cycle. Templates are customizable via SECURITYDIGESTTITLE / SECURITYDIGESTBODY. (#300)

  • Opt-in scheduled-scan notifications — New DD_SECURITY_SCAN_NOTIFICATIONS=true flag enables security-alert event emission from scheduled scans. Default is false; on-demand scans always emit.

  • Bulk security scan endpointPOST /api/v1/containers/scan-all scans all (or a filtered subset of) watched containers server-side, streams per-container progress over the existing scan SSE channel, and honors client-disconnect aborts. Rate-limited to 1 request / 60s per IP (authenticated-admin bypass).

  • SSE Last-Event-ID replay (#289) — The server stamps every broadcast event with a monotonic <bootId>:<counter> id and retains a 5-minute time-bounded ring buffer. Clients reconnecting with a `...

Read more