Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- **Portwing WS connections are now proactively closed when an agent stops answering pings.** The server sends a ping every 30s and expects a pong in return; an agent that misses two consecutive cycles (60s, matching portwing's own `readDeadline`) is treated as dead and the connection is force-closed, freeing the agent slot immediately instead of leaving a zombie connection registered until the underlying transport eventually notices. (#470)

- **Bidirectional MQTT: Home Assistant can now trigger updates back, not just observe them.** Setting `DD_NOTIFICATION_MQTT_{trigger_name}_HASS_COMMANDS=true` adds a `command_topic` to each container's discovery payload, so the update entity's Install button in Home Assistant becomes clickable. Clicking it publishes to a per-container command topic that drydock subscribes to, which dispatches through the same update path the `/update/:containerName` webhook already uses, so all existing eligibility checks, trigger resolution, and agent routing apply unchanged. Commands are rate-limited per container (one accepted click every 30s) and every outcome (accepted, rejected, unexpected error) is recorded in the audit log under the new `mqtt-command-update` action. (#210)
- **Bidirectional MQTT: Home Assistant can now trigger updates back, not just observe them.** Setting `DD_NOTIFICATION_MQTT_{trigger_name}_HASS_COMMANDS=true` adds a `command_topic` to each container's discovery payload, so the update entity's Install button in Home Assistant becomes clickable. Clicking it publishes to a per-container command topic that drydock subscribes to, which dispatches through the same update path the `/update/:containerName` webhook already uses, so all existing eligibility checks, trigger resolution, and agent routing apply unchanged. Commands are rate-limited per container (one accepted click every 30s) and every outcome (accepted, rejected, unexpected error) is recorded in the audit log under the new `mqtt-command-update` action. ([Discussion #210](https://github.com/CodesWhat/drydock/discussions/210))

- **Edge agents can choose their own display name via `hello.agentName`.** Previously every edge agent was named `portwing-edge-<agentId>` unconditionally. A supplied name is now sanitized to a safe slug (lowercase, alphanumeric + hyphen, max 63 chars) and used as the registry/display name, falling back to the old `portwing-edge-<agentId>` form when absent or empty. The name is bound to the authenticating Ed25519 key on first use β€” a later `hello` reusing the same name is only admitted under the same key (rejected with an `agent-name-claimed` error otherwise), and the binding is released when its owning key is revoked β€” so one registered key can no longer squat or steal another agent's chosen name. The binding is persisted (a `name-bindings` store collection, reloaded into memory on startup) so this guarantee holds across a server restart too, not just for the lifetime of one process. (#470)

Expand All @@ -52,6 +52,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- **Health-status event notifications.** A new `container-unhealthy` notification rule fires your existing notification triggers (Slack, SMTP, webhook, etc.) the moment a Docker health check enters the `unhealthy` state β€” detected on both the per-event Docker `health_status` listener (near-instant) and the 6-hour cron fallback, for a container that has previously been observed in a non-`unhealthy` state within the same instantiation. Never fires for a container with no `HEALTHCHECK` configured, for a container discovered already unhealthy with no prior baseline, or repeatedly while a container stays continuously unhealthy β€” but does fire again after a fast restart/crash-loop resets the container's boot timestamp, even if the health status reads `unhealthy` on both observations. Disabled by default, matching the `agent-disconnect`/`agent-reconnect` precedent; enable the `container-unhealthy` rule and assign triggers (or leave the trigger list empty to fire to every notification trigger) to receive it. No recovery/"healthy again" companion event, no `dd.autoheal*` label, and no corrective action ship with this β€” the full auto-heal loop stays a later, separately-scoped feature. ([Discussion #198](https://github.com/CodesWhat/drydock/discussions/198))

- **Column show/hide picker now covers every major data view.** Audit, Security, Servers, and Watchers gain the same per-view column show/hide picker (`DataTableColumnPicker`, backed by the `useViewColumnVisibility` composable) that Containers and Agents already had, so every one of those six views can hide low-priority columns and have the choice persist per view.

### Changed

- **Registry requests now retry transient network failures.** Response-less network errors β€” timeouts (`ECONNABORTED`/`ETIMEDOUT`), connection resets (`ECONNRESET`), and temporary DNS failures (`EAI_AGAIN`) β€” are retried up to twice with exponential backoff before a watch error is recorded, complementing the existing `429`/`503` Retry-After handling. A brief registry blip no longer marks affected containers as errored until the next hourly cycle.
Expand All @@ -64,12 +66,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- **The shared application log viewer stays responsive with large histories.** JSON highlighting now uses the existing bounded tokenizer cache, and collections above 200 entries render through a measured, overscanned virtual window while preserving search navigation, wrapping, line numbers, auto-scroll, and live row-height changes.

- **CI dependency metadata is cleaner.** The Playwright workflow comment now matches its actual Chromium-only matrix ([PR #486](https://github.com/CodesWhat/drydock/pull/486)), and the unused `@types/node-cron` package was removed from the backend workspace ([PR #489](https://github.com/CodesWhat/drydock/pull/489)). Both fixes are applied directly on `dev/v1.6`; the Renovate PRs remain open until default-branch convergence.
- **CI dependency metadata is cleaner.** The Playwright workflow comment now matches its actual Chromium-only matrix ([PR #486](https://github.com/CodesWhat/drydock/pull/486)), and the unused `@types/node-cron` package was removed from the backend workspace. Both fixes are applied directly on `dev/v1.6`; the Renovate PRs remain open until default-branch convergence.

- **Base image bumped to Alpine 3.24.** The `healthcheck-build` stage in the `Dockerfile` moves from `alpine:3.21` to `alpine:3.24`.

- **Every list view toggles between table and cards, and the choice sticks per view.** The v1.6 UI refactor rebuilt all list views on a shared `DataTable`, and each filter bar's view switch is now table⇄cards across Containers, Agents, Notifications, Security, Triggers, Watchers, Servers, Registries, Audit, and Auth. The selected mode is persisted per view; below ~640px the layout automatically reflows to cards and the now-redundant toggle is hidden. The old three-way `list` (accordion) mode has been removed β€” it's table or cards.

- **Card view gained a sort control.** Containers, Agents, and Security now render a sort-field and ascending/descending control (`DataSortControl`) in the filter bar when in card mode β€” the pre-v1.6 card grid (`DataCardGrid`, since removed) had no sorting of its own, so cards showed whatever order the table view happened to produce.

- **Container resource shortcuts are now consistent across views ([Discussion #295](https://github.com/CodesWhat/drydock/discussions/295)).** The existing source-project and release-note capabilities now render through one Source β†’ Release notes β†’ Registry toolbar in container table/cards/details, Security table/cards/detail, and Dashboard Recent Updates. The Containers table has a required Resources column separate from lifecycle Actions; cards and compact layouts wrap resources onto their own row. Every resource target is 44Γ—44 px, and registry opens the filtered internal Registries view instead of a raw OCI API endpoint. Release-note dialogs stay inside the viewport, contain touch scrolling and keyboard focus, reset when the selected row changes, allow only one open dialog, and restore focus when dismissed with Escape or Close. The removed accordion `list` mode is not a current target.

- **`dd.action.*` and `dd.notification.*` trigger labels are now strictly category-scoped.** A container's `dd.action.include`/`dd.action.exclude` labels now gate **only** action triggers (`docker`, `dockercompose`, `command`); `dd.notification.include`/`dd.notification.exclude` gate **only** notification triggers. Previously, whichever of the two was set on a container silently won for both categories, so a lone `dd.action.include` also filtered notification triggers (and vice versa) β€” see the Fixed entry below. The deprecated `dd.trigger.include`/`dd.trigger.exclude` labels still apply to both categories as a shared fallback beneath the scoped labels, unchanged. See the Upgrade Notes entry below before upgrading if you rely on a single scoped label to gate both trigger categories.
Expand Down Expand Up @@ -120,6 +124,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- **A stalled auth bootstrap request no longer leaves the app blank indefinitely.** The `/auth/user` request times out after eight seconds and falls through to the existing logged-out redirect path.

- **Authenticated session checks no longer trip the shared auth rate limiter.** `GET /auth/user` calls from an already-authenticated session are now exempt from the shared public auth limiter (100 requests/15 min); normal signed-in navigation could exhaust that budget through repeated re-reads and start returning spurious `429`s even though the backend was healthy. Every other auth route β€” including unauthenticated `/auth/user` checks, status discovery, login, remember, logout, and mutations β€” remains rate-limited as before. ([PR #526](https://github.com/CodesWhat/drydock/pull/526))

- **Old preference schemas no longer skip intermediate migrations.** Schema-v3 data now advances through each numbered migration, so later additions such as the `softwareVersion` column are applied before reaching the current schema.

- **Open tabs recover from stale lazy-loaded chunks after an upgrade.** Vite preload errors and matching Vue Router dynamic-import failures request one guarded page reload; successful navigation clears the session guard so a future upgrade can recover independently.
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ Fully interactive β€” real UI, mock data, no install required. Runs entirely in-

Container images drift out of date silently. A base image patches a CVE, an app cuts a release, a tag moves. Unless you're watching every registry by hand, your running containers fall behind until something breaks or gets exploited.

Most tools force a tradeoff. The auto-updaters (Watchtower, Ouroboros) pull and restart with little visibility or control, and are now largely unmaintained. The dashboards (Portainer) manage containers but aren't built for update intelligence. Drydock is **monitor-first**: it watches 23 registries and tells you exactly what changed (major, minor, patch, or digest) before anything happens, then acts only when you let it. And it goes further than any of them. Trivy vulnerability scanning blocks unsafe updates, cosign verifies signatures, pre-update image backups roll back automatically on health-check failure, distributed agents cover remote hosts, and 20 notification and action integrations close the loop. The full update lifecycle, with a web UI and a REST API.
Most tools force a tradeoff. The auto-updaters (Watchtower, Ouroboros) pull and restart with little visibility or control, and are now largely unmaintained. The dashboards (Portainer) manage containers but aren't built for update intelligence. Drydock is **monitor-first**: it watches 23 registries and tells you exactly what changed (major, minor, patch, or digest) before anything happens, then acts only when you let it. And it goes further than any of them. Trivy/Grype vulnerability scanning blocks unsafe updates, cosign verifies signatures, pre-update image backups roll back automatically on health-check failure, distributed agents cover remote hosts, and 20 notification and action integrations close the loop. The full update lifecycle, with a web UI and a REST API.

<hr>

Expand All @@ -244,7 +244,7 @@ Most tools force a tradeoff. The auto-updaters (Watchtower, Ouroboros) pull and
| πŸ”­ | **Monitor-First Detection** | Watches every running container and classifies each available update as major, minor, patch, or digest before anything happens. Nothing changes until you say so. |
| πŸ“¦ | **23 Registry Providers** | Docker Hub, GHCR, ECR, ACR, GCR, GAR, GitLab, Quay, Harbor, Artifactory, Nexus, and 12 more. Public and private, cloud and self-hosted, with per-registry TLS and auth. |
| πŸ”” | **20 Triggers** | 17 notification channels (Slack, Discord, Telegram, Teams, SMTP, MQTT, ntfy, and more) plus Docker, Docker Compose, and Command actions, with per-event/provider templates, live preview, threshold filtering, and batch mode. |
| πŸ₯Š | **Update Bouncer** | Trivy vulnerability scanning blocks unsafe updates before they deploy, with cosign signature verification and SBOM generation (CycloneDX and SPDX). |
| πŸ₯Š | **Update Bouncer** | Trivy/Grype vulnerability scanning blocks unsafe updates before they deploy, with cosign signature verification and SBOM generation (CycloneDX and SPDX). |
| ↩️ | **Image Backup & Auto Rollback** | Pre-update image snapshots with configurable retention, automatic rollback on health-check failure, and one-click manual rollback from the UI. |
| πŸͺ | **Lifecycle Hooks** | Pre and post-update shell commands via container labels, with per-hook timeouts and abort-on-failure control. |
| πŸ—‚οΈ | **Docker Compose Updates** | Pull and recreate Compose services through the Docker Engine API with YAML-preserving image patching. |
Expand Down Expand Up @@ -279,7 +279,7 @@ Anonymous (opt-in via `DD_ANONYMOUS_AUTH_CONFIRM=true`) Β· Basic (username + pas

### πŸ₯Š Update Bouncer

Trivy-powered vulnerability scanning blocks unsafe updates before they deploy. Includes cosign signature verification and SBOM generation (CycloneDX & SPDX).
Trivy- or Grype-powered vulnerability scanning blocks unsafe updates before they deploy. Includes cosign signature verification and SBOM generation (CycloneDX & SPDX).

<hr>

Expand Down Expand Up @@ -318,7 +318,7 @@ Trivy-powered vulnerability scanning blocks unsafe updates before they deploy. I
<tr><td>Container start/stop/restart/update</td><td align="center">βœ…</td><td align="center">❌</td><td align="center">❌</td><td align="center">❌</td><td align="center">❌</td></tr>
<tr><td>Distributed agents (remote)</td><td align="center">βœ…</td><td align="center">❌</td><td align="center">βœ…</td><td align="center">⚠️</td><td align="center">❌</td></tr>
<tr><td>Audit log</td><td align="center">βœ…</td><td align="center">❌</td><td align="center">❌</td><td align="center">❌</td><td align="center">❌</td></tr>
<tr><td>Security scanning (Trivy)</td><td align="center">βœ…</td><td align="center">❌</td><td align="center">❌</td><td align="center">❌</td><td align="center">❌</td></tr>
<tr><td>Security scanning (Trivy/Grype)</td><td align="center">βœ…</td><td align="center">❌</td><td align="center">❌</td><td align="center">❌</td><td align="center">❌</td></tr>
<tr><td>Semver-aware updates</td><td align="center">βœ…</td><td align="center">βœ…</td><td align="center">βœ…</td><td align="center">❌</td><td align="center">❌</td></tr>
<tr><td>Digest watching</td><td align="center">βœ…</td><td align="center">βœ…</td><td align="center">βœ…</td><td align="center">βœ…</td><td align="center">βœ…</td></tr>
<tr><td>Multi-arch (amd64/arm64)</td><td align="center">βœ…</td><td align="center">βœ…</td><td align="center">βœ…</td><td align="center">βœ…</td><td align="center">βœ…</td></tr>
Expand All @@ -339,7 +339,7 @@ Trivy-powered vulnerability scanning blocks unsafe updates before they deploy. I
<details>
<summary><strong>Migrating from WUD (What's Up Docker?)</strong></summary>

Drydock v1.6 no longer loads `WUD_*` environment variables or `wud.*` labels at runtime. Rewrite them before starting the upgraded service; persisted state still migrates automatically. Use `config migrate --dry-run` to preview, then `config migrate --file .env --file compose.yaml` to rewrite configuration to `DD_*` and `dd.*` naming.
Drydock v1.6 no longer loads `WUD_*` environment variables or `wud.*` labels at runtime. Rewrite them before starting the upgraded service; persisted state still migrates automatically. Use `docker exec -it drydock node dist/index.js config migrate --dry-run` to preview, then `docker exec -it drydock node dist/index.js config migrate --file .env --file compose.yaml` to rewrite configuration to `DD_*` and `dd.*` naming.

</details>

Expand Down
Loading