Skip to content

🚀 release: v1.6.0-rc.3 - #572

Merged
scttbnsn merged 175 commits into
mainfrom
dev/v1.6
Jul 21, 2026
Merged

🚀 release: v1.6.0-rc.3#572
scttbnsn merged 175 commits into
mainfrom
dev/v1.6

Conversation

@scttbnsn

Copy link
Copy Markdown
Contributor

Promotes the v1.6.0-rc.3 content from dev/v1.6 to main for the release cut.

rc.3 delta over rc.2:

After merge: dispatch release-cut.yml from main with release_tag=v1.6.0-rc.3.

scttbnsn added 30 commits June 30, 2026 13:00
Review-driven corrections to the v1.5.2 docs/api batch (040d490), found by two
adversarially-verified review passes. Targets v1.6 (no standalone v1.5.2).

- 🐛 fix(api): define NotificationOutboxEntry/NotificationOutboxResult schemas — both were referenced by $ref but never declared, making the assembled OpenAPI document invalid (Swagger UI/Redoc/codegen failed on the unresolved refs)
- ✅ test(api): add a recursive guard that fails on ANY dangling #/components/schemas/* $ref, so this class of bug can't regress
- 🐛 fix(api): emit the canonical /api/v1 base in every pagination _links (container list, watch, security vulnerabilities, update-operations, audit) to match the migrated spec
- 🐛 fix(api): correct registryWebhookSignature headers in the spec — drop x-gitlab-token (never checked), add x-registry-signature + x-harbor-signature (both accepted by the handler)
- 🐛 fix(api): revert LogoutResponse.logoutUrl to type:string (the handler omits the field, it never emits null)
- 🐛 fix(api): correct the remote-trigger 409 description (rollback-container only), document the outbox GET 500, and the scan-all 400 scanner-not-configured case
- ✅ test(api): add spec-structure tests for /api/v1/log/components, /auth/status, /api/v1/app auth posture, RegistryWebhookResponse, and the full /api/v1/webhooks/registry responses
- 📝 docs(api): correct the registry webhook header list in api/index.mdx; drop legacy dd.trigger.include from the edited eligibility row; document the now-reachable GET /api/v1/containers/backups in CHANGELOG
- 🐛 fix(ui): cancelUpdateOperation now POSTs /api/v1/operations/:id/cancel (was the deprecated /api/ alias slated for removal in v1.6.0, which would have 404'd)
- 🐛 fix(ui): ConfigView webhook panel and copyable curl example show /api/v1/webhook/* to match the published docs
…anches

- 📝 docs(readme): point the docs-refresh note and roadmap row at v1.6 (the standalone v1.5.2 lane was dropped — folded into v1.6)
- 📝 docs(contributing): add the qlty-smells advisory row and renumber the pre-push pipeline table to match lefthook.yml
- ✅ test(web): cover the docs-link-rewriter bare /docs/assets and single-quoted-attribute branches
- suppressHydrationWarning on the 7 homepage .reveal wrappers (the pre-hydration reveal bootstrap mutates them, causing a hydration mismatch)
- og:image + twitter:image on /compare, /compare/[slug], and the Trivy security advisory (page-level openGraph was replacing the layout's and dropping the share image)
- dockge compare: i18n row reflects shipped 17-locale support (was stale 'Planned'/competitor; both ship i18n now → tie)
- discord-icon.tsx: official Discord mark, mirrors github-icon API
- site-config: discordUrl invite
- footer: Discord social link between GitHub and Docs
- README: Community section pointing to Discord + GitHub Issues
First commit of #242 table unification. Adds an isMobile prop (default false, non-regressive): the first non-icon column pins sticky-left during horizontal scroll, interactive rows are floored at a 44px touch target, and the 8px resize handle is hidden on touch. 3769 ui tests green at 100% coverage. Refs: #242
Removes the DataCardGrid/DataListAccordion render branches and the view-mode switcher binding from the 5 simple data views; DataTable now renders unconditionally. Also drops AuditView's ?view= URL-query mode mechanism (now a no-op). useViewMode/DataCardGrid/DataListAccordion source files are deleted in a later commit. 3763 ui tests green at 100% coverage, typecheck clean. Refs: #242
Removes the card/list render branches and view-mode switcher from the 4 remaining non-container views. AgentsView drops its non-table EmptyState (DataTable empty slot covers it); SecurityView keeps its breakpoint-driven isCompact column swap; TriggersView's inline test button moves into the DataTable actions column. 3763 ui tests green at 100% coverage, typecheck clean. Refs: #242
Removes the ~427-line DataCardGrid/DataListAccordion render block and the containerViewMode switcher from ContainersGroupedViews/ContainersView/ContainersListContent; the column picker is now always shown, and the template-context type + its typecheck guard drop containerViewMode. Dead helpers (getContainerViewMemo, getGroupByKey) removed. 3747 ui tests green at 100% coverage, tsc clean. Refs: #242
Removes DataCardGrid.vue and DataListAccordion.vue (now-unused card/list surfaces) plus useViewMode.ts and auditViewMode.ts, each with its spec. Cleans up the main.ts global registrations, the preferences index export, the ComponentImports/main registration assertions, the data-view stubs, and rewrites the lifecycle spec off useViewMode. 3674 ui tests green at 100% coverage, tsc clean. Refs: #242
The table/cards/list switcher is dead now that every view is table-only. Removes the modelValue/viewModes props, the update:modelValue emit, defaultViewModes, viewModeName/viewModeLabel, and the switcher button group; keeps the count display, filter toggle, badge, slots, and filter panel. Test stubs simplified to match. 3669 ui tests green at 100% coverage, tsc clean. Refs: #242
Every view is table-only now, so viewMode/mode are dead. Bumps CURRENT_SCHEMA_VERSION 7->8, removes containers.viewMode and views.*.mode from the schema and DEFAULTS (security/agents keep their sort fields; the mode-only views collapse to empty stubs), and strips the dead migration branches (SIMPLE_VIEW_MODE_KEYS, migrateSimpleViewModePreferences, migrateAuditViewPreference, the mode logic in migrateSortableViewPreference/migrateContainersPreference/sanitizeContainers). Legacy dd-*-view-v1 keys stay in LEGACY_KEYS so they're still cleaned up, just not migrated. mergeDefaults drops any persisted mode field, so no explicit v7->v8 transform is needed. 3657 ui tests green at 100% coverage, tsc clean. Refs: #242
After schema v8 dropped every view-mode field, the ViewMode type, VIEW_MODES set, and isViewMode guard have zero consumers. Removes them from schema.ts/validators.ts and the preferences index re-export, with their validators.spec/index.spec tests. 3651 ui tests green at 100% coverage, tsc clean. Refs: #242
The v1.5.1 brand refresh (#439) moved the website to the cropped whale
"headshot" icon but left the app UI on the old full-body whale. Ship the
same icon set in ui/ so the product matches getdrydock.com.

- Replace ui/public favicon.ico, favicon-96x96.png, apple-touch-icon.png
  and both web-app-manifest PNGs with the website's headshot assets
- Remove the stale favicon.svg (browsers preferred it over the PNGs, so it
  kept rendering the old mark) and its <link>; the website ships no SVG
- Add a ?v=2 cache-buster to the icon links so existing installs re-fetch
  instead of serving the aggressively cached old favicon
- Match the website manifest's "any maskable" PWA icon purpose

Ref: #439
…ast (#466)

The SPA history-mode fallback returned index.html for every unmatched
path, including a request for a content-hashed /assets/*.js bundle that a
prior build referenced but the upgrade deleted. Browsers won't execute a
module script served as text/html, so a page cached from the old build
(often by a reverse proxy that ignores the HTML's Cache-Control: no-store)
white-screened, and caching proxies could poison the asset URL with HTML.

- 🐛 fix(api): return a clean 404 (no-store) for /assets/* misses in the
  UI catch-all instead of the SPA shell (app/api/ui.ts)
- ✅ test(api): cover the 404 branch + the index.html fallback branch
  (app/api/ui.test.ts — 100% coverage on ui.ts)
- 📝 docs(server): add a "Reverse proxy caching (blank page after an
  update)" section with browser/proxy cache guidance + per-proxy notes

Doesn't self-heal an already-blank page (clearing the browser/proxy cache
is still the fix for that), but stops drydock turning a missing bundle
into a silent white screen.

Ref: #466
C8-fix for #242 responsive DataTable — corrects the a11y/touch defects shipped in 4b11bd8:
- logical inset props (start-0/end-0) so the sticky identity + actions columns render correctly in RTL (ar)
- z-stack fix: header sticky cell z-20 out-stacks body sticky cell z-10
- 48px min touch row height (Material 3), overscroll-x-contain (Safari back-nav), scope=col on all header cells
- new per-theme --dd-sticky-separator token (>=3:1, WCAG 1.4.11) replacing the sub-threshold grey box-shadow; avoids reusing the 1.58:1 --dd-border-strong

Follow-up (not in this commit): AppIconButton size=toolbar renders 32px — clears WCAG 2.5.8 min (24px) but under the 44px target.
…ainer width

- ✨ feat(ui): card mode renders each row as a stacked card (title from the
  first non-icon column, priority-or-order subtitle, dt/dd body) reusing the
  existing cell/actions/full-row slots — no per-view changes needed
- ✨ feat(ui): native sort select + direction toggle replace the clickable
  table header in card mode, emitting the same update:sortKey/update:sortAsc
- 🌐 i18n(ui): add sharedComponents.dataTable.sortBy + sort direction labels
- ✅ test(ui): 41 new card-mode specs (breakpoint boundaries, slot reuse,
  sort control edge cases, virtual-scroll spacers, 44px touch targets,
  logical-properties-only RTL guard)

Card mode triggers on DataTable's own container width (ResizeObserver),
deliberately separate from the 768px window-based isMobile prop.

Refs: #242
- ✨ feat(ui): first @media print rules in the app — cards keep
  break-inside: avoid so a card never splits across pages, the card-mode
  sort chrome (select + direction toggle) is hidden on paper, and the
  table's horizontal scroll container becomes overflow: visible so
  off-screen columns print instead of being clipped
- ✅ test(ui): source-assertion specs for the print block (house pattern
  from the column-sizing standard) + DOM assertions for the two new
  hook classes (dd-data-table-scroll, dd-data-table-card-sort-bar)

Refs: #242
… composition

- ✨ feat(ui): new DataTableColumn fields — cardTitle forces a column to
  be the card title (table mode unaffected); cardPriority drives subtitle
  promotion (highest positive wins, ties by declared order) and body
  demotion (negative = out of the card; data stays reachable via the
  DetailPanel). Deliberately distinct from the auto-hide priority field,
  whose semantics run the opposite direction (higher = dropped first) —
  Containers pipes those values through, so reusing them would have
  promoted registry/uptime as the subtitle
- 🐛 fix(ui): NotificationsView card title was the bare enabled
  ToggleSwitch (first non-icon column) — name is now flagged cardTitle,
  triggers is the subtitle
- 🐛 fix(ui): AuditView card title was the raw timestamp — containerName
  is now the title, action the subtitle
- ✨ feat(ui): density annotations — Containers subtitle = update kind
  with server/registry/uptime demoted; Watchers demotes cron; Agents
  subtitle = status with docker/os demoted; Servers subtitle = status;
  Security wide branch subtitle = critical
- 🐛 fix(ui): Dashboard recent-updates widget mobile columns had empty
  labels, rendering blank dt labels in card mode — now reuse the desktop
  i18n keys
- ✅ test(ui): 27 new specs across DataTable, useColumnVisibility, and
  eight view suites, incl. regression proof that the auto-hide priority
  field no longer influences card composition

Refs: #242
- ✨ feat(ui): new useFocusTrap composable — saves the previously focused
  element on activation, focuses the container (tabindex=-1), cycles
  Tab/Shift+Tab within focusable descendants, and restores focus on
  deactivation or scope disposal when the saved element is still connected.
  Built on a post-flush watchEffect because watch with immediate+post still
  runs its first call synchronously at setup, before template refs bind
- ✨ feat(ui): DetailPanel activates the trap only when open on mobile —
  the desktop panel is a non-modal sticky sidebar that stays open across
  row clicks, so trapping or moving focus there would break keyboard
  row-browsing (matches the existing conditional aria-modal)
- 🐛 fix(ui): mobile panel offset used physical right-0 — now logical
  end-0 so the overlay anchors correctly in RTL locales
- ✅ test(ui): 15 composable specs (activation, restore, disconnected
  restore target, wrap both directions, no-focusables, disposal) + 6
  DetailPanel specs incl. desktop must-not-steal-focus guards

Refs: #242
…only

User column pickers hide columns from the table, but card mode (<640px
container) deliberately ignores the hidden set — mobile has no picker to
un-hide a column, so every non-demoted column must stay reachable there.

- ✨ feat(ui): hiddenColumnKeys prop filters the table pipeline at
  normalizedColumns, so flex width redistribution excludes hidden
  columns and the template loops need no changes; full-width/spacer
  colspans follow the filtered count
- ✨ feat(ui): card computeds now read an unfiltered normalized set
  (cardResolvedColumns) with a card-side first-non-icon fallback, so the
  card title stays stable even when its column is table-hidden
- ✅ test(ui): 10 specs — th/td/colgroup omission, width redistribution,
  sticky-column reflow when the first non-icon column is hidden, colspan
  shrink, card-mode precedence (hidden column reappears in cards), and
  composition with cardPriority demotion (each mechanism in its own mode)

Refs: #242
…ble, picker)

- ✨ feat(ui): defaulted views.{security,watchers,servers,audit,agents}
  .hiddenColumns preference fields — persisted as the HIDDEN set (not the
  visible set, unlike Containers) so columns added in future releases stay
  visible for existing users; no schema version bump needed, mergeDefaults
  backfills; sanitizeViews clamps garbage, unknown keys, and required keys
- ✨ feat(ui): useViewColumnVisibility composable — per-call state seeded
  from preferences with deep-watch write-back, required-column toggle
  no-op, hiddenCount scoped to the current column set, reset
- ✨ feat(ui): shared DataTableColumnPicker component modeled on the
  Containers picker (config icon trigger + hidden-count badge, teleported
  flip-aware popover, Escape/outside-click close), hidden below 640px —
  no picker on mobile since card mode ignores hiding; required rows never
  emit toggle
- 🌐 i18n(ui): sharedComponents.columnPicker keys (tooltip, heading,
  reset, badge tooltip)
- ✅ test(ui): 41 specs across sanitize clamps, composable persistence,
  and picker interaction; preferences .ts files stay gate-enforced at 100%

Refs: #242
…and Audit

- ✨ feat(ui): each view marks its identity column required, derives
  pickerColumns from tableColumns, persists hiding via
  useViewColumnVisibility, and renders DataTableColumnPicker in the
  filter bar's extra-buttons slot
- ♻️ refactor(ui): SecurityView's compact (<1024px window) branch no
  longer swaps to a two-column set — tableColumns always returns all 7
  and the severity columns are force-hidden via the hiddenColumnKeys
  union instead, so card mode finally sees the full set (critical
  subtitle annotation engages on mobile); the picker hides in compact so
  users can't toggle columns the override would re-hide
- 🐛 fix(ui): SecurityView spec mocked useBreakpoints with plain objects
  instead of refs, so template auto-unwrap never applied and every
  isCompact v-if was constantly truthy in tests — dormant infra bug, now
  real refs
- ✅ test(ui): sync guards pinning each view's column keys to
  VIEW_TABLE_COLUMN_KEYS, picker render/toggle/persistence per view,
  Security compact-union and picker-absence cases

Refs: #242
… path

- ♻️ refactor(ui): delete the bespoke picker (in-memory visible-set ref,
  non-teleported popover, hand-rolled positioning) in favor of
  DataTableColumnPicker + useViewColumnVisibility
- ✨ feat(ui): picker choices now persist to
  preferences.views.agents.hiddenColumns — the bespoke picker had no
  persistence and reset on every reload
- ♻️ refactor(ui): the compact (<1024px window) collapse to required
  columns now flows through the hiddenColumnKeys union instead of
  pre-filtering, so card mode sees the full 7-column set; the in-name
  compact badge row is unchanged
- 🗑️ remove(ui): dead agentsView.list.toggleColumns/columnPickerHeading
  keys across all 17 locales (key-parity gate forbids orphans)
- ✅ test(ui): sync guard, required flag, persistence, compact
  union/picker-absence, badge-row cases; useBreakpoints mock switched to
  real refs (same dormant auto-unwrap bug class as SecurityView.spec)

Refs: #242
Containers pre-filtered its columns before DataTable saw them, so
picker-hidden and width-auto-hidden columns vanished from mobile cards —
where no picker exists to bring them back.

- 🐛 fix(ui): tableColumns now maps the full column catalog and hiding
  flows through DataTable's hiddenColumnKeys prop (picker-hidden ∪
  width-auto-hidden), which card mode ignores by design; the uptime
  ticker stays gated on picker-visibility so opt-out users pay no cost
- ♻️ refactor(ui): ContainersListContent adopts the shared
  DataTableColumnPicker; bespoke popover, showColumnPicker plumbing, and
  global click/scroll handlers deleted; new resetColumns restores the
  shipped default set (opt-in uptime stays hidden)
- 🗑️ remove(ui): activeColumns (zero consumers) and the +N auto-hidden
  badge (picker-hidden count is the actionable signal); dead
  containerComponents.listContent picker keys purged from all 17 locales
- ✅ test(ui): hiddenColumnKeys cases (picker-only, auto-only, union,
  opt-in column), reset, wiring assertions across five containers specs

Refs: #242
- 🗑️ remove(ui): dataFilterBar.viewMode/viewModeLabel/viewModes.* (the
  switcher died in a76780f) and dataCardGrid/dataListAccordion keys
  (components died in d3329a0) from sharedComponents
- 🗑️ remove(ui): dead per-view card/list label sets (audit, security,
  registries, servers, triggers, watchers, agents) — every removal
  backed by a zero-consumer grep incl. template-literal t() sweeps
- 🌐 i18n(ui): securityView.card.total kept (live in
  SecurityDetailPanel); all 17 locales updated in lockstep, -884 lines

Refs: #242
scttbnsn added 7 commits July 17, 2026 08:56
Closes the round-7 review finding on #550 plus two adjacent test-hygiene
issues found by an adversarial pass over the same surface, so the whole
batch lands in one review round.

## Changes

**Version cache reset (CodeRabbit finding on #550,
`app/api/portwing-ws.test.ts:324`)**
- `injectDrydockVersionForTesting` now accepts `undefined` to clear the
module-level `_drydockVersion` cache (the `!== undefined` guard in
`drydockVersion()` makes it naturally re-prime from `getVersion()`).
- The welcome-version test resets the cache before
`vi.mocked(getVersion).mockClear()`, so it stays order-independent: an
earlier inject or welcome frame can no longer leave the cache primed and
let the test pass without exercising the canonical `getVersion()` path.

**Same bug class one describe block down**
- The injector suite ended by "restoring" the cache to a literal
`'1.5.0'` — leaving every later test in the file primed with a stale
magic value, the exact order-dependency this PR fixes. It now resets to
`undefined` like the new idiom.

**Duplicate test scenario in the orchestration suite**
- `keeps an errored broken reference on the fast path and preserves its
digest` (repair-branch digest stickiness) and `repairs a broken
reference for an errored container on the fast path` (fast-path
eligibility) ran byte-identical arrange/act; the newer test asserts a
strict superset. Folded into one test whose name now carries both
intents. Coverage verified 100% across all metrics at directory scope
after the merge.

**Comment accuracy in `container-processing.ts`**
- The catch-block comment implied release-notes enrichment can reject
after a fresh result; `enrichContainerWithReleaseNotes` swallows its own
errors and never rejects, so the guard is defense-in-depth. The comment
now says so instead of overstating the live path.

## Verification

- `npx vitest run api/portwing-ws.test.ts
watchers/providers/docker/container-processing.test.ts
watchers/providers/docker/docker-image-details-orchestration.test.ts` —
158/158 pass.
- Directory-scope coverage: `docker-image-details-orchestration.ts` and
`container-processing.ts` both 100% branches/lines/functions/statements.
- Full pre-push gate (biome, qlty, coverage, build) green on push.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Changelog

- ✨ Added support for clearing the cached Drydock version with
`undefined`.
- 🔧 Changed API tests to reset the version cache, preventing
order-dependent behavior.
- 🔧 Clarified the defensive result-restore comment in Docker container
processing.
- 🗑️ Removed a duplicate errored fast-path repair test and consolidated
digest assertions.

## Concerns

- Verify the targeted test suite and pre-push checks remain green.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
…rors (#559)

## Summary

- CI flake: `docker.test.ts` asserts the exact message `Scanner worker
timed out after 10ms`, but `waitForTimeout(timeoutMs)` bakes whatever
value it's armed with into the `DockerScannerTimeoutError` message. The
`run()` call site at the container-start phase arms the timer with
`remainingTimeoutMs` (configured `timeoutMs` minus elapsed setup time),
so if even ~1ms elapses before that timer arms, the message reads e.g.
`"9ms"` instead of the configured `"10ms"`. Failed twice on `main` run
29657499692.
- Root-cause fix: `waitForTimeout` now takes an optional
`reportedTimeoutMs` (defaults to `timeoutMs`) used only in the error
message, while `setTimeout` still arms with the real remaining budget.
The call site that passes `remainingTimeoutMs` now also passes
`runOptions.timeoutMs` as the reported value, so the displayed timeout
always matches what was configured — decoupled from how much budget was
actually left when the timer armed.
- Added a deterministic regression test (`reports the configured timeout
even when the deadline is armed mid-flight`) that mocks `Date.now` to
force a non-zero elapsed-setup remainder before the container-start
phase arms its timer, then asserts the rejection message still reads
`Scanner worker timed out after 10ms`.

This is test-hygiene for the rc.3 line — no runtime behavior change
beyond the error message text (the timer still fires after the same
remaining budget as before).

## Test plan

- [x] `npx vitest run security/backends/docker.test.ts` — 63/63 passed
- [x] `npx vitest run --coverage security/backends/` — `docker.ts` at
100% lines/branches/functions/statements in
`coverage/coverage-summary.json`
- [x] `npx biome check security/backends/docker.ts
security/backends/docker.test.ts` — clean
- [x] Full local pre-push gate (biome, qlty, qlty-smells,
workflow-tests, typecheck, coverage, build) — all green

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Changelog

- 🐛 Fixed scanner timeout errors to report the configured timeout
instead of the reduced remaining budget.
- 🔧 Updated `waitForTimeout` to separate timer duration from reported
timeout.
- ✨ Added a deterministic regression test covering elapsed setup time.
- ✅ Docker tests, coverage, formatting, and pre-push checks pass.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
…ession cookie, CSP, CI secret scans (#567)

## What

Nine security-hardening findings from the 2026-07-20 internal security
review, as 12 atomic commits plus CHANGELOG entries:

- **SSRF** (`app/triggers/providers/http`, Hass webhook URL handling,
registry/icon fetches): guarded DNS lookup blocks
cloud-metadata/link-local resolution on every request (override:
`allowmetadata=true`), and redirects can't hop into blocked address
space (DNS-rebinding + cross-host redirect protection).
- **WebSocket origin validation** (`app/api/ws-upgrade-utils.ts`): full
scheme/host/port origin comparison;
`X-Forwarded-Proto`/`X-Forwarded-Host` honored only with trust proxy
enabled; socket transport used otherwise.
- **Anonymous auth fails closed on upgrades**
(`app/authentications/providers/anonymous`): the warn-and-serve
grandfather path is gone — no auth configured (or anonymous unconfirmed)
now refuses to start, same as fresh installs.
`DD_ANONYMOUS_AUTH_CONFIRM=true` is the explicit opt-in. **Behavior
change for upgraders running open dashboards — called out in the
CHANGELOG.**
- **Store permissions**: `/store` created `0700` (Dockerfile), `dd.json`
`0600`, `umask 077` in the entrypoint and store process so Loki autosave
temp/replacement files never widen.
- **Application-scoped session cookie**: `drydock.sid` replaces the
Express default `connect.sid`. **One-time sign-out on upgrade.**
- **Icon CDN pinning** (`app/api/icons/providers.ts`):
dashboard-icons/selfh.st/simple-icons pinned to exact revisions/versions
instead of floating refs.
- **Website CSP** (`apps/web`): static `'unsafe-inline'` header CSP
replaced by a request-scoped nonce CSP built in `src/proxy.ts` per
Next.js guidance; `experimental.sri` stays off (the existing guard test
still enforces that) and the vercel.json CSP header is removed in favor
of the nonce policy. New `content-security-policy.test.mjs` covers the
builder.
- **CI secret scanning**: checksum-pinned Gitleaks in `ci-verify.yml`
with an exact-baseline `.gitleaksignore` (427 historical entries) and
`scripts/scan-secrets.sh` for local parity.
- **Dependency-test repairs**: the demo/scripts lockfile security tests
now accept patched upgrade ranges instead of pinning to vulnerable-era
versions.

## Why

Internal security review follow-up (report kept local by policy).
Targets the v1.6.1/rc.3 train via `dev/v1.6` — nothing here touches the
rc.2 soak or the GA promotion candidate.

## Tests

Full pre-push gate green on this exact branch (cut from
`origin/dev/v1.6`, cherry-picked 1:1 from the review line): app and ui
coverage gates at 100% on all four metrics, builds, biome, qlty,
workflow tests. New coverage: forwarded-protocol WS branches,
fail-closed upgrade auth, SSRF lookup guards, store-permission
enforcement, CSP builder.

## Notes for review

- The anonymous-auth fail-closed change and the session-cookie rename
are the two user-visible behavior changes; both have explicit CHANGELOG
entries with migration guidance.
- `vercel.json` loses its static CSP intentionally — the nonce policy
from `proxy.ts` supersedes it (a static header can't carry a per-request
nonce).


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Changelog

- 🔒 Security: Hardened SSRF protection with DNS re-resolution,
metadata/link-local address blocking, pinned DNS results, and disabled
redirects (`maxRedirects: 0`).
- 🔒 Security: Strengthened WebSocket `Origin` validation by checking
both effective protocol + host with TLS/trust-proxy awareness.
- ⚠️ Breaking: Anonymous authentication now requires explicit
`DD_ANONYMOUS_AUTH_CONFIRM=true` and fails closed during upgrade without
confirmation.
- ⚠️ Breaking: Renamed the session cookie to `drydock.sid`, updating
CSRF and OpenAPI security scheme configuration and related tests.
- 🔒 Security: Enforced restrictive filesystem permissions for the store
(directory/file modes), Docker `/store` permissions (`0700`), and Docker
entrypoint-created files via `umask 077`; store permission repair now
avoids the TOCTOU window by handling `ENOENT` only.
- 🔒 Security: Replaced moving icon CDN references with pinned
versions/immutable upstream revisions for all affected providers.
- 🔒 Security: Implemented request-scoped nonce CSP handling for the
website (proxy-injected CSP + per-request nonce), removed global static
CSP header behavior, and ensured nonce-safe JSON-LD rendering via a
shared `JsonLd` component.
- ✨ Added: CI “secrets” security job using checksum-pinned Gitleaks with
full-history + tracked-tree scanning, repository-local script-driven
invocation, and a historical baseline ignore configuration.
- 🔧 Changed: Updated dependency-security tests to accept patched semver
upgrade ranges; expanded SSRF/WebSocket test fixtures for
axios/proxy/trust-proxy correctness; tightened lockfile vulnerability
tests to scan nested package paths.
- 🐛 Fixed: Updated and repaired security/format/workflow/integration
tests to match hardened behavior (store perms, cookie rename, pinned
icons, CSP/nonce propagation, SSRF DNS guards, and CI secrets scanning
assertions).

## Concerns

- Large, line-specific `.gitleaksignore` baseline may be brittle as repo
files evolve—ensure it stays aligned with the pinned scanning commands
and modes.
- Confirm all runtime clients/integrations tolerate the `drydock.sid`
cookie rename (including any external reverse proxies or browser
automation).
- Validate CSP nonce propagation in all paths (dev vs prod,
caching/prefetch, and proxy matcher exclusions) and ensure no leftover
static CSP header conflicts.
- Review DNS guard correctness across IPv4/IPv6, `all: true` lookups,
and connection-time resolution failures; ensure metadata/link-local
detection is complete for every resolved address.
- Ensure store permission enforcement + repairs behave correctly under
all failure modes (only `ENOENT` swallowed; all other errors rethrown)
and that `umask` is correctly captured/restored.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
…te identity changes (#568)

## What

Fixes the #565 report: a manual per-container "check for updates"
restarted the maturity soak countdown, which reads to the user as their
maturity settings being wiped (the config itself was never touched —
only the clock).

Root cause: `getUpdateLifecycleTimestamp`'s restart branch gated on
`Container#resultChanged`, which compares `tag`, `suggestedTag`,
`digest`, AND `created`. The last two are display metadata that can
drift between scans (a manual recheck bypasses the registry poll cache),
so a cosmetic wobble re-stamped `updateDetectedAt` to now and the
countdown snapped back to the full window. Registries with trusted
publish dates (Docker Hub/GHCR) mask this via the publishedAt floor;
everyone else is fully exposed.

Fix: new `hasCandidateIdentityChanged()` comparing only `tag` +
`digest`; the lifecycle restart branch uses it directly.
`resultChanged`/`hasResultChanged` are untouched — the
trigger-notification `changed` flag in `container-processing.ts` must
stay sensitive to `suggestedTag`/`created` changes for notification
content. Intentional behavior is preserved: a genuinely new candidate
(tag or digest change) still restarts the soak.

## Why

Diagnosed against the live module (drove `updateContainer` through the
exact recheck sequence): identical results leave the clock alone; a
`suggestedTag`-only or `created`-only difference reset it every time.
Pre-existing since the v1.5.x maturity gate (restart branch from
v1.5.1-rc.1, comparison set from v1.5.0) — not an rc.2 regression.
Targets the v1.6.1/rc.3 train.

## Tests

Unit coverage for `hasCandidateIdentityChanged` (undefined handling both
directions, each field diff, metadata-only diff → false) plus three
store-level regression tests on `updateContainer`: metadata-only recheck
preserves `updateDetectedAt`; tag change resets; digest change resets.
Full pre-push gate green; `app/model/container.ts` and
`app/store/container.ts` at 100% on all four metrics.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Changelog

- 🐛 Fixed maturity soak countdowns restarting when only display metadata
changes.
- ✨ Added `hasCandidateIdentityChanged()` to compare update candidate
identity using only `tag` + `digest`.
- 🔧 Updated lifecycle timestamp logic to restart only on candidate
identity (`tag`/`digest`) changes (leaving existing `resultChanged`
behavior for trigger notifications intact).
- ✨ Added unit + store regression tests covering metadata-only changes,
tag/digest changes, and undefined handling.
- 🔒 Security: Updated `brace-expansion` minimum safe version (5.0.7) and
related e2e override validation.
- 🔧 Dependency: Bumped pinned `js-yaml` override (4.2.0 → 4.3.0) and
`brace-expansion` override (5.0.6 → 5.0.7).

- Ensure maturity restart logic uses the correct “raw update” `result`
shape consistently across persistence/rechecks.
- Verify `hasCandidateIdentityChanged()` undefined/missing-field
semantics align with actual runtime inputs.
- Confirm trigger notification paths still rely on existing
`resultChanged` behavior as intended.
- Run/verify unit + store tests and e2e security suite with the updated
dependency overrides.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
…delta (#569)

## What

Docs/copy freshness pass for the v1.6.1 delta (the #567 security batch,
the #566 display batch, and the #568 maturity-clock fix). 30 findings
from an adversarially-verified audit workflow (8 surface auditors, every
finding independently re-verified against the shipped code before it
counted; 1 rejected in verification), applied as one coherent edit per
doc region:

- **Anonymous-auth fail-closed (A1)**: the authentications page
(callout, behavior matrix, and the "Upgrading from v1.3.x" section), the
security guide, the README banner + quickstart line, and DEPRECATIONS.md
all still promised upgraders a warn-and-serve grace period. Rewritten
everywhere: upgrades now fail closed exactly like fresh installs;
`DD_ANONYMOUS_AUTH_CONFIRM=true` is the opt-in.
- **Session cookie rename (A4)**: `connect.sid` → `drydock.sid` one-time
sign-out documented in DEPRECATIONS.md, the upgrading section, and the
README banner.
- **SSRF hardening (A2)**: the HTTP trigger page now documents the
guarded DNS lookup, redirect refusal, and the `ALLOWMETADATA` escape
hatch.
- **WebSocket origin checks (A3)**: server config, FAQ, quickstart
reverse-proxy warning, `api/log.mdx`, and `api/container.mdx` now state
that origin validation covers scheme/host/port and that forwarded
headers are honored only with `DD_SERVER_TRUSTPROXY`.
- **UI vocabulary (B3/B4/B5)**: watchers page and FAQ updated for the
pin-glyph/"Current" pinned treatment; eligibility + API docs document
the new additive `clockSource`/`clockStartAt` blocker fields; the UI
page covers the column-picker auto-hidden annotation. Wording grounded
in the actual #566 locale strings, not paraphrased.
- **Website quickstarts actually start**: the shared `DockerRunSnippet`
(all 7 comparison pages) and the FAQ quickstart now include the auth
opt-in env var — without it the one-liner produces a container that
refuses to start on the v1.6 line.

Also rides along: `protobufjs` pinned to 7.6.5 (CVE-2026-59877 /
GHSA-j3f2-48v5-ccww, third fresh advisory today) in app (new override)
and e2e (existing override bumped) — it began failing the qlty
osv-scanner gate mid-push.

Deliberately skipped: `content/docs/current/updates/index.mdx` (strictly
per-release curated notes; rc.3 highlights belong to the actual cut) and
`UPGRADE-NOTES.md` (version-gated to 1.4.6–1.5.x by
`scripts/append-upgrade-notes.mjs`; v1.6 content there would leak into
1.5.x release notes).

## Why

The security batch merged with zero docs changes, leaving the auth docs
actively contradicting shipped behavior. Docs on `dev/v1.6` publish at
the GA dev→main merge, so these corrections go live exactly when the
behavior does.

## Tests

Full pre-push gate green. apps/web script tests 43/43; MDX parse
validated via sync-docs + fumadocs-mdx; biome clean.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Changelog

- 🔒 Documented fail-closed anonymous authentication for both fresh
installs and upgrades; require `DD_ANONYMOUS_AUTH_CONFIRM=true` (and
clarified `/health` vs API response behavior).
- ⚠️ Security documented “no deprecation window” enforcement: removed
upgrade grandfather path when anonymous auth isn’t configured/confirmed;
upgrades stop serving/fail closed.
- ⚠️ 🔒 Documented `connect.sid` → `drydock.sid` session cookie rename
and one-time sign-out on upgrade.
- 🔒 Documented SSRF protections for guarded DNS lookup, redirect refusal
(3xx never followed), and `ALLOWMETADATA` escape hatch.
- 🔒 Documented WebSocket `Origin` validation (scheme/host/port) and
clarified reverse-proxy requirements via `DD_SERVER_TRUSTPROXY` +
forwarded headers (`X-Forwarded-Proto`/`X-Forwarded-Host`), including
log-stream connection failures when misconfigured.
- ✨ Documented maturity-clock details for `maturity-not-reached` (clock
source + ISO-8601 start timestamp payload) and UI countdown rendering
behavior.
- ✨ Updated UI copy/annotations for column visibility and “hidden to
fit” behavior.
- ✨ Updated watcher UI description for fully-pinned tags to reflect
`updateInsight` chip styling + persistent pin glyph/tooltips.
- 🔧 Added `DD_ANONYMOUS_AUTH_CONFIRM=true` to website quickstarts and
updated Docker run snippet wording.
- 🔧 Pinned `protobufjs` to `7.6.5` in both app and e2e dependencies.
- 🔧 Updated doc identity/release tests to assert current fail-closed
semantics for `content/docs/current` while preserving legacy v1.5
behavior.

## Concerns

- Confirm `DD_SERVER_TRUSTPROXY` docs explicitly require forwarding
`X-Forwarded-Proto` and `X-Forwarded-Host` for scheme/host matching.
- Validate that the documented fail-closed behavior matches the actual
endpoints: API 401 behavior vs `/health` 503 reporting.
- Ensure the “redirects never followed” documentation aligns with the
actual redirect-following disablement and how `ALLOWMETADATA` is
evaluated.
- Verify cookie rename handling is documented consistently across all
relevant upgrade/install docs (including one-time sign-out semantics).
- Check that WebSocket origin validation requirements cover both
dashboard log viewer and container log streaming endpoints (upgrade vs
steady-state behavior).
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
…ph, one-clock maturity panel (#566)

## What

The display-honesty batch: renames and removes the update-status
vocabulary that #498 and #556 reported as unreadable, based on a
peer-tool UX research pass (WUD, Diun, Watchtower, Cup, Dockge, Arcane,
Portainer, Komodo, Home Assistant, Renovate/Dependabot) and an
owner-approved design.

- **"Digest" → "Digest update"** in the Update column (Cup's web-UI
string, Arcane's popover title). A digest-only change now reads as what
it is: an update.
- **NEW/MATURE badges removed.** The freshness fact moves into the
update-kind badge's tooltip ("Detected 3 days ago"). Idle state gets no
chrome; `UpdateMaturityBadge.vue` deleted.
- **"Pinned" leaves the Update column.** It was an update status
pretending to be a tag property (only rendered when a newer family
version existed — the exact inconsistency reported in #498). Pinned-ness
is now a persistent pin glyph on the Tag cell (existing `pin` icon-map
entry, not the padlock — that's a live action affordance), with a
tooltip carrying the `dd.tag.family=loose` remedy. Up-to-date pinned
rows read "Current" like everything else.
- **Maturity panel keeps exactly one clock.** The policy sentence names
the clock the gate actually measures against ("Candidate published
{date} — {n} more days until the {minDays}-day minimum", detection-date
fallback), and the duplicated countdown collapses to "{countdown} ·
unlocks {date}".
- **Backend rider:** `resolveMaturityClock()` in
`app/model/maturity-policy.ts` (behavior-preserving refactor of
`getMaturityStartMs`) now exposes `clockSource`/`clockStartAt` as
additive optional fields on the maturity blocker's `details`, and both
UI call sites (`container-mapper.ts`, `useContainerPolicy.ts`) consume
the backend verdict instead of re-deriving maturity-blocked from
`updateDetectedAt` alone — three drifting formulas become one.
- **Column picker honesty:** breakpoint-auto-hidden columns stay checked
but get a muted "{column} — hidden to fit" annotation, and the "+1"
toolbar badge tooltip names the hidden columns instead of a bare count.
- **Unknown-kind badge:** the silent unknown-update-kind fallback now
renders a neutral "Unknown" badge.
- **Bouncer "Blocked" → "Security hold"** (display string only; the
maturity/hard-block wording is untouched).

i18n: net 12 keys added / 7 removed / 12 values changed, English
catalogs only per convention, plus stale values for the *repurposed*
keys dropped from all 16 non-English locales so they fall back to
English until Crowdin retranslates (otherwise German would render the
old badge text "NEU" as a tooltip).

## Why

Direct follow-up to the rc.2 field feedback in #498 (Cesc1986) and the
#556 display cluster. Design researched and approved before
implementation; targets v1.6.1/rc.3 — nothing here touches the rc.2
soak.

## Tests

TDD gates green in full: app 100/100/100/100 (12,244 tests), ui
100/100/100/100 (4,236 tests). New coverage for the clock resolution
(source precedence + tie-breaks), blocker serialization, pin glyph,
unknown-kind fallback, column-picker annotation branches, collapsed
countdown, and both `useContainerPolicy` paths (backend-verdict
preferred, legacy fallback).

## Notes for review

- `dashboard-container-metrics.ts` kept its `freshUpdates` KPI via a
local `isFreshUpdate()` helper (same 7-day threshold) after the badge
helper's deletion — dashboard behavior unchanged.
- The owner does a rendered visual pass in the QA environment before
this merges.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Changelog

- ✨ Added backend-resolved maturity clocks with source/start metadata
and defensive eligibility parsing.
- ✨ Added shared update-kind labeling with localized `Unknown` fallback.
- ✨ Added pin-gate policy detection and `tagPinGated` propagation from
backend to UI.
- ✨ Added auto-hidden column annotations and accurate picker
badge/tooltips.
- 🔧 Changed update terminology: “Digest update” and “Security hold”.
- 🔧 Consolidated maturity countdowns into update-kind/status tooltips
and insight notes.
- 🔧 Moved pinned status to persistent pin glyphs in version/tag cells.
- 🔧 Preserved dashboard fresh-update KPI behavior with an independent
seven-day threshold.
- 🔧 Improved tooltip fallback synchronization and registry search by
dotted instance ID.
- 🔧 Updated pluralized legacy-configuration banner translations.
- 🗑️ Removed `UpdateMaturityBadge`, `getUpdateMaturity`, separate
NEW/MATURE badges, and the dedicated pinned update-status state.
- 🗑️ Removed stale localized `blockedByBouncer`, `updateAge`,
maturity-badge, and pinned-summary values.
- ⚠️ Updated the `Container` contract: removed `updateMaturity`; added
`updateMaturityTooltip`, `updatePolicyState`, and `tagPinGated`.

## Concerns

- Verify backend `updateEligibility` payloads expose sufficiently
reliable maturity clock metadata.
- Confirm Crowdin retranslation coverage for removed and repurposed
locale keys.
- Audit downstream consumers for the removed `getUpdateMaturity`,
`updateMaturity`, and `pinned` APIs.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Release plumbing for the v1.6.0-rc.3 cut — the full identity bump the
guard tests enforce.

- Dated `[1.6.0-rc.3] — 2026-07-21` CHANGELOG heading, fresh empty
`[Unreleased]` section, matching link-reference definitions
- rc.3 across README badge/highlights summary, website site-config +
timeline, updates page (new rc.3 highlights section), API doc example
payloads, quickstart tag matrix, and the demo runtime fixtures
- Guard tests updated in lockstep: release-identity,
release-docs-identity (compare base now `v1.6.0-rc.2...v1.6.0-rc.3`),
changelog-links

rc.3 delta over rc.2: #566 display-honesty batch, #567 security
hardening, #568 maturity-clock restart fix (#565), #569 docs freshness.

Verified: 105/105 script tests, 43/43 web-script tests, full pre-push
gate green.

Last dev-side change before the release head freeze.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Changelog

🔧 **Changed**
- Updated release identity from `v1.6.0-rc.2` to `v1.6.0-rc.3` across
README, website metadata, roadmap, docs, API examples, quickstart tags,
and demo fixtures.
- Added the dated `v1.6.0-rc.3` updates section and refreshed changelog
link references.
- Updated release identity and documentation guard tests, including rc
comparison ranges and candidate fixtures.

✨ **Added**
- Documented the rc.3 changes covering issues `#566`, `#567`,
`#568/`#565, and `#569`.

🔒 **Security**
- Documented the rc.3 security hardening batch.

## Verification

- Script tests: 105/105 passed
- Web-script tests: 43/43 passed
- Full pre-push gate: passed

## Concerns

- Verify the release date `July 21, 2026` is correct.
- Confirm all remaining `1.6.0-rc.2` references are intentional.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
@vercel

vercel Bot commented Jul 21, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
drydock-website Ready Ready Preview, Comment Jul 21, 2026 8:34pm
drydockdemo-website Ready Ready Preview, Comment Jul 21, 2026 8:34pm

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 212 files, which is 112 over the limit of 100.

To get a review, narrow the scope:
• coderabbit review --committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

Upgrade to a paid plan to raise the limit.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 03eeb03d-9ce5-4e40-9a68-71eca8c58f27

📥 Commits

Reviewing files that changed from the base of the PR and between 6d2b771 and 6526a69.

⛔ Files ignored due to path filters (6)
  • CHANGELOG.md is excluded by !CHANGELOG.md
  • app/package-lock.json is excluded by !**/package-lock.json, !**/package-lock.json
  • apps/demo/package-lock.json is excluded by !**/package-lock.json, !**/package-lock.json
  • apps/web/package-lock.json is excluded by !**/package-lock.json, !**/package-lock.json
  • e2e/package-lock.json is excluded by !**/package-lock.json, !**/package-lock.json
  • ui/package-lock.json is excluded by !**/package-lock.json, !**/package-lock.json
📒 Files selected for processing (214)
  • .github/tests/ci-verify-workflow.test.ts
  • .github/tests/release-cut-retry-workflow.test.ts
  • .github/workflows/ci-verify.yml
  • .github/workflows/release-cut.yml
  • .gitleaks.toml
  • .gitleaksignore
  • .planning/ROADMAP.md
  • .planning/roadmap/current-tracker.md
  • .planning/v1.6.0-MILESTONE-AUDIT.md
  • DEPRECATIONS.md
  • Docker.entrypoint.sh
  • Dockerfile
  • README.md
  • SECURITY.md
  • app/agent/AgentClient.test.ts
  • app/agent/AgentClient.ts
  • app/api/auth.test.ts
  • app/api/auth.ts
  • app/api/container/log-stream.test.ts
  • app/api/csrf.test.ts
  • app/api/icons.test.ts
  • app/api/icons/fetch.test.ts
  • app/api/icons/providers.test.ts
  • app/api/icons/providers.ts
  • app/api/log-stream.test.ts
  • app/api/openapi.test.ts
  • app/api/openapi/index.ts
  • app/api/openapi/schemas.test.ts
  • app/api/openapi/schemas.ts
  • app/api/preview-errors.test.ts
  • app/api/preview-errors.ts
  • app/api/preview.test.ts
  • app/api/session-cookie.ts
  • app/api/ws-upgrade-utils.test.ts
  • app/api/ws-upgrade-utils.ts
  • app/authentications/providers/anonymous/Anonymous.test.ts
  • app/authentications/providers/anonymous/Anonymous.ts
  • app/configuration/dockerfile-defaults.test.ts
  • app/model/container.test.ts
  • app/model/container.ts
  • app/model/maturity-policy.test.ts
  • app/model/maturity-policy.ts
  • app/model/update-eligibility.test.ts
  • app/model/update-eligibility.ts
  • app/package.json
  • app/registries/BaseRegistry.test.ts
  • app/registries/BaseRegistry.ts
  • app/registry/index.test.ts
  • app/registry/index.ts
  • app/security/backends/docker.test.ts
  • app/security/backends/docker.ts
  • app/store/container.test.ts
  • app/store/container.ts
  • app/store/index.test.ts
  • app/store/index.ts
  • app/tag/family.test.ts
  • app/tag/family.ts
  • app/triggers/providers/http/Http.test.ts
  • app/triggers/providers/http/Http.ts
  • app/triggers/providers/mqtt/Hass.test.ts
  • app/triggers/providers/mqtt/Hass.ts
  • app/watchers/providers/docker/Docker.ts
  • app/watchers/providers/docker/digest-cache-lifecycle.test.ts
  • app/watchers/providers/docker/digest-cache-lifecycle.ts
  • app/watchers/providers/docker/tag-candidates.ts
  • apps/demo/package.json
  • apps/demo/src/mocks/data/agents.ts
  • apps/demo/src/mocks/data/audit.ts
  • apps/demo/src/mocks/data/containers.ts
  • apps/demo/src/mocks/data/notifications.ts
  • apps/demo/src/mocks/data/server.ts
  • apps/demo/src/mocks/handlers.contract.test.ts
  • apps/demo/src/mocks/handlers/app.ts
  • apps/demo/src/mocks/handlers/json.ts
  • apps/demo/src/mocks/handlers/notifications.ts
  • apps/demo/src/mocks/handlers/settings.ts
  • apps/demo/tests/security/yaml-lockfile.test.js
  • apps/web/next.config.mjs
  • apps/web/package.json
  • apps/web/scripts/content-security-policy.test.mjs
  • apps/web/scripts/next-config.test.mjs
  • apps/web/src/app/compare/page.tsx
  • apps/web/src/app/data/faq.ts
  • apps/web/src/app/docs/[[...slug]]/page.tsx
  • apps/web/src/app/layout.tsx
  • apps/web/src/app/page.tsx
  • apps/web/src/app/security/trivy-supply-chain-march-2026/page.tsx
  • apps/web/src/components/comparison-page.tsx
  • apps/web/src/components/docker-run-snippet.tsx
  • apps/web/src/components/json-ld.tsx
  • apps/web/src/lib/content-security-policy.mjs
  • apps/web/src/lib/site-config.ts
  • apps/web/src/lib/site-content.ts
  • apps/web/src/proxy.ts
  • apps/web/vercel.json
  • content/docs/current/api/agent.mdx
  • content/docs/current/api/app.mdx
  • content/docs/current/api/container.mdx
  • content/docs/current/api/log.mdx
  • content/docs/current/api/portwing.mdx
  • content/docs/current/configuration/actions/update-eligibility.mdx
  • content/docs/current/configuration/authentications/index.mdx
  • content/docs/current/configuration/server/index.mdx
  • content/docs/current/configuration/triggers/http/index.mdx
  • content/docs/current/configuration/ui/index.mdx
  • content/docs/current/configuration/watchers/index.mdx
  • content/docs/current/faq/index.mdx
  • content/docs/current/guides/security/index.mdx
  • content/docs/current/quickstart/index.mdx
  • content/docs/current/updates/index.mdx
  • e2e/package.json
  • e2e/playwright/v16-modes-pins.spec.ts
  • e2e/tests/security/brace-expansion-lockfile.test.js
  • e2e/tests/security/yaml-lockfile.test.js
  • scripts/changelog-links.test.mjs
  • scripts/release-docs-identity.test.mjs
  • scripts/release-identity.test.mjs
  • scripts/scan-secrets.sh
  • ui/src/components/DataTableColumnPicker.vue
  • ui/src/components/containers/ContainerFullPageDetail.vue
  • ui/src/components/containers/ContainerFullPageOverviewTab.vue
  • ui/src/components/containers/ContainerFullPageTabContent.vue
  • ui/src/components/containers/ContainerSideTabContent.vue
  • ui/src/components/containers/ContainersGroupedViews.vue
  • ui/src/components/containers/ContainersListContent.vue
  • ui/src/components/containers/UpdateMaturityBadge.vue
  • ui/src/components/containers/UpdateStatusPanel.vue
  • ui/src/components/containers/containersViewTemplateContext.ts
  • ui/src/composables/useUpdateStatus.ts
  • ui/src/directives/tooltip.ts
  • ui/src/layouts/AppLayout.vue
  • ui/src/locales/ar/appShell.json
  • ui/src/locales/ar/containerComponents.json
  • ui/src/locales/de/appShell.json
  • ui/src/locales/de/containerComponents.json
  • ui/src/locales/en/appShell.json
  • ui/src/locales/en/containerComponents.json
  • ui/src/locales/en/sharedComponents.json
  • ui/src/locales/es/appShell.json
  • ui/src/locales/es/containerComponents.json
  • ui/src/locales/fr/appShell.json
  • ui/src/locales/fr/containerComponents.json
  • ui/src/locales/it/appShell.json
  • ui/src/locales/it/containerComponents.json
  • ui/src/locales/ja/appShell.json
  • ui/src/locales/ja/containerComponents.json
  • ui/src/locales/ko/appShell.json
  • ui/src/locales/ko/containerComponents.json
  • ui/src/locales/nl/appShell.json
  • ui/src/locales/nl/containerComponents.json
  • ui/src/locales/pl/appShell.json
  • ui/src/locales/pl/containerComponents.json
  • ui/src/locales/pt-BR/appShell.json
  • ui/src/locales/pt-BR/containerComponents.json
  • ui/src/locales/ru/appShell.json
  • ui/src/locales/ru/containerComponents.json
  • ui/src/locales/tr/appShell.json
  • ui/src/locales/tr/containerComponents.json
  • ui/src/locales/uk/appShell.json
  • ui/src/locales/uk/containerComponents.json
  • ui/src/locales/vi/appShell.json
  • ui/src/locales/vi/containerComponents.json
  • ui/src/locales/zh-CN/appShell.json
  • ui/src/locales/zh-CN/containerComponents.json
  • ui/src/locales/zh-TW/appShell.json
  • ui/src/locales/zh-TW/containerComponents.json
  • ui/src/services/preview.ts
  • ui/src/types/container.d.ts
  • ui/src/utils/container-mapper.ts
  • ui/src/utils/dashboard-container-metrics.ts
  • ui/src/utils/display.ts
  • ui/src/utils/update-eligibility.ts
  • ui/src/utils/update-kind-labels.ts
  • ui/src/utils/update-maturity.ts
  • ui/src/views/ContainersView.vue
  • ui/src/views/NotificationsView.vue
  • ui/src/views/RegistriesView.vue
  • ui/src/views/containers/useContainerPolicy.ts
  • ui/src/views/containers/useContainerPreview.ts
  • ui/tests/boot/i18n.spec.ts
  • ui/tests/components/ContainerSideTabContent.spec.ts
  • ui/tests/components/DataTableColumnPicker.spec.ts
  • ui/tests/components/containers/ContainerFullPageDetail.spec.ts
  • ui/tests/components/containers/ContainerFullPageOverviewTab.spec.ts
  • ui/tests/components/containers/ContainerFullPageTabContent.spec.ts
  • ui/tests/components/containers/ContainersGroupedViews.button-states.spec.ts
  • ui/tests/components/containers/ContainersGroupedViews.spec.ts
  • ui/tests/components/containers/ContainersListContent.spec.ts
  • ui/tests/components/containers/UpdateMaturityBadge.spec.ts
  • ui/tests/components/containers/UpdateStatusPanel.spec.ts
  • ui/tests/composables/useContainerFilters.spec.ts
  • ui/tests/composables/useDetailPanel.spec.ts
  • ui/tests/composables/useOperationDisplayHold.spec.ts
  • ui/tests/composables/useUpdateStatus.spec.ts
  • ui/tests/composables/useVulnerabilities.spec.ts
  • ui/tests/directives/tooltip.spec.ts
  • ui/tests/services/preview.spec.ts
  • ui/tests/utils/container-mapper.spec.ts
  • ui/tests/utils/dashboard-container-metrics.spec.ts
  • ui/tests/utils/display.spec.ts
  • ui/tests/utils/hide-pinned.spec.ts
  • ui/tests/utils/update-eligibility.spec.ts
  • ui/tests/utils/update-kind-labels.spec.ts
  • ui/tests/utils/update-maturity.spec.ts
  • ui/tests/views/ContainersView.applyContainerPatch.spec.ts
  • ui/tests/views/ContainersView.spec.ts
  • ui/tests/views/DashboardView.spec.ts
  • ui/tests/views/NotificationsView.spec.ts
  • ui/tests/views/RegistriesView.spec.ts
  • ui/tests/views/containers/useContainerActions.spec.ts
  • ui/tests/views/containers/useContainerLogs.spec.ts
  • ui/tests/views/containers/useContainerPolicy.spec.ts
  • ui/tests/views/dashboard/useDashboardComputed.spec.ts
  • ui/tests/views/dashboard/useDashboardData.spec.ts

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Review on demand using usage pricing
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev/v1.6

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Merge origin/main (#560 /store persistence quickstart, #561 rc.2-audit copy
freshness + SRI guard hardening, #562 README translations) into the rc.3
release head. Identity conflicts resolved to the rc.3 side with main's
copy/badge deltas re-applied (150K pulls, 20 providers, Node 24.7+, Biome
2.5, unified-clock roadmap row). Guard tests 105/105 + 44/44.
Closes every repository-actionable finding from the comprehensive v1.6 review, incorporates the superseded E2E correction from #573, and includes all follow-up review fixes and evidence hardening.
## Summary

- replace two secret-like bearer-token fixtures with low-entropy test
values
- preserve the strict cache-expiry boundary assertions
- restore the tracked-working-tree Gitleaks gate after earlier tests
shifted the allowlisted source lines

## Verification

- `node ./node_modules/vitest/vitest.mjs run
registries/BaseRegistry.test.ts --maxWorkers=1 --fileParallelism=false`
(221 passed)
- `./scripts/scan-secrets.sh` (history and tracked tree clean)
- full pre-push gate (100% app/UI coverage, app/UI builds, scripts,
workflows, typecheck, Zizmor)
@scttbnsn

Copy link
Copy Markdown
Contributor Author

Release-readiness update for exact head 6526a69dd14564076082645817421929093320e6:

The PR is mergeable and now blocked only on a requested maintainer approval. After approval, preserve this exact tree for the v1.6.0-rc.3 cut and the fresh 24-hour NAS store-size acceptance; any further runtime change invalidates that acceptance candidate.

@biggest-littlest biggest-littlest left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, release checks green.

@ALARGECOMPANY ALARGECOMPANY left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving release.

@scttbnsn
scttbnsn merged commit 0f90b16 into main Jul 21, 2026
28 checks passed
@scttbnsn
scttbnsn deleted the dev/v1.6 branch July 21, 2026 23:03
@scttbnsn
scttbnsn restored the dev/v1.6 branch July 21, 2026 23:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants