github CodesWhat/drydock v1.8.0-rc.3

pre-release2 hours ago

v1.8.0-rc.3

Full Changelog: v1.8.0-rc.2...v1.8.0-rc.3

[1.8.0-rc.3] - 2026-10-20

Warning

Release candidate for opt-in testing. SQLite changes the on-disk store. Stop Drydock and back up the complete store directory before upgrading from v1.7 or earlier. First startup imports existing JSON data into SQLite. Downgrading requires restoring the intended pre-upgrade JSON snapshot and any separate session file; changes made only in SQLite are not carried back. A fresh v1.8 installation has no pre-upgrade JSON snapshot. Start with a copy of your store and test containers.

This candidate includes the v1.8.0-rc.1 features and the v1.8.0-rc.2 fixes, and retains their upgrade warnings. Drydock is now MIT licensed, and OIDC bearer tokens must be issued for this Drydock client. Image-baked hook labels are now audited, agents older than 1.7.0 trigger a warning, a recovery-code sign-in makes the user review API keys, and the image uses Alpine's patched zlib. v1.8 GA is not released; v1.7.0 (GA 2026-10-19) is the latest stable release. RC3 has its own publication-based soak.

Added

  • Record a hook-label-ignored audit entry (status info) when Drydock ignores a dd.hook.* label baked into an image, once per update per container. It lists the container and the ignored label keys, never the command values, and says the labels come from the image and DD_HOOKS_ALLOW_IMAGE_LABELS is not set. Before this the refusal only reached the log as a warning. Hook execution is unchanged.

  • Show why a notification was skipped without turning on debug logging (DR-85). Each notification trigger now remembers the last skip reason per container (rule-disabled, threshold-not-met, trigger-excluded, once-already-sent, recently-applied, digest-buffered), clears it when a notification is sent, and keeps at most 1000 entries in memory. The reasons appear in the container detail Actions tab, as notificationSkips on GET /api/v1/containers/{id} and /triggers, and in the debug dump.

  • Warn when an agent older than 1.7.0 connects. Those agents still trust lifecycle hook labels baked into images, whatever the controller version. The controller logs one warning per connection, GET /api/agents returns warnings: ["hook-labels-trusted"] for the agent, and the Agents view shows an Update agent badge next to its version. Nothing about hooks or triggers changes.

Changed

  • CI Verify now ends with an advisory CI timing job that writes a job and slowest-step timing table to the run summary and, on non-PR runs, compares the branch's median run time for the last 7 days against the 7 before, with a warning above 20%. It is not a required check.
  • Count containers for the stats aggregator and the agent summary from the store's lightweight stats projection instead of cloning every full container row. On a 2000-container store with large scan and result payloads this drops one call from about 115 ms and 63 MiB held to about 1.3 ms and 0.5 MiB, with identical output. The projection gains the container name.
  • API: the dd:update-operation-changed SSE event now includes updatedAt, fromVersion and toVersion, matching the container list payload, so an update started after page load carries its versions without a list reload. The fields are additive, and agent operation events relay the versions to the controller.
  • The removed-API-path banner now names the resource that was hit (/api/containers/*, never an id) and a coarse client class (homepage, curl, python, ...) with a last-seen time, and the first hit per resource and client each hour is logged with the method, path, User-Agent and remote address so you can find the caller (#1370).
  • The Agents, Auth, Notifications, Registries, Triggers and Watchers views now share one useSearchFromQuery composable for seeding the search box from ?q=. No behavior change.
  • The image now uses Alpine's patched zlib (1.3.2-r1, which carries the CVE-2026-85091 fix) instead of a custom build, pinned in the Dockerfile's apk install. The custom zlib package and its grype VEX file are gone, and CI no longer fetches from zlib.net.
  • Drydock is now MIT licensed (was AGPL-3.0). Contributors agreed to the change.
  • Removed the README upgrade alert for the 1.4.6 security-hardening changes; UPGRADE-NOTES.md still covers them for anyone updating from an older release.
  • Refreshed the docs for the 1.7.0 GA. Stable is now v1.7.0 across the docs site, README, deprecations page and quickstart tag table (1, latest and the new 1.7.0 and 1.7 rows resolve to it), and the Updates page gains the v1.7.0 and v1.6.1 entries. Added the image-baked lifecycle hook label entry to DEPRECATIONS.md and the deprecations page. Reworded the README upgrade alert to target updates from 1.5.x or earlier, marked the approval queue as v1.8 (RC) in the comparison tables, and synced the translated READMEs with the English roadmap status. Fixed the dead sockguard preset link and the stale CONTRIBUTING notification example, and covered the registry init failure when trigger configuration throws.
  • Registry and trigger configuration masking now follows one rule. Any value that is present and non-empty is redacted as [REDACTED], so numeric, boolean and object secrets (including 0 and false) no longer slip through in logs and API responses. Previously trigger fields masked strings only and registry fields masked on truthiness. Docr and ECR no longer emit undefined keys for unset secret fields.
  • Pin v1.8 documentation deployment examples to 1.8.0-rc.3 and align the website, demo, support policy and translated README testing guidance with RC3. Feature and deprecation introduction versions and the RC1 and RC2 release records are preserved.

Fixed

  • A controller-to-agent event stream that silently died (half-open connection) left the agent shown as connected until something else failed. Agents now send an SSE comment heartbeat every 15 seconds, and the controller destroys the stream and reconnects after 45 seconds of silence, marking the agent disconnected meanwhile. The watchdog only arms once an agent has sent a heartbeat, so agents older than 1.8.1 are unaffected. (DR-86)
  • The demo's font proxy now serves IBM Plex Mono, so the default monospace font loads on the demo host instead of falling back to the system font.
  • Fill the update-operation record for compose updates the way the Docker action does. An update run through the dockercompose action left triggerName, fromVersion, toVersion and targetImage empty on the record because the compose path bypasses the Docker executor that sets them. Each compose service's record now carries its own values.
  • Update detection no longer waits on release notes. The watcher fetched optional GitHub release notes before saving a container's result, so a 403 or slow lookup (with its 60 s retry intervals) stalled detection and left registry fixtures unready. The lookup now gets a 5 s budget; past it the result is saved without notes and the notes are attached when the lookup finishes, still honoring the provider's retry and cooldown.
  • The dependency graph endpoint (GET /api/containers/dependencies) now builds from the same containers the list shows, so rollback containers no longer appear as nodes or edges and the graph agrees with each row's dependencyCount.
  • Tell Portwing agent timeouts apart from unreachable agents. The Docker bridge answered every transport failure with the same 502, so an image prune on an agent that was down got the same "may still be running" 504 as a genuinely slow one. The bridge now returns 504 for the 30 s inactivity timeout and 502 with a reason of agent-unreachable, bad-upstream-response or transport-failed otherwise, and the prune and preview routes add details.reason and only say the prune may still be running for the timeout.
  • Send the website's Content-Security-Policy on paths that merely start with /api (such as /api, or /apiary). The proxy matcher skipped every api-prefixed path, but the static CSP in vercel.json only covers /api/*, so those 404 pages went out with no CSP. The matcher now skips only /api and /api/..., and a test pins the boundary. The public-site ZAP rules also ignore 10051 (Relative Path Confusion) and 40038 (Bypassing 403), which only hit Vercel firewall 403 responses during the active scan.
  • Restore the weekly public-site DAST scan, which crashed on the 90004-1 and 90004-2 entries in .zap/rules-public-site.tsv because the ZAP scan scripts only accept integer plugin IDs. The file now ignores the whole 90004 plugin for the public sites, and a workflow test requires plain integer IDs in every .zap rules file and confirms both sites still send Cross-Origin-Opener-Policy.
  • Keep a retained update policy (maturity, overrides) with the replacement when a container is recreated and the renamed old container is still listed after the discovery settle. With watchall on, the old container could be re-reported first and take the policy meant for the replacement, leaving a duplicate row. The stash now records the removed row's Docker id, and a re-report of that id leaves it for the replacement (DR-147).

Security

  • Sanitize every field in the removed-API-path log line (method, path, user agent and remote address) with the shared sanitizeLogParam helper, so control characters and ANSI escapes in request data can't forge log lines. Resolves CodeQL alert 3157.
  • A two-factor recovery-code sign-in now makes the user review their API keys. When any active key exists, the session is flagged (apiKeyReviewPending on GET /auth/user) and the UI holds it on the API Keys view, with a banner and each key's creator, creation time and last use, until the user chooses Done reviewing (POST /auth/api-key-review/acknowledge, session-only). Keys are not revoked automatically, and revoking one does not release the review. Previously a key minted from a stolen session survived the recovery login's session sweep without any prompt.
  • A configured GitLab registry no longer sends its bearer credentials to an image registry whose host only contains, or is contained in, the configured URL. match() compared the two with a substring check, so a registry at gitlab.example.com or example.com was treated as the one configured at https://registry.gitlab.example.com. GitLab registries now match on exact host and port, case-insensitive, with the scheme, path and trailing slash of the configured URL ignored.
  • Run the agent's batch route for update actions on the agent's own container records. It used the containers in the request body as sent, so a caller holding the agent secret could inject a dd.hook.pre label that the hook provenance check then treated as operator-set. For update actions the route now looks each container up by id in the agent's store, as the single trigger route does, and takes only the id and operationId from the body. If any id is unknown the whole batch is rejected with 404 and nothing runs. Notification batches still run on the payload as sent, since they carry transient fields and rows the agent store doesn't have. The controller payload is unchanged, so older controllers keep working, but there is no agent version gate: a newer controller talking to an older agent still gets the old behavior.
  • Only accept OIDC bearer tokens issued for this drydock client. The bearer path used to accept any access token the IdP would answer a userinfo call for, so a token minted for another application on the same IdP authenticated against the API. JWT access tokens are now verified against the IdP's JWKS (issuer, expiry, and aud containing the client ID or azp/client_id equal to it), opaque tokens go through RFC 7662 introspection, and a token that can be checked neither way is rejected. DD_AUTH_OIDC_{name}_BEARERAUDIENCES adds extra accepted audiences. The browser login flow is unchanged. See DEPRECATIONS.md.
  • Override the e2e-only http-cache-semantics to 4.3.0 and argparse to 2.0.1 so the load-test harness no longer resolves the vulnerable http-cache-semantics 4.2.0 or sprintf-js 1.0.3 (GHSA-ch52-4w7c-c8xp, GHSA-hp3w-g68c-fv3c). Neither is in the shipped image. argparse is only used by js-yaml's CLI, which Artillery doesn't call.
  • Update the backend's transitive follow-redirects (pulled in by axios) from 1.16.0 to 1.16.1 for CVE-2026-111755 (GHSA-8r9p-f939-6h3c).
  • Update the dev-only vite to 8.2.4 in the backend and UI and 7.3.7 in the demo for CVE-2026-112943 (GHSA-698v-4r3x-3vcv, Windows launch-editor argument escaping). It isn't part of the shipped image.

Don't miss a new drydock release

NewReleases is sending notifications on new releases.