diff --git a/CHANGELOG.md b/CHANGELOG.md index 970c7b7f1..22ed1ef8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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-` 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-` 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) @@ -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. @@ -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. @@ -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. diff --git a/README.md b/README.md index ef436d0c9..c3741b022 100644 --- a/README.md +++ b/README.md @@ -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.
@@ -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. | @@ -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).
@@ -318,7 +318,7 @@ Trivy-powered vulnerability scanning blocks unsafe updates before they deploy. I Container start/stop/restart/update✅❌❌❌❌ Distributed agents (remote)✅❌✅⚠️❌ Audit log✅❌❌❌❌ -Security scanning (Trivy)✅❌❌❌❌ +Security scanning (Trivy/Grype)✅❌❌❌❌ Semver-aware updates✅✅✅❌❌ Digest watching✅✅✅✅✅ Multi-arch (amd64/arm64)✅✅✅✅✅ @@ -339,7 +339,7 @@ Trivy-powered vulnerability scanning blocks unsafe updates before they deploy. I
Migrating from WUD (What's Up Docker?) -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.
diff --git a/apps/web/src/app/data/faq.ts b/apps/web/src/app/data/faq.ts index 3b2a05b64..d70cc582c 100644 --- a/apps/web/src/app/data/faq.ts +++ b/apps/web/src/app/data/faq.ts @@ -7,7 +7,7 @@ export const faqItems: Array<{ question: string; answer: string }> = [ { question: "Which container registries does Drydock support?", answer: - "Drydock ships with 23 registry providers: Docker Hub, GitHub Container Registry (GHCR), Amazon ECR, Google Container Registry (GCR), Google Artifact Registry (GAR), GitLab Registry, Red Hat Quay, LinuxServer Container Registry (LSCR), Azure Container Registry (ACR), Harbor, JFrog Artifactory, Sonatype Nexus, Gitea, Forgejo, Portus, and more — plus a generic private-registry provider for anything that speaks HTTP Basic or bearer token auth. TLS customization (CA file, insecure skip-verify) is available on every provider.", + "Drydock ships with 23 registry providers: Docker Hub, GitHub Container Registry (GHCR), Amazon ECR, Google Container Registry (GCR), Google Artifact Registry (GAR), GitLab Registry, Red Hat Quay, LinuxServer Container Registry (LSCR), Azure Container Registry (ACR), Harbor, JFrog Artifactory, Sonatype Nexus, Gitea, Forgejo, Codeberg, and more — plus a generic private-registry provider for anything that speaks HTTP Basic or bearer token auth. TLS customization (CA file, insecure skip-verify) is available on every provider.", }, { question: "What is the difference between a notification and an action trigger?", @@ -17,7 +17,7 @@ export const faqItems: Array<{ question: string; answer: string }> = [ { question: "How do I control which containers Drydock watches and which tags it considers?", answer: - "Add Docker labels to your containers. dd.watch=true opts a container in (or set DD_WATCHER_LOCAL_ALLCONTAINERS=true to watch everything by default). Use dd.tag.include with a regex to restrict which tags are eligible — for example dd.tag.include=^\\d+\\.\\d+\\.\\d+$ to match only semver tags. dd.tag.exclude filters out tags that match. dd.tag.transform applies a rewrite to the tag string before matching. dd.display.name overrides the container name shown in the dashboard, and dd.group groups containers into collapsible stacks.", + "Drydock watches every container by default. Add dd.watch=false to opt a container out, or set DD_WATCHER_{name}_WATCHBYDEFAULT=false to flip that watcher to opt-in mode, where only containers labeled dd.watch=true are watched. Use dd.tag.include with a regex to restrict which tags are eligible — for example dd.tag.include=^\\d+\\.\\d+\\.\\d+$ to match only semver tags. dd.tag.exclude filters out tags that match. dd.tag.transform applies a rewrite to the tag string before matching. dd.display.name overrides the container name shown in the dashboard, and dd.group groups containers into collapsible stacks.", }, { question: "What is the controller-agent architecture and when do I need it?", @@ -27,12 +27,12 @@ export const faqItems: Array<{ question: string; answer: string }> = [ { question: "How does Drydock compare to Watchtower?", answer: - "Watchtower auto-updates containers on a schedule with minimal config and no UI. Drydock is an update management platform: it detects what is available, shows you a dashboard, lets you preview and approve changes, backs up images before applying them, and rolls back automatically if the new container fails its health check. It also adds security scanning (Trivy CVEs, SBOM generation, Cosign signature verification) and per-container update policies. If you want silent automatic updates, Watchtower is simpler. If you want visibility, control, security gates, and notification integrations, Drydock fits better.", + "Watchtower auto-updates containers on a schedule with minimal config and no UI. Drydock is an update management platform: it detects what is available, shows you a dashboard, lets you preview and approve changes, backs up images before applying them, and rolls back automatically if the new container fails its health check. It also adds security scanning (Trivy/Grype CVEs, SBOM generation, Cosign signature verification) and per-container update policies. If you want silent automatic updates, Watchtower is simpler. If you want visibility, control, security gates, and notification integrations, Drydock fits better.", }, { question: "Does Drydock support vulnerability scanning and image signature verification?", answer: - "Yes. Drydock integrates Trivy for CVE scanning and SBOM generation for any image in your update queue. The Update Bouncer deployment gate supports Cosign signature verification — you can block an update from being applied if the new image is unsigned or signed by an unexpected identity. Scanning and verification are opt-in per container or globally, and results surface in the dashboard alongside the available-update list so you can make an informed decision before pulling.", + "Yes. Drydock integrates Trivy and Grype (DD_SECURITY_SCANNER=trivy|grype|both) for CVE scanning and SBOM generation for any image in your update queue. The Update Bouncer deployment gate supports Cosign signature verification — you can block an update from being applied if the new image is unsigned or signed by an unexpected identity. Scanning and verification are opt-in per container or globally, and results surface in the dashboard alongside the available-update list so you can make an informed decision before pulling.", }, { question: "Is Drydock open source, and how do I get started?", diff --git a/apps/web/src/components/roadmap.tsx b/apps/web/src/components/roadmap.tsx index da631fb42..790ae544b 100644 --- a/apps/web/src/components/roadmap.tsx +++ b/apps/web/src/components/roadmap.tsx @@ -8,8 +8,9 @@ import { type Milestone, roadmap } from "@/lib/site-content"; // the vibe — not literally `git log` output. const released = roadmap.filter((m) => m.status === "released"); -// HEAD release: last released milestone -const headRelease = released[released.length - 1] as Milestone; +// HEAD: an in-progress "next" release (active RC) takes priority; otherwise the last released milestone. +const headRelease = (roadmap.find((m) => m.status === "next") ?? + released[released.length - 1]) as Milestone; function GitItem({ item, isReleased }: { item: string; isReleased: boolean }) { return ( @@ -27,8 +28,8 @@ function GitItem({ item, isReleased }: { item: string; isReleased: boolean }) { } function GitLogRow({ milestone }: { milestone: Milestone }) { - const isReleased = milestone.status === "released"; const isHead = milestone.version === headRelease.version; + const isReleased = milestone.status === "released" || isHead; const preview = milestone.items.slice(0, 2); const rest = milestone.items.slice(2); diff --git a/content/docs/current/configuration/ui/index.mdx b/content/docs/current/configuration/ui/index.mdx index fe10cc4a1..8b7de6cb4 100644 --- a/content/docs/current/configuration/ui/index.mdx +++ b/content/docs/current/configuration/ui/index.mdx @@ -107,15 +107,22 @@ Data tables support: ## View Modes -Data views that expose view-mode controls support table, card, or list presentation depending on the page: +Data views that expose view-mode controls toggle between table and card presentation: | Mode | Description | | --- | --- | | **Table** (default) | Rows with shared sizing, interactive column resizing, persisted widths, and sticky actions where relevant | | **Cards** | Grid layout | -| **List** | Accordion-style | -Toggle between modes using the view mode buttons in the toolbar where they are available. Utility pages with naturally tabular data may use table-only presentation. +Toggle between modes using the view mode buttons in the toolbar where they are available. The choice is persisted per view. Below ~640px the layout automatically reflows to cards and the now-redundant toggle is hidden. Utility pages with naturally tabular data may use table-only presentation. The old three-way `list` (accordion) mode was removed in v1.6 — it's table or cards. + +### Column Visibility + +Containers, Agents, Audit, Security, Servers, and Watchers each expose a column show/hide picker in the toolbar, letting you hide low-priority columns from the table. Hidden columns are persisted per view and have no effect in card mode. + +### Card Sorting + +Containers, Agents, and Security cards include a sort-field and ascending/descending control in the toolbar when in card mode, so cards can be ordered the same way table rows can. ## Keyboard Shortcuts diff --git a/content/docs/current/configuration/watchers/index.mdx b/content/docs/current/configuration/watchers/index.mdx index 3950c021d..69945b56d 100644 --- a/content/docs/current/configuration/watchers/index.mdx +++ b/content/docs/current/configuration/watchers/index.mdx @@ -782,7 +782,7 @@ To fine-tune the behaviour of drydock _per container_, you can add labels on the **Release notes inline popover** — The release-notes icon on each container row opens an inline popover showing both the current and available-version release notes side by side, with expand/collapse per panel. When only an external release URL is available (no structured notes), the popover contains a single row linking out to that URL. -**Intermediate releases.** When a container is several versions behind, drydock also fetches the releases between the running tag (exclusive) and the update target (inclusive), so you see the full range in one place. This is best-effort and semver-only: both tags must be strict semver (a leading `v` is fine). Date tags and rolling tags (`latest`, `stable`) fall back to the standard two-panel current/target view. The intermediate list is lazy-loaded when the popover opens — it is not embedded in the container model or the agent snapshot, so it adds no ongoing payload weight. Control the cap with `DD_RELEASE_NOTES_MAX_INTERMEDIATE` (default `20`; set to `0` to disable intermediate fetching entirely). When the range exceeds the cap, the popover shows a "N older releases not shown" notice. The `__FILE` secret-file convention is supported (`DD_RELEASE_NOTES_MAX_INTERMEDIATE__FILE`). The backing endpoint is `GET /api/containers/{id}/intermediate-release-notes?from=&to=` (`from` required; `to` defaults to the container's pending update tag). +**Intermediate releases.** When a container is several versions behind, drydock also fetches the releases between the running tag (exclusive) and the update target (inclusive), so you see the full range in one place. This is best-effort and semver-only: both tags must be strict semver (a leading `v` is fine). Date tags and rolling tags (`latest`, `stable`) fall back to the standard two-panel current/target view. The intermediate list is lazy-loaded when the popover opens — it is not embedded in the container model or the agent snapshot, so it adds no ongoing payload weight. Control the cap with `DD_RELEASE_NOTES_MAX_INTERMEDIATE` (default `20`; set to `0` to disable intermediate fetching entirely). When the range exceeds the cap, the popover shows a "N older releases not shown" notice. The `__FILE` secret-file convention is supported (`DD_RELEASE_NOTES_MAX_INTERMEDIATE__FILE`). The backing endpoint is `GET /api/v1/containers/{id}/intermediate-release-notes?from=&to=` (`from` required; `to` defaults to the container's pending update tag). **Token behavior.** Release notes are fetched from GitHub. The dedicated `DD_RELEASE_NOTES_GITHUB_TOKEN`, when configured, is forwarded for all source resolutions: trusted OCI image labels, GHCR image paths, Docker Hub metadata lookups, and container-label sources (`dd.source.repo`, persisted `container.sourceRepo`). The GHCR token fallback (see [GHCR](/docs/configuration/registries/ghcr)) stays restricted to trusted sources only and is never sent to a container-label source. Because `DD_RELEASE_NOTES_GITHUB_TOKEN` can be sent to a repo named by a (potentially attacker-controllable) container label, scope it narrowly. Two safe options: (a) a classic PAT with the `public_repo` scope only — no other scopes; or (b) a fine-grained PAT with read-only **Contents** permission limited to public repositories and no write or account permissions. diff --git a/content/docs/current/deprecations/index.mdx b/content/docs/current/deprecations/index.mdx index b0479a1a2..ebfc51b9a 100644 --- a/content/docs/current/deprecations/index.mdx +++ b/content/docs/current/deprecations/index.mdx @@ -44,6 +44,16 @@ The unversioned `/api/*` path was a backward-compatible alias for `/api/v1/*`. A | **Removed since** | v1.6.0 | | **Migration** | Replace `/api/` with `/api/v1/` in all integrations. If you depend on wud-card or Homepage's `whatsupdocker` widget and can't change their base path, set `DD_COMPAT_WUDCARD=true` | +### Unversioned WebSocket alias (`WS /api/log/stream`) {#unversioned-ws-log-stream} + +The system log stream WebSocket accepted both the versioned `/api/v1/log/stream` path and the unversioned `/api/log/stream` alias, following the same transition-alias policy as the REST `/api/*` path above. As of v1.6.0, an upgrade request to the unversioned path is rejected with a 410-style upgrade failure instead of completing the WebSocket handshake. + +| | | +| --- | --- | +| **Deprecated since** | v1.5.0 | +| **Removed since** | v1.6.0 | +| **Migration** | Point WebSocket clients at `/api/v1/log/stream` | + ### Legacy `WUD_*` environment variables {#legacy-env-vars} `WUD_*` prefixed environment variables are ignored as of v1.6.0. Only `DD_*` variables are loaded at runtime; the migration CLI still recognizes old names so it can rewrite existing files. diff --git a/content/docs/current/quickstart/index.mdx b/content/docs/current/quickstart/index.mdx index 57620f733..cd4fbcaab 100644 --- a/content/docs/current/quickstart/index.mdx +++ b/content/docs/current/quickstart/index.mdx @@ -96,7 +96,7 @@ docker run -d --name drydock \ -The official image is published on Docker Hub and GitHub Container Registry. +The official image is published on Docker Hub, GitHub Container Registry, and Quay.io. Release tags use the same channel names in every registry: