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-ignoredaudit entry (statusinfo) when Drydock ignores add.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 andDD_HOOKS_ALLOW_IMAGE_LABELSis 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, asnotificationSkipsonGET /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/agentsreturnswarnings: ["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 timingjob 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-changedSSE event now includesupdatedAt,fromVersionandtoVersion, 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-Agentand remote address so you can find the caller (#1370). - The Agents, Auth, Notifications, Registries, Triggers and Watchers views now share one
useSearchFromQuerycomposable 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,latestand the new1.7.0and1.7rows 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 toDEPRECATIONS.mdand 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 (including0andfalse) 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 emitundefinedkeys for unset secret fields. - Pin v1.8 documentation deployment examples to
1.8.0-rc.3and 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
dockercomposeaction lefttriggerName,fromVersion,toVersionandtargetImageempty 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'sdependencyCount. - 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
reasonofagent-unreachable,bad-upstream-responseortransport-failedotherwise, and the prune and preview routes adddetails.reasonand 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 everyapi-prefixed path, but the static CSP invercel.jsononly covers/api/*, so those 404 pages went out with no CSP. The matcher now skips only/apiand/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-1and90004-2entries in.zap/rules-public-site.tsvbecause 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.zaprules file and confirms both sites still sendCross-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
watchallon, 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
sanitizeLogParamhelper, 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 (
apiKeyReviewPendingonGET /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 atgitlab.example.comorexample.comwas treated as the one configured athttps://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.prelabel 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
audcontaining the client ID orazp/client_idequal to it), opaque tokens go through RFC 7662 introspection, and a token that can be checked neither way is rejected.DD_AUTH_OIDC_{name}_BEARERAUDIENCESadds extra accepted audiences. The browser login flow is unchanged. See DEPRECATIONS.md. - Override the e2e-only
http-cache-semanticsto 4.3.0 andargparseto 2.0.1 so the load-test harness no longer resolves the vulnerablehttp-cache-semantics4.2.0 orsprintf-js1.0.3 (GHSA-ch52-4w7c-c8xp, GHSA-hp3w-g68c-fv3c). Neither is in the shipped image.argparseis only used by js-yaml's CLI, which Artillery doesn't call. - Update the backend's transitive
follow-redirects(pulled in byaxios) from 1.16.0 to 1.16.1 for CVE-2026-111755 (GHSA-8r9p-f939-6h3c). - Update the dev-only
viteto 8.2.4 in the backend and UI and 7.3.7 in the demo for CVE-2026-112943 (GHSA-698v-4r3x-3vcv, Windowslaunch-editorargument escaping). It isn't part of the shipped image.