github CodesWhat/drydock v1.5.0-rc.36

latest release: v1.5.0-rc.37
pre-release6 hours ago

v1.5.0-rc.36

Full Changelog: v1.4.6...v1.5.0-rc.36

[1.5.0-rc.36] — 2026-06-15

Added

  • Experimental Portwing edge-agent mode — agents behind NAT or firewalls can now dial OUT to drydock over a persistent wss:// WebSocket instead of waiting for an inbound controller connection (PR #429, M5). The feature is experimental and opt-in: set DD_EXPERIMENTAL_PORTWING=true to enable it. When disabled, the endpoint is not mounted and the feature has zero runtime footprint. Once enabled, agents connect to WS /api/v1/portwing/ws using the portwing/1.0 subprotocol. Authentication is Ed25519 public-key challenge-response with timestamp + nonce replay protection (±60 s clock-skew window, 16 MB maximum frame size). Operator key management is exposed through a REST registry at /api/v1/portwing/keys (list, register, revoke). Because the feature is experimental the protocol and API surface may change in a future release without a deprecation notice.

  • Remote-agent runtime info now carries logLevel and pollInterval in the acknowledgement payload (PR #430, M4). Drydock threads these fields through buildRuntimeInfoFromAck and surfaces them in the Agents view alongside the existing runtime metadata.

Fixed

  • Containers pinned to a fully-specified semver tag (e.g. image:v1.13.3, 3+ numeric segments) no longer climb to newer versions by default (#321). Tags classified as specific precision are now treated as digest-only by default — getTagCandidates returns an empty tag list so updates track digest changes only, not semver version bumps. Opt in to semver climbing by setting dd.tag.include (restricts climbing to matching tags) or dd.tag.family=loose (unrestricted climbing as before). Floating tags (latest, 16-alpine, etc.) and 1–2-segment partial versions are unaffected.

  • Maintenance window now gates when auto-updates are applied, not just when update checks run (#321). Previously watchFromCron respected the window, but maybeFastResyncAfterUpdate (the post-update fast resync) called watchContainer unconditionally — allowing a triggered resync to detect a new image and dispatch an update outside the window. The fast resync now mirrors the same maintenance-window guard used by watchFromCron. Additionally, computeUpdateEligibility gains a maintenanceWindowOpen context field: when false, a soft maintenance-window-closed blocker is recorded in the eligibility result. Manual UI/API-triggered updates pass undefined and remain ungated.

  • Self-update overlay no longer flickers — the UI holds the "Applying Update" screen until the swap is actually complete. Three compounding defects made the self-update experience look broken: (1) the UI's connectivity probe treated any successful /auth/user response as "update finished", but the old server keeps answering during the image pull — so the overlay dismissed almost immediately, then the page died again when the old container actually stopped; (2) the in-progress self-update operation could never be recorded as completed: the finalize callback secret was regenerated per-process (the helper's POST always failed with 403 after the restart) and startup reconciliation expired the operation before the helper could finalize it; and (3) the transient drydock-self-update-<timestamp> helper container carried no watch-exclusion label, so it flashed into the container list with watch-by-default enabled. The finalize secret is now per-operation, with its SHA-256 hash persisted on the operation row so the restarted process can validate the helper's callback; startup reconciliation grants fresh in-progress self-update operations a 10-minute grace window (with expiry as the bounded fallback if the helper dies); a new unauthenticated GET /api/v1/self-update/{operationId}/status endpoint reports the operation state while the session is unavailable mid-restart; the UI polls that endpoint during a self-update and only reloads once the operation reaches a terminal state (succeeded, rolled-back, failed, or expired); and the helper container is labeled dd.watch=false so it never appears in the watcher's container list. The UI also closes its SSE connection when self-update mode begins (after the ack is delivered) — the browser's built-in EventSource retry would otherwise reconnect to the new server, clear the overlay before the swap was committed, and leave the SPA running stale pre-update assets. Dry-run mode no longer shows the overlay at all: the UI notification is skipped and the no-op operation is marked terminal immediately instead of lingering in-progress.

Warning

Upgrade notes — behavioral changes, please read before updating. Releases 1.4.6 and the entire 1.5 line ship security-hardening fixes that change runtime behavior. These are not deprecations: there is no compatibility shim or grace period, so a previously-working deployment can change behavior on upgrade.

  1. OIDC login now requires authorization_endpoint in your provider's discovery metadata. The authorization-redirect allowlist no longer falls back to a broad same-origin match. Mainstream identity providers (Keycloak, Authentik, Authelia, Okta, Google, Entra/Azure AD, Zitadel, …) publish this field and are unaffected. If your /.well-known/openid-configuration does not advertise authorization_endpoint, OIDC sign-in will now fail closed — make sure the discovery document exposes it.
  2. Unauthenticated rate-limit buckets now key on the TCP peer address instead of X-Forwarded-For. Behind a reverse proxy (nginx / Traefik / Caddy), all unauthenticated clients now share a single bucket (the proxy's address), regardless of DD_SERVER_TRUSTPROXY. Internet-facing or multi-user instances may begin to see unexpected 429 Too Many Requests on unauthenticated endpoints. Authenticated requests are keyed per session and are unaffected.
  3. HTTP-trigger proxy URLs must now use the http:// or https:// scheme. Any other scheme (e.g. socks5://) is rejected at config load. Such values were previously accepted but only ever treated as an HTTP proxy — switch to an http(s):// proxy URL.

Don't miss a new drydock release

NewReleases is sending notifications on new releases.