Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
b9b1664
✨ feat(registry): add mTLS client certificate support
s-b-e-n-s-o-n Mar 11, 2026
d62ec79
📝 docs(readme): replace screenshots with live demo link
s-b-e-n-s-o-n Mar 11, 2026
c5c41b5
⚡ perf(mqtt): default HASS_ATTRIBUTES preset to short
s-b-e-n-s-o-n Mar 11, 2026
c93cc2b
🐛 fix(ui): retry server feature flags after pre-login fetch failure
s-b-e-n-s-o-n Mar 11, 2026
4dc7412
✨ feat(audit): record container-update events from external lifecycle…
s-b-e-n-s-o-n Mar 11, 2026
74d8d32
🐛 fix(agent): load CA certificate when CAFILE provided without CERTFILE
s-b-e-n-s-o-n Mar 11, 2026
d3355ee
🐛 fix(log): propagate configured level to multistream destinations
s-b-e-n-s-o-n Mar 11, 2026
144a14f
🔒 security(demo): validate postMessage origin in service worker
s-b-e-n-s-o-n Mar 11, 2026
f0cdfcb
🔧 chore(qa): add Traefik and nginx reverse proxy QA environments
s-b-e-n-s-o-n Mar 11, 2026
ce98a74
🐛 fix(trigger): upgrade silent skip paths to warn-level logging
s-b-e-n-s-o-n Mar 11, 2026
e1c54e1
🐛 fix(icons): use no-store cache header for fallback icon responses
s-b-e-n-s-o-n Mar 11, 2026
db3859e
📝 docs(changelog): add trigger diagnostics and icon cache fix entries
s-b-e-n-s-o-n Mar 11, 2026
798733d
🐛 fix(trigger): resolve compose directory paths to compose file candi…
s-b-e-n-s-o-n Mar 11, 2026
d9c6703
🐛 fix(docker): support TLS backend in container healthcheck
s-b-e-n-s-o-n Mar 11, 2026
25300b4
🐛 fix(auth): accept legacy v1.3.9 htpasswd hash formats on upgrade
s-b-e-n-s-o-n Mar 11, 2026
566ff6f
🐛 fix(trigger): accept lowercased env var keys for camelCase compose …
s-b-e-n-s-o-n Mar 12, 2026
9f07649
✨ feat(trigger): preserve explicit docker.io registry prefix in compo…
s-b-e-n-s-o-n Mar 12, 2026
d7cd180
🐛 fix(demo): default theme to system variant for OS light/dark match
s-b-e-n-s-o-n Mar 12, 2026
e839194
🐛 fix(demo): navigate theme editor button to appearance tab
s-b-e-n-s-o-n Mar 12, 2026
3336eff
📝 docs: add changelog entries for rc.12 fixes
s-b-e-n-s-o-n Mar 12, 2026
319402d
📝 docs(config): update docs for basic auth legacy hashes, TLS healthc…
s-b-e-n-s-o-n Mar 12, 2026
b17129a
✅ test(qa): add rc.12 QA compose environment and test stacks
s-b-e-n-s-o-n Mar 12, 2026
891d1bf
📝 docs: update GHCR pulls badge to 32K+
s-b-e-n-s-o-n Mar 12, 2026
872f2ce
🔒 security(auth): reject unsupported hash formats instead of plaintex…
s-b-e-n-s-o-n Mar 12, 2026
5836de8
⚡ perf(icons): eliminate redundant fs.access syscall in cache check
s-b-e-n-s-o-n Mar 12, 2026
ddf104d
⚡ perf(mqtt): use getContainerCount instead of cloning full collection
s-b-e-n-s-o-n Mar 12, 2026
57d61fb
⚡ perf(ui): hoist sort-order constants to module scope in ContainersView
s-b-e-n-s-o-n Mar 12, 2026
1b03531
♻️ refactor(trigger): improve compose trigger type safety and extraction
s-b-e-n-s-o-n Mar 12, 2026
f583457
🐛 fix(watcher): skip same-name dedupe for containers with empty names
s-b-e-n-s-o-n Mar 12, 2026
8718ec0
🐛 fix(demo): harden postMessage origin, share fallback, and SSE dispatch
s-b-e-n-s-o-n Mar 12, 2026
c47cc8d
✅ test(log): replace setTimeout with vi.waitFor in debug-level tests
s-b-e-n-s-o-n Mar 12, 2026
d597db9
✅ test(audit): add container-update handler and empty-name fallback test
s-b-e-n-s-o-n Mar 12, 2026
669357e
📝 docs: move rc.12 changelog entries under v1.4.0 and fix mTLS key names
s-b-e-n-s-o-n Mar 12, 2026
40d1b7a
💄 style(ui): remove borders from shared data components and layout
s-b-e-n-s-o-n Mar 12, 2026
1a6979d
💄 style(ui): remove borders from all views, config tabs, and detail p…
s-b-e-n-s-o-n Mar 12, 2026
23ae4b7
💄 style(ui): redesign detail panel full-page button with frame-corner…
s-b-e-n-s-o-n Mar 12, 2026
1ef9ec3
✨ feat(ui): sync config tab selection to URL query parameter
s-b-e-n-s-o-n Mar 12, 2026
60087df
🎨 style(web): fix biome formatting in demo-section
s-b-e-n-s-o-n Mar 12, 2026
9859160
✅ test(ui): update tests for borderless UI and config tab routing
s-b-e-n-s-o-n Mar 12, 2026
e2ece3d
🔧 chore: add timeouts to lefthook pre-push and soften clean-tree gate
s-b-e-n-s-o-n Mar 12, 2026
507f4c0
🎨 style: add trailing newline to Dockerfile
s-b-e-n-s-o-n Mar 12, 2026
4a02d88
🎨 style: fix CHANGELOG formatting and remove unused reference link
s-b-e-n-s-o-n Mar 12, 2026
fcb258a
✨ feat(auth): accept PHC-format argon2id hashes in basic authentication
s-b-e-n-s-o-n Mar 12, 2026
c337a5a
📝 docs(auth): update hash generation guidance to recommend argon2 CLI
s-b-e-n-s-o-n Mar 12, 2026
55edc64
✨ feat(ui): add maxHeight prop to DataTable for non-virtual scroll ov…
s-b-e-n-s-o-n Mar 12, 2026
448ea67
♻️ refactor(dashboard): replace raw updates table with DataTable comp…
s-b-e-n-s-o-n Mar 12, 2026
bab44bd
📝 docs(readme): add community QA section thanking v1.4 testers
s-b-e-n-s-o-n Mar 12, 2026
e886758
✅ test(auth): add edge-case coverage for argon2/SHA/MD5 hash parsing
s-b-e-n-s-o-n Mar 12, 2026
a68c6ac
🔧 chore(ui): use random port and disable HMR in vitest config
s-b-e-n-s-o-n Mar 12, 2026
3dc06e4
✅ test(auth): improve PHC argon2 edge-case test coverage
s-b-e-n-s-o-n Mar 12, 2026
c0671f4
🔧 chore: harden lefthook pre-push clean-tree gate and tune qlty timeout
s-b-e-n-s-o-n Mar 12, 2026
3607ec9
🐛 fix(mqtt): update HASS entity_picture URL to whale-logo.png (#138)
s-b-e-n-s-o-n Mar 12, 2026
df6c193
💄 style(ui): left-align version column in dashboard updates table
s-b-e-n-s-o-n Mar 12, 2026
cb5d9be
✨ feat(mqtt): resolve entity_picture from dd.display.icon and dd.disp…
s-b-e-n-s-o-n Mar 12, 2026
9ce46ea
🔧 chore: add pre-commit coverage gate for staged source files
s-b-e-n-s-o-n Mar 12, 2026
4ed7fd9
📝 docs: sync changelog and configuration docs with rc.12 changes
s-b-e-n-s-o-n Mar 12, 2026
05ece36
📝 docs(web): fix roadmap v1.6 item — Teams/Matrix shipped in v1.4
s-b-e-n-s-o-n Mar 12, 2026
35c8852
🎨 style(web): fix biome formatting in roadmap items array
s-b-e-n-s-o-n Mar 12, 2026
4627948
🔒 security(auth): suppress CodeQL false positive on legacy SHA-1 veri…
s-b-e-n-s-o-n Mar 12, 2026
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
35 changes: 34 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [1.4.0] — 2026-02-28

### Breaking Changes

- **MQTT `HASS_ATTRIBUTES` default changed from `full` to `short`** — This changes Home Assistant entity payloads by default, excluding large SBOM documents, scan vulnerabilities, details, and labels. To retain the previous payload behavior, set `DD_TRIGGER_MQTT_{name}_HASS_ATTRIBUTES=full` explicitly.

### Added

- **Audit log for container state changes** — External container lifecycle events (start, stop, restart via Portainer or CLI) now generate `container-update` audit entries with the new status, so the audit log reflects all state changes, not just Drydock-initiated actions. ([#120](https://github.com/CodesWhat/drydock/discussions/120))
- **mTLS client certificate support** — Registry providers now accept `CLIENTCERT` and `CLIENTKEY` options for mutual TLS authentication with private registries that require client certificates.

#### Backend / Core

- **Container recent-status API** — `GET /api/containers/recent-status` returns pre-computed update status (`updated`/`pending`/`failed`) per container, replacing the client-side audit log scan and reducing dashboard fetch payload size.
Expand Down Expand Up @@ -55,6 +62,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **Security vulnerability overview endpoint** — New `GET /api/containers/security/vulnerabilities` returns pre-aggregated vulnerability data grouped by image with severity summaries, so the Security view no longer needs to load all containers.
- **MQTT attribute filtering for Home Assistant** — MQTT trigger supports attribute-based filtering for Home Assistant integration, allowing selective publishing based on container attributes.
- **Docker Compose post_start env validation** — Docker Compose trigger validates environment variables in `post_start` hooks before execution, preventing runtime errors from missing or invalid env var references.
- **MQTT HASS entity_picture from container icons** — When Home Assistant HASS discovery is enabled, `entity_picture` is now automatically resolved from the container's `dd.display.icon` label. Icons with `sh:`, `hl:`, or `si:` prefixes map to jsDelivr CDN URLs for selfhst, homarr-labs, and simple-icons respectively. Direct HTTP/HTTPS URLs pass through unchanged. ([#138](https://github.com/CodesWhat/drydock/issues/138))
- **`dd.display.picture` container label** — New label to override the MQTT HASS `entity_picture` URL directly. Takes precedence over icon-derived pictures when set to an HTTP/HTTPS URL.

#### UI / Dashboard

Expand Down Expand Up @@ -88,6 +97,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **Rollback confirmation dialog** — Container rollback actions now require explicit confirmation through a danger-severity dialog before restoring from backup.
- **Update confirmation dialog** — Container update actions now require explicit confirmation through a dialog before triggering an update.
- **SHA-1 hash deprecation banner** — Dashboard shows a dismissible deprecation banner when legacy SHA-1 password hashes are detected, prompting migration to argon2id.
- **Config tab URL deep-linking** — Config view tab selection syncs to the URL query parameter, enabling shareable direct links to specific config tabs.

### Changed

Expand Down Expand Up @@ -119,9 +129,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **6 color themes** — Replaced original Drydock theme with popular editor palettes: One Dark, GitHub, Dracula, Catppuccin, Gruvbox, and Ayu. Each with dark and light variants.
- **Argon2id password hashing** — Basic auth now uses argon2id (OWASP recommended) via Node.js built-in `crypto.argon2Sync()` instead of scrypt for password hashing. Default parameters: 64 MiB memory, 3 passes, parallelism 4.
- **PUT `/api/settings` deprecated** — `PUT /api/settings` now returns RFC 9745 `Deprecation` and RFC 8594 `Sunset` headers. Use `PATCH /api/settings` for partial updates. PUT alias removal targeted for v1.5.0.
- **Basic auth argon2id PHC compatibility** — Basic authentication now accepts PHC-format argon2id hashes (`$argon2id$v=19$m=...,t=...,p=...$salt$hash`) in addition to the existing Drydock `argon2id$memory$passes$parallelism$salt$hash` format. Hash-generation guidance now recommends the standard `argon2` CLI command first, with Node.js as a secondary option.
- **Borderless UI redesign** — Removed borders from all views, config tabs, detail panels, and shared data components for a cleaner visual appearance.
- **Dashboard version column alignment** — Version column in the dashboard updates table is now left-aligned for better readability.
- **Detail panel expand button redesigned** — Full-page expand button in the detail panel now uses a frame-corners icon instead of the previous maximize icon.
- **Sidebar active indicator removed** — Removed the blue active indicator bar from sidebar navigation items for a cleaner look.

### Fixed

- **Log level setting had no effect** — `DD_LOG_LEVEL=debug` was correctly parsed but debug messages were silently dropped because pino's multistream destinations defaulted to `info` level. Stream destinations now inherit the configured log level. ([#134](https://github.com/CodesWhat/drydock/issues/134))
- **Server feature flags not loaded after login** — Feature flags (`containeractions`, `delete`) were permanently stuck as disabled when authentication was required, because the pre-login bootstrap fetch failure marked the flags as "loaded" and never retried. Now failed fetches allow automatic retry after login. ([#120](https://github.com/CodesWhat/drydock/discussions/120))
- **Compose trigger silently skips containers** — Multiple failure paths in the compose trigger were logged at `debug` level, making it nearly impossible to diagnose why a trigger reports success but containers don't update. Key diagnostic messages (compose file mismatch, label inspect failure, no containers matched) promoted to `warn` level, and the "already up to date" message now includes container names. ([#84](https://github.com/CodesWhat/drydock/discussions/84))
- **Fallback icon cached permanently** — The Docker placeholder icon was served with `immutable` cache headers, causing browsers to cache it permanently even after the real provider icon becomes available. Fallback responses now use `no-store`.
- **Basic auth upgrade compatibility restored** — Basic auth now accepts legacy v1.3.9 Basic auth hashes (`{SHA}`, `$apr1$`/`$1$`, `crypt`, and plain fallback) to preserve smooth upgrades. Legacy formats remain deprecated and continue showing a migration banner, with removal still planned for v1.6.0.
- **Compose trigger rejects lowercase env var keys** — Configuration keys like `COMPOSEFILEONCE`, `DIGESTPINNING`, and `RECONCILIATIONMODE` were lowercased by the env parser but the Joi schema expected camelCase. Schema now maps lowercase keys to their camelCase equivalents. ([#120](https://github.com/CodesWhat/drydock/discussions/120))
- **Compose trigger strips docker.io prefix** — When a compose file uses an explicit `docker.io/` registry prefix, compose mutations now preserve it instead of stripping it to a bare library path. ([#120](https://github.com/CodesWhat/drydock/discussions/120))
- **Compose trigger fails when FILE points to directory** — `DD_TRIGGER_DOCKERCOMPOSE_{name}_FILE` now accepts directories, automatically probing for `compose.yaml`, `compose.yml`, `docker-compose.yaml`, or `docker-compose.yml` inside the directory. ([#84](https://github.com/CodesWhat/drydock/discussions/84))
- **Container healthcheck fails with TLS backend** — The Dockerfile healthcheck now detects `DD_SERVER_TLS_ENABLED=true` and switches to `curl --insecure https://` for self-signed certificates. Also skips the healthcheck entirely when `DD_SERVER_ENABLED=false`. ([#120](https://github.com/CodesWhat/drydock/discussions/120))
- **Agent CAFILE ignored without CERTFILE** — The agent subsystem now loads the CA certificate from `CAFILE` even when `CERTFILE` is not provided, fixing TLS verification for agents behind reverse proxies with custom CA chains.
- **Service worker accepts cross-origin postMessage** — The demo service worker now validates `postMessage` origins against the current host, preventing potential cross-origin message injection.

- **Action buttons disable and show spinner during in-progress actions** — Container action buttons (Stop, Start, Restart, Update, Delete) now show a disabled state with a spinner while the action runs in the background, providing clear visual feedback. The confirm dialog closes immediately on accept instead of blocking the UI.
- **Command palette clears stale filter on navigation** — Navigating to a container via Ctrl+K search now clears the active `filterKind`, preventing stale filter state from hiding the navigated container.
- **Manual update button works with compose triggers** — The update container endpoint now searches for both `docker` and `dockercompose` trigger types, matching the existing preview endpoint behavior. Previously, users with only a compose trigger saw "No docker trigger found for this container".
Expand Down Expand Up @@ -186,6 +213,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **Backup retention on failed updates** — Backup entries are now pruned on the failure path, not just after successful updates, preventing indefinite accumulation of stale backups.
- **Backup pruning with undefined maxCount** — `pruneOldBackups()` no longer deletes all backups when `maxCount` is `undefined` (e.g. when `DD_BACKUPCOUNT` is not configured). Now correctly no-ops on invalid or non-finite values.
- **Auto-rollback audit fromVersion accuracy** — Rollback audit entries now correctly record `fromVersion` as the failing new image tag (via `updateKind.remoteValue`) instead of the pre-update old tag.
- **HASS entity_picture URL broken after logo rename** — MQTT HASS discovery payload referenced a renamed logo file (`drydock.png` instead of `whale-logo.png`), causing missing entity pictures in Home Assistant. ([#138](https://github.com/CodesWhat/drydock/issues/138))
- **Watcher crashes on containers with empty names** — Docker watcher's same-name deduplication filter threw errors when containers had empty or missing names. Now skips deduplication for unnamed containers.
- **Container names not reconciled after external recreate** — Containers recreated externally (via Portainer or `docker compose up`) retained stale names in the store until the next full poll cycle. Now reconciles container names immediately on detection.
- **Nested icon prefixes fail proxy request** — Icon proxy rejected icons with doubled prefixes like `mdi:mdi-docker`. Now normalizes nested prefixes before proxying.
- **Colon-separated icon prefixes rejected** — `dd.display.icon` labels using colon separators (e.g., `sh:nextcloud`) were rejected by the API validation pattern. Validation now accepts colon-prefixed icon identifiers.
- **Bouncer-blocked state missing from container details** — Container detail views didn't reflect bouncer-blocked status. Now correctly wires the blocked state into detail panel display.

### Security

Expand Down Expand Up @@ -227,6 +260,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **Identity-aware rate limit keying** — Opt-in `DD_SERVER_RATELIMIT_IDENTITYKEYING=true` keys authenticated route rate limits by session/username instead of IP, preventing collisions for multiple users behind shared proxies. Unauthenticated routes remain IP-keyed. Disabled by default.
- **Reactive server feature flags in UI** — Container action buttons (update, rollback, scan, triggers) are now gated by server-side feature flags via a `useServerFeatures` composable. When features like `DD_SERVER_FEATURE_CONTAINERACTIONS` are disabled, buttons show a disabled state with tooltip explaining why instead of silently failing at runtime.
- **Compose trigger hardening** — Auto compose file detection from container labels (`com.docker.compose.project.config_files`) with Docker inspect fallback, pre-commit `docker compose config --quiet` validation before writes, compose file reconciliation (warn/block modes for runtime vs compose image drift), optional digest pinning (`DIGESTPINNING` trigger config), compose-file-once batch mode for multi-service stacks, multi-file compose chain awareness with deterministic writable target selection, compose metadata in update preview API, and compose file path display in container detail UI.
- **Unsupported hash formats fail closed** — Basic auth now rejects unsupported hash formats instead of falling through to plaintext comparison, preventing accidental plaintext password acceptance.

### Performance

Expand Down Expand Up @@ -706,7 +740,6 @@ Remaining upstream-only changes (not ported — not applicable to drydock):
| Fix codeberg tests | Covered by drydock's own tests |
| Update changelog | Upstream-specific |

[Unreleased]: https://github.com/CodesWhat/drydock/compare/v1.4.0...HEAD
[1.4.0]: https://github.com/CodesWhat/drydock/compare/v1.3.9...v1.4.0
[1.3.9]: https://github.com/CodesWhat/drydock/compare/v1.3.8...v1.3.9
[1.3.8]: https://github.com/CodesWhat/drydock/compare/v1.3.7...v1.3.8
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ ENV WORKDIR=/home/node/app
ENV DD_LOG_FORMAT=text
ENV DD_VERSION=$DD_VERSION

HEALTHCHECK --interval=30s --timeout=5s CMD ["sh", "-c", "if [ -z \"$DD_SERVER_ENABLED\" ] || [ \"$DD_SERVER_ENABLED\" = 'true' ]; then curl --fail http://localhost:${DD_SERVER_PORT:-3000}/health || exit 1; else exit 0; fi"]
HEALTHCHECK --interval=30s --timeout=5s CMD ["sh", "-c", "if [ -n \"$DD_SERVER_ENABLED\" ] && [ \"$DD_SERVER_ENABLED\" != 'true' ]; then exit 0; fi; if [ \"$DD_SERVER_TLS_ENABLED\" = 'true' ]; then curl --fail --insecure https://localhost:${DD_SERVER_PORT:-3000}/health || exit 1; else curl --fail http://localhost:${DD_SERVER_PORT:-3000}/health || exit 1; fi"]

# Install system packages, trivy, and cosign
# hadolint ignore=DL3018,DL3028,DL4006
Expand Down
Loading
Loading