mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-07-26 20:00:08 +00:00
fix/routing-stack-refresh-shell
693 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9d2f76da6e |
fix(routing): show hydration shell on stack detail refresh
Avoid flashing HomeDashboard when activeView is editor but selectedFile is not loaded yet on cold load or hard refresh. |
||
|
|
7517a4f49c |
fix(routing): guard remote stack hydration and settle empty env routes (#1602)
Gate stack resolution until the active node matches a pending remote deep link. Return env inventory from loadFileForRoute so empty stacks settle env tab routes. Reject backslashes in buildPath env query tokens. |
||
|
|
1de9f5e4b2 |
fix(routing): basename env URLs and defer writes during node hydration (#1598)
* fix(routing): basename env URLs and defer writes during node hydration Encode only env file basenames in ?env= and omit the default file. Resolve legacy absolute-path bookmarks on load. Block history writes until the active node matches a cold-loaded remote deep link. * fix(routing): restore env deep links after stack load hydrates file list Defer env selection until envFiles is populated after loadFileForRoute. Apply default env when the URL omits ?env= (Back/popstate). Document env URL rules and legacy basename resolution in deep-links.mdx. |
||
|
|
ad5e3f38c8 |
chore(deps): bump the all-npm-frontend group in /frontend with 20 updates (#1596)
* chore(deps): bump the all-npm-frontend group Bumps the all-npm-frontend group in /frontend with 20 updates: | Package | From | To | | --- | --- | --- | | [@radix-ui/react-alert-dialog](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/alert-dialog) | `1.1.18` | `1.1.19` | | [@radix-ui/react-checkbox](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/checkbox) | `1.3.6` | `1.3.7` | | [@radix-ui/react-context-menu](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/context-menu) | `2.3.2` | `2.3.3` | | [@radix-ui/react-dialog](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/dialog) | `1.1.18` | `1.1.19` | | [@radix-ui/react-dropdown-menu](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/dropdown-menu) | `2.1.19` | `2.1.20` | | [@radix-ui/react-hover-card](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/hover-card) | `1.1.18` | `1.1.19` | | [@radix-ui/react-popover](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/popover) | `1.1.18` | `1.1.19` | | [@radix-ui/react-scroll-area](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/scroll-area) | `1.2.13` | `1.2.14` | | [@radix-ui/react-select](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/select) | `2.3.2` | `2.3.3` | | [@radix-ui/react-slider](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/slider) | `1.4.2` | `1.4.3` | | [@radix-ui/react-tabs](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/tabs) | `1.1.16` | `1.1.17` | | [@radix-ui/react-tooltip](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/tooltip) | `1.2.11` | `1.2.12` | | [@xyflow/react](https://github.com/xyflow/xyflow/tree/HEAD/packages/react) | `12.11.1` | `12.11.2` | | [radix-ui](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/radix-ui) | `1.6.1` | `1.6.2` | | [recharts](https://github.com/recharts/recharts) | `3.9.1` | `3.9.2` | | [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `26.1.0` | `26.1.1` | | [typescript](https://github.com/microsoft/TypeScript) | `6.0.3` | `7.0.2` | | [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.62.1` | `8.63.0` | | [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) | `8.1.2` | `8.1.3` | | [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) | `4.1.9` | `4.1.10` | Updates `@radix-ui/react-alert-dialog` from 1.1.18 to 1.1.19 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/alert-dialog/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/alert-dialog) Updates `@radix-ui/react-checkbox` from 1.3.6 to 1.3.7 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/checkbox/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/checkbox) Updates `@radix-ui/react-context-menu` from 2.3.2 to 2.3.3 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/context-menu/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/context-menu) Updates `@radix-ui/react-dialog` from 1.1.18 to 1.1.19 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/dialog/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/dialog) Updates `@radix-ui/react-dropdown-menu` from 2.1.19 to 2.1.20 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/dropdown-menu/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/dropdown-menu) Updates `@radix-ui/react-hover-card` from 1.1.18 to 1.1.19 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/hover-card/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/hover-card) Updates `@radix-ui/react-popover` from 1.1.18 to 1.1.19 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/popover/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/popover) Updates `@radix-ui/react-scroll-area` from 1.2.13 to 1.2.14 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/scroll-area/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/scroll-area) Updates `@radix-ui/react-select` from 2.3.2 to 2.3.3 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/select/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/select) Updates `@radix-ui/react-slider` from 1.4.2 to 1.4.3 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/slider/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/slider) Updates `@radix-ui/react-tabs` from 1.1.16 to 1.1.17 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/tabs/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/tabs) Updates `@radix-ui/react-tooltip` from 1.2.11 to 1.2.12 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/tooltip/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/tooltip) Updates `@xyflow/react` from 12.11.1 to 12.11.2 - [Release notes](https://github.com/xyflow/xyflow/releases) - [Changelog](https://github.com/xyflow/xyflow/blob/main/packages/react/CHANGELOG.md) - [Commits](https://github.com/xyflow/xyflow/commits/@xyflow/react@12.11.2/packages/react) Updates `radix-ui` from 1.6.1 to 1.6.2 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/radix-ui/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/radix-ui) Updates `recharts` from 3.9.1 to 3.9.2 - [Release notes](https://github.com/recharts/recharts/releases) - [Changelog](https://github.com/recharts/recharts/blob/main/CHANGELOG.md) - [Commits](https://github.com/recharts/recharts/compare/v3.9.1...v3.9.2) Updates `@types/node` from 26.1.0 to 26.1.1 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `typescript` from 6.0.3 to 7.0.2 - [Release notes](https://github.com/microsoft/TypeScript/releases) - [Commits](https://github.com/microsoft/TypeScript/commits) Updates `typescript-eslint` from 8.62.1 to 8.63.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.63.0/packages/typescript-eslint) Updates `vite` from 8.1.2 to 8.1.3 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v8.1.3/packages/vite) Updates `vitest` from 4.1.9 to 4.1.10 - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Changelog](https://github.com/vitest-dev/vitest/blob/main/docs/releases.md) - [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.10/packages/vitest) --- updated-dependencies: - dependency-name: "@radix-ui/react-alert-dialog" dependency-version: 1.1.19 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: "@radix-ui/react-checkbox" dependency-version: 1.3.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: "@radix-ui/react-context-menu" dependency-version: 2.3.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: "@radix-ui/react-dialog" dependency-version: 1.1.19 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: "@radix-ui/react-dropdown-menu" dependency-version: 2.1.20 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: "@radix-ui/react-hover-card" dependency-version: 1.1.19 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: "@radix-ui/react-popover" dependency-version: 1.1.19 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: "@radix-ui/react-scroll-area" dependency-version: 1.2.14 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: "@radix-ui/react-select" dependency-version: 2.3.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: "@radix-ui/react-slider" dependency-version: 1.4.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: "@radix-ui/react-tabs" dependency-version: 1.1.17 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: "@radix-ui/react-tooltip" dependency-version: 1.2.12 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: "@xyflow/react" dependency-version: 12.11.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: radix-ui dependency-version: 1.6.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: recharts dependency-version: 3.9.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: "@types/node" dependency-version: 26.1.1 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: typescript dependency-version: 7.0.2 dependency-type: direct:development update-type: version-update:semver-major dependency-group: all-npm-frontend - dependency-name: typescript-eslint dependency-version: 8.63.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: all-npm-frontend - dependency-name: vite dependency-version: 8.1.3 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: vitest dependency-version: 4.1.10 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: all-npm-frontend ... Signed-off-by: dependabot[bot] <support@github.com> * chore(deps): hold typescript on the 6.x line in frontend group typescript-eslint 8.63.0 supports typescript <6.1.0, so bumping to 7.0.2 crashes eslint (typescript-estree cannot read the TS7 native compiler API). Keep typescript at ^6.0.3 until typescript-eslint supports TS7; the rest of the group bump is unaffected. --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: SaelixCode <dev@saelix.com> |
||
|
|
5fe0843eb2 |
feat: add routable browser URLs for stacks and shell views (#1586)
* feat: add routable browser URLs for stacks and shell views Sync in-memory navigation to the address bar via a History API hook so deep links, refresh, Back/Forward, and bookmarks work across nodes, views, stack editor tabs, and mobile surfaces. Gate role/tier URL normalization on permissions and license readiness, preserve URLs on metadata fetch failure, and surface retryable stack-list errors without rewriting pending stack paths. * fix: preserve deep-link views on cold load and refresh Stop the node-switch effect from resetting to dashboard on initial mount. Defer URL writer settlement until hydrated activeView matches the route. Adds E2E coverage for shell cold loads, stack refresh, and compose env tab. * fix: keep mobile dashboard on list surface so sidebar renders On mobile, the URL sync hook was routing /nodes/<slug>/dashboard to the content surface, hiding the stack list sidebar. This prevented the data-stacks-loaded sentinel from appearing, causing sidebar truncation E2E tests to time out after reload on a mobile viewport. Mobile dashboard now stays on the list surface; other non-editor views still render on the content surface. * fix: complete mobile URL routing follow-ups for stack deep links Restore mobile /dashboard vs /stacks, list surface always writes /stacks. Hydrate pendingDetailStack, freeze compose failures with routeDetailError, and add unit plus E2E coverage. * fix: hydrate shell views from URL and sync in-app navigation Bootstrap activeView and tab state from the pathname on cold load. Settle route phase when state already matches, normalize unknown segments, and open Monaco editor tabs from stack deep links via applyEditorRouteState. * fix: prevent mobile stack deep links from hanging on cold load The resolvePendingStack effect did not re-fire when the pending stack ref was populated during URL hydration, because the urlHydratingStack state set in the same callback was not listed in the effect's dependency array. Adding it causes the effect to retry once hydration has committed. A resolvingRef mutex prevents concurrent invocations. When the target file is already loaded, route state is applied directly without calling loadFileForRoute, which avoids unmounting the editor (and hiding the recovery chip) if a background refresh triggers route resolution during a deploy operation. * test: adapt stack, deploy, and sidebar e2e specs to routable stack URLs * ci: raise E2E Playwright job timeout to 20 minutes |
||
|
|
0c37d18586 |
fix: normalize dashboard network sparkline to bytes per second (#1589)
Historical net_rx_mb/net_tx_mb values are MB/s rates, not cumulative bytes. Replace delta bucketing with per-bucket aggregate averaging converted to bytes/s so the NETWORK sparkline matches the live headline units. |
||
|
|
e12602091a |
fix: repin semver compose tags during fleet self-update (#1587)
* fix: repin semver compose tags during fleet self-update Fleet updates failed when docker-compose.yml pinned a semver tag because recreate reused the on-disk pin. Pull the target image first, rewrite semver pins via the update helper, and block digest or unresolved pins with fast 409s. * fix: update OFFLINE_META shape in capability and node-registry meta tests |
||
|
|
dbe230eef3 |
fix(editor): remove misleading image line above stack actions (#1584)
Closes #1580. The Command Center header showed the first container's image directly above stack-wide Start/Stop/Update controls, which implied those buttons targeted one image. Remove the header image/digest row; per-container ImageSourceMenu on each row remains. |
||
|
|
383d2248a2 | fix: keep confirm modal actions visible for long titles (#1578) | ||
|
|
30f1ca0a5f |
fix: default to compact density only for multi-container stacks (#1576)
* fix: only default to compact density for multi-container stacks * fix: update density reset test for single-container detailed default |
||
|
|
bbcc4b59e4 |
fix: UI polish - tooltips, masthead, tabs, settings, and layout consistency (#1563)
* fix: increase masthead rail shimmer visibility Bumped shimmer highlight opacity from 25% to 50% and reduced animation duration from 11s to 5s, matching the login page AuthCanvas shimmer precedent (via-white/60 at 4.5s). * fix: reduce masthead rail opacity to match login page pattern Changed all MastheadRail background classes from 100% to 70% opacity (bg-brand → bg-brand/70, etc.) so the shimmer highlight reads as a surface reflection rather than a detached floating bar. Matches the AuthCanvas login card precedent. * fix: improve masthead rail glow visibility Replaced the invisible inset box-shadow glow (10px blur inside a 3px element) with a white overlay that pulses opacity 0.15-0.55 over 4s. Sped up glow animation from 5.5s to 4s for a more noticeable breath. * fix: replace cursor-follow tooltip with standard Radix tooltip in sidebar rows Replaced the Cursor/CursorFollow animate-ui primitives in StackRow with the standard Radix Tooltip/TooltipTrigger/TooltipContent already used throughout the app. The custom cursor dot that followed the mouse is gone; tooltips now appear as static popovers on hover/touch. * fix: wrap tooltip icon triggers in span for Radix compatibility TooltipTrigger asChild requires a native element to forward props. Lucide icon components did not reliably receive data-state and event handlers, so tooltips on the check-failed and git-pending indicators did not open. Wrapped each icon in a span. * fix: rename Notification Routing to Routing in settings sidebar * fix: replace Timeline/All tasks buttons with SegmentedControl The two loose Button elements had indistinguishable selected/unselected states (secondary vs ghost). Replaced with the SegmentedControl component already used elsewhere on the same page, which has a clear active highlight indicator. * fix: standardize Blueprint empty state headings to font-heading Replaced manual font-serif [font-style:var(--heading-style)] with the font-heading class on the main heading and three step titles so the Deployments empty state follows the same heading convention as the rest of the app and respects the Calm/Signature theme toggle. * fix: remove custom tooltip styling, use standard TooltipContent defaults The RowTooltip wrapped its label in a font-mono text-stat-value span instead of using the TooltipContent base styling (text-xs text-popover-foreground) like every other tooltip in the app. * Revert "fix: remove custom tooltip styling, use standard TooltipContent defaults" This reverts commit |
||
|
|
0f9925e04f |
feat: block self-stack lifecycle ops with UI and preflight guardrails (#1569)
* feat: block self-stack lifecycle ops with UI and preflight guardrails Refuse update, deploy, down, stop, and delete when the stack matches Sencho's compose project. Return 409 self_stack_protected. Expose isSelf on /statuses and disable guarded UI actions. Add SelfStackProtectedDialog and self-managed-stack preflight warning. Closes #1564 * fix: add missing stackSelfFlags mock to useSidebarContextMenu test The production hook now reads stackListState.stackSelfFlags[file], but the test mock did not include it, causing 6 tests to fail with TypeError: Cannot read properties of undefined (reading 'web.yml'). * fix: harden self-stack protection during startup Add a global environment preflight warning when Sencho is managed inside COMPOSE_DIR. Align status decoration and route guards on Docker label fallback detection. Block rollback and service-level stop on the protected self stack. * fix: add self_stack_location to diagnostics-route expected check IDs |
||
|
|
f30a65ee08 |
fix(anatomy): name each image with a pending update in the update banner (#1575)
Show each updated image and tag transition in the Anatomy banner. Uses the per-image data the update-preview API already returns. |
||
|
|
b3bc223c45 |
fix(deploy): selective compose recreate on Save and Deploy (#1565) (#1568)
* feat: move core Blueprint orchestration to Community tier Blueprints CRUD, reconciliation, and drift modes are now available on Community. Pin remains Admiral-only via Federation placement controls. * test: update NodeCard cordon tests for Admiral-only tier gate Cordon now requires both isPaid and node:manage permission, matching the backend requirePaid + requirePermission guard. Three tests still used isPaid:false but expected the menu to be visible. |
||
|
|
adcc27e2cf | fix: update NodeCard cordon tests for paid-tier gate parity (#1571) | ||
|
|
a7e856f447 |
feat: enforce singleton local node per instance (#1567)
Only one local node is allowed. Creating a second local returns 409, and the last local node cannot be deleted or converted to a remote type. Existing duplicate local nodes from older versions are preserved and can be cleaned up individually. Zero-local recovery auto-assigns the default flag. Frontend delete surfaces and the Add Node form respect the new invariant. Enforced in DatabaseService (addNode/updateNode/deleteNode guards) and routes (error translations). Legacy test fixtures use raw SQL helpers. |
||
|
|
33231089c3 |
fix: truncate long sidebar stack names before trailing indicators clip (#1562)
Clamp the stack list width with ScrollArea block and min-w-0 on the row flex chain so long names ellipsize instead of pushing update dots past the sidebar edge. Add E2E layout coverage for trailing-indicator edge cases. |
||
|
|
f2b5c68d84 |
feat: add build-aware compose stack updates (#1561)
Detect services with build: in the update preview and run compose build --pull plus pull --ignore-buildable when Update is triggered on those stacks, while keeping the existing pull-only path for image-only stacks. |
||
|
|
4077546492 |
feat: acknowledge Compose Doctor preflight findings per stack (#1560)
* feat: acknowledge Compose Doctor preflight findings per stack Add node-scoped preflight acknowledgements with read-time filtering. Supports four expiry modes and activeStatus for banner, tab dot, and readiness. * fix: align preflight acknowledge UI with design system Use Combobox, modal chrome, mono fields, and non-destructive clear confirm. * fix: update test mocks to match new preflight field names The preflight-acknowledgements feature renamed status-\>activeStatus and highestSeverity-\>activeHighestSeverity in the preflight report shape. The corresponding test mocks in three files still used the old field names, causing 6 test failures across backend and frontend. - backend: update-guard-service mock now passes activeStatus - frontend PreflightPanel: Report interface and report() helper now include activeStatus, activeHighestSeverity, activeCount, acknowledgedCount - frontend StackAnatomyPanel doctor: mock API response now includes activeHighestSeverity and activeStatus |
||
|
|
fbe98676cb |
feat: bound container health pane so logs stay visible on large stacks (#1556)
* feat: bound container health pane so logs stay visible on large stacks On the stack overview page, multi-container stacks (8+ services) pushed the logs pane off-screen because the Command Center card had shrink-0 and no scroll mechanism. This reworks the desktop layout for stacks with more than one container: - The Command Center card is bounded at max-h-[42%] so it never consumes the full left column. The container list scrolls internally via Sencho's existing Radix ScrollArea. - A summary strip shows total / running / paused / unhealthy counts above the list, with a Compact / Detailed density toggle. Detailed (default) shows CPU / Mem / Net sparklines; Compact hides them for a denser list. - The logs pane has a guaranteed min-h-[180px] so it is always reachable. - Single-container stacks are untouched: the card keeps its original shrink-0 behaviour, and no summary strip or density toggle appears. Mobile is unaffected (it already uses segmented Health / Logs / Compose panes). * fix: key ContainersHealth by node+stack so density resets on navigation The density toggle state (compact/detailed) is local to ContainersHealth. Without a key, selecting Compact on one multi-container stack would persist when navigating to a single-container stack, hiding sparklines while also hiding the toggle itself. Keying by node+stack ensures a clean remount. Also added test coverage for zero containers and density reset on remount. * fix: remove unused rerender variable from ContainersHealth test |
||
|
|
bb35c1bc92 |
feat: add sidebar update indicator toggle and Stack Health badge (#1570)
* feat: add sidebar update indicator toggle and Stack Health badge - Add image_update_sidebar_indicators setting (default off, node-scoped) - Gate the Updates filter chip and sidebar status indicators on the setting - Add "Update available" badge to Stack Health table (always visible) - Extend ImageUpdateStatus with sidebarIndicators boolean - Poll /api/image-updates/status alongside /detail in useImageUpdates - React to SENCHO_SETTINGS_CHANGED for instant toggle propagation - Reset sidebar state on node switch; generation-guard stale responses - Disable toggle when status is null (loading) or field is absent (old node) - Wire stackUpdates through ViewRouter → HomeDashboard → StackHealthTable - Update settings registry, operator docs, and sidebar/dashboard docs * fix: guard against stale node renders, memo drift, and cross-node error toasts - Track owning node ID in useImageUpdates state so React never renders node B with node A's data before the passive effect resets (P2) - Replace incorrect stackUpdates dependency with sidebarStackUpdates in chipFilteredFiles useMemo (P3) - Guard the error toast in handleSidebarIndicatorsChange so a stale PATCH failure from node A does not surface while viewing node B (P3) * fix: default sidebar update indicators to on (opt-out) The sidebar indicators are a safe convenience that most users want. Switching the default from off to on matches the opt-out convention used by prune_on_update, reclaim_hero, and health_gate_enabled. |
||
|
|
fdbc1b1ebb |
feat: move Blueprint orchestration and Federation placement to Community tier (#1555)
* feat: move core Blueprint orchestration to Community tier Blueprints CRUD, reconciliation, and drift modes are now available on Community. Pin remains Admiral-only via Federation placement controls. * feat: move Federation placement controls to Community tier Remove requirePaid from cordon, uncordon, and blueprint pin routes. Ungate the Federation tab and gate cordon UI on node:manage only. Update licensing and fleet docs for the new tier split. |
||
|
|
1fac6f797f |
refactor: replace PNG logos with SVG (#1559)
Swap sencho-logo PNGs for SVGs in frontend/public. Update all references in index.html, SidebarBrand.tsx, and README.md. |
||
|
|
4a350e7a0a | feat: add Docker label audit across Fleet and Stack views (#1531) | ||
|
|
10fb93dcb1 |
feat(scheduler): schedule container restart, stop, and start (#1526)
* feat(scheduler): schedule container restart, stop, and start Add container as a scheduled-task target type so operators can automate lifecycle actions against standalone containers by node and name, with matching UI pickers, validation, execution on local and remote nodes, and tests. * fix(scheduler): stack service matching and container picker hygiene Backfill Service on smartFallback containers so per-service stack restarts work when container_name is set. Match services by compose label and container name in stack routes and scheduled restarts. Exclude Sencho from GET /api/containers lists. Hide the Restart Stack service picker when a stack has only one service. * test(scheduler): scope service checkbox assertion to Services block The create dialog also has a Delete after run checkbox. Count checkboxes only inside the Services section so CI does not include unrelated form controls. * fix(scheduler): narrow closest() result to HTMLElement in schedule test The service-checkbox assertion passed an Element from closest() into within(), which requires an HTMLElement, failing tsc -b in the frontend build and Docker build stages. Use the closest<HTMLElement>() type argument so the value type-checks without an unsafe cast. * fix(scheduler): hide Sencho container on remote node picker lists Remote container lists are proxied from peer Sencho instances, so id-only self filtering missed peers on older builds. Await SelfIdentity init, match ImageID, and drop official saelix/sencho images. Apply the same heuristic in the scheduled-operations UI and when the hub fetches remote containers for scheduled runs. * test(monitor): add missing DatabaseService mocks for scan history cleanup * test(scheduler): add missing markStaleScansAsFailed mock SchedulerService.tick() calls db.markStaleScansAsFailed() to sweep stale vulnerability scans. The scheduler-service test was missing this method in its DatabaseService mock, causing TypeError failures during test initialization. Added mockMarkStaleScansAsFailed to hoisted mocks and DatabaseService mock object, returning safe default of 0 scans marked as failed. * test(compose): add missing FileSystemService mocks for getStackContent/getEnvContent * test(containers-route): mock SelfIdentityService to prevent initialize() crash The excludeSelfContainers() helper calls SelfIdentityService.initialize(), which tries to access DockerController. Without a proper SelfIdentityService mock, the initialize() call fails silently, causing a 500 error on GET /api/containers. Added SelfIdentityService mock with initialize(), isOwnContainer(), and isOwnImage() methods to prevent the crash. |
||
|
|
b65daf6845 |
feat: add notification suppression rules (#1525)
* feat: add notification suppression rules * fix: restore label routing and routing test mocks for suppression * fix: allow bell mute shortcuts for history-only notification categories Suppression rule validation used the routable category whitelist, which rejected history-only categories such as update_started that appear in the bell during stack updates. * feat: expand Mute Rules UX with compose-first entry points and activity badges * fix: add missing NodeContext mocks for notification suppression tests |
||
|
|
c3dcf31cc8 |
fix: wrap auto-heal policy history in stack monitor sheet (#1538)
* fix: wrap auto-heal policy history in stack monitor sheet Restructure PolicyRow history entries to stack vertically with break-words so long container names and reasons stay inside the sheet width. Fixes #1532 * fix: cap auto-heal policy history to a scrollable region Recent activity could grow the policy row unbounded, pushing the add-new-policy form far down the sheet when a policy accumulated many history entries. Wrap the list in a fixed-height ScrollArea so it scrolls internally instead. |
||
|
|
00a323252d |
chore(deps): bump the all-npm-frontend group (#1543)
Bumps the all-npm-frontend group in /frontend with 25 updates: | Package | From | To | | --- | --- | --- | | [@radix-ui/react-alert-dialog](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/alert-dialog) | `1.1.17` | `1.1.18` | | [@radix-ui/react-checkbox](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/checkbox) | `1.3.5` | `1.3.6` | | [@radix-ui/react-context-menu](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/context-menu) | `2.3.1` | `2.3.2` | | [@radix-ui/react-dialog](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/dialog) | `1.1.17` | `1.1.18` | | [@radix-ui/react-dropdown-menu](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/dropdown-menu) | `2.1.18` | `2.1.19` | | [@radix-ui/react-hover-card](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/hover-card) | `1.1.17` | `1.1.18` | | [@radix-ui/react-label](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/label) | `2.1.10` | `2.1.11` | | [@radix-ui/react-popover](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/popover) | `1.1.17` | `1.1.18` | | [@radix-ui/react-scroll-area](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/scroll-area) | `1.2.12` | `1.2.13` | | [@radix-ui/react-select](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/select) | `2.3.1` | `2.3.2` | | [@radix-ui/react-separator](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/separator) | `1.1.10` | `1.1.11` | | [@radix-ui/react-slider](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/slider) | `1.4.1` | `1.4.2` | | [@radix-ui/react-tabs](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/tabs) | `1.1.15` | `1.1.16` | | [@radix-ui/react-tooltip](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/tooltip) | `1.2.10` | `1.2.11` | | [cronstrue](https://github.com/bradymholt/cRonstrue) | `3.21.0` | `3.24.0` | | [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react) | `1.21.0` | `1.23.0` | | [motion](https://github.com/motiondivision/motion) | `12.41.0` | `12.42.2` | | [radix-ui](https://github.com/radix-ui/primitives/tree/HEAD/packages/react/radix-ui) | `1.6.0` | `1.6.1` | | [recharts](https://github.com/recharts/recharts) | `3.9.0` | `3.9.1` | | [@tailwindcss/vite](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/@tailwindcss-vite) | `4.3.1` | `4.3.2` | | [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `26.0.0` | `26.1.0` | | [eslint](https://github.com/eslint/eslint) | `10.5.0` | `10.6.0` | | [tailwindcss](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/tailwindcss) | `4.3.1` | `4.3.2` | | [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.62.0` | `8.62.1` | | [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) | `8.1.0` | `8.1.2` | Updates `@radix-ui/react-alert-dialog` from 1.1.17 to 1.1.18 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/alert-dialog/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/alert-dialog) Updates `@radix-ui/react-checkbox` from 1.3.5 to 1.3.6 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/checkbox/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/checkbox) Updates `@radix-ui/react-context-menu` from 2.3.1 to 2.3.2 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/context-menu/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/context-menu) Updates `@radix-ui/react-dialog` from 1.1.17 to 1.1.18 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/dialog/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/dialog) Updates `@radix-ui/react-dropdown-menu` from 2.1.18 to 2.1.19 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/dropdown-menu/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/dropdown-menu) Updates `@radix-ui/react-hover-card` from 1.1.17 to 1.1.18 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/hover-card/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/hover-card) Updates `@radix-ui/react-label` from 2.1.10 to 2.1.11 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/label/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/label) Updates `@radix-ui/react-popover` from 1.1.17 to 1.1.18 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/popover/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/popover) Updates `@radix-ui/react-scroll-area` from 1.2.12 to 1.2.13 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/scroll-area/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/scroll-area) Updates `@radix-ui/react-select` from 2.3.1 to 2.3.2 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/select/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/select) Updates `@radix-ui/react-separator` from 1.1.10 to 1.1.11 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/separator/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/separator) Updates `@radix-ui/react-slider` from 1.4.1 to 1.4.2 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/slider/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/slider) Updates `@radix-ui/react-tabs` from 1.1.15 to 1.1.16 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/tabs/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/tabs) Updates `@radix-ui/react-tooltip` from 1.2.10 to 1.2.11 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/tooltip/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/tooltip) Updates `cronstrue` from 3.21.0 to 3.24.0 - [Release notes](https://github.com/bradymholt/cRonstrue/releases) - [Changelog](https://github.com/bradymholt/cRonstrue/blob/main/CHANGELOG.md) - [Commits](https://github.com/bradymholt/cRonstrue/compare/v3.21.0...v3.24.0) Updates `lucide-react` from 1.21.0 to 1.23.0 - [Release notes](https://github.com/lucide-icons/lucide/releases) - [Commits](https://github.com/lucide-icons/lucide/commits/1.23.0/packages/lucide-react) Updates `motion` from 12.41.0 to 12.42.2 - [Changelog](https://github.com/motiondivision/motion/blob/main/CHANGELOG.md) - [Commits](https://github.com/motiondivision/motion/compare/v12.41.0...v12.42.2) Updates `radix-ui` from 1.6.0 to 1.6.1 - [Changelog](https://github.com/radix-ui/primitives/blob/main/packages/react/radix-ui/CHANGELOG.md) - [Commits](https://github.com/radix-ui/primitives/commits/HEAD/packages/react/radix-ui) Updates `recharts` from 3.9.0 to 3.9.1 - [Release notes](https://github.com/recharts/recharts/releases) - [Changelog](https://github.com/recharts/recharts/blob/main/CHANGELOG.md) - [Commits](https://github.com/recharts/recharts/compare/v3.9.0...v3.9.1) Updates `@tailwindcss/vite` from 4.3.1 to 4.3.2 - [Release notes](https://github.com/tailwindlabs/tailwindcss/releases) - [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.3.2/packages/@tailwindcss-vite) Updates `@types/node` from 26.0.0 to 26.1.0 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `eslint` from 10.5.0 to 10.6.0 - [Release notes](https://github.com/eslint/eslint/releases) - [Commits](https://github.com/eslint/eslint/compare/v10.5.0...v10.6.0) Updates `tailwindcss` from 4.3.1 to 4.3.2 - [Release notes](https://github.com/tailwindlabs/tailwindcss/releases) - [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/tailwindlabs/tailwindcss/commits/v4.3.2/packages/tailwindcss) Updates `typescript-eslint` from 8.62.0 to 8.62.1 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.62.1/packages/typescript-eslint) Updates `vite` from 8.1.0 to 8.1.2 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v8.1.2/packages/vite) --- updated-dependencies: - dependency-name: "@radix-ui/react-alert-dialog" dependency-version: 1.1.18 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: "@radix-ui/react-checkbox" dependency-version: 1.3.6 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: "@radix-ui/react-context-menu" dependency-version: 2.3.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: "@radix-ui/react-dialog" dependency-version: 1.1.18 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: "@radix-ui/react-dropdown-menu" dependency-version: 2.1.19 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: "@radix-ui/react-hover-card" dependency-version: 1.1.18 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: "@radix-ui/react-label" dependency-version: 2.1.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: "@radix-ui/react-popover" dependency-version: 1.1.18 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: "@radix-ui/react-scroll-area" dependency-version: 1.2.13 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: "@radix-ui/react-select" dependency-version: 2.3.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: "@radix-ui/react-separator" dependency-version: 1.1.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: "@radix-ui/react-slider" dependency-version: 1.4.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: "@radix-ui/react-tabs" dependency-version: 1.1.16 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: "@radix-ui/react-tooltip" dependency-version: 1.2.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: cronstrue dependency-version: 3.24.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-npm-frontend - dependency-name: lucide-react dependency-version: 1.23.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-npm-frontend - dependency-name: motion dependency-version: 12.42.2 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-npm-frontend - dependency-name: radix-ui dependency-version: 1.6.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: recharts dependency-version: 3.9.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: "@tailwindcss/vite" dependency-version: 4.3.2 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: "@types/node" dependency-version: 26.1.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: all-npm-frontend - dependency-name: eslint dependency-version: 10.6.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: all-npm-frontend - dependency-name: tailwindcss dependency-version: 4.3.2 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: typescript-eslint dependency-version: 8.62.1 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: all-npm-frontend - dependency-name: vite dependency-version: 8.1.2 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: all-npm-frontend ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
d80538f254 |
fix(deps): patch frontend dompurify and @babel/core advisories (#1539)
Bump the frontend dependency overrides to force patched versions of two packages flagged by security advisories: - dompurify: monaco-editor pins 3.2.7, which the existing override raised only to 3.4.1. Raise the override to ^3.4.11 so the resolved version is 3.4.11, clearing eight dompurify advisories (IN_PLACE and template sanitization bypasses, hook/config pollution, Trusted Types poisoning). - @babel/core: force the transitive build-time dependency from 7.29.0 to ^7.29.6 (resolves to 7.29.7), clearing the sourceMappingURL arbitrary file read advisory. Its consumers accept ^7.0.0 / ^7.24.4. dompurify is not imported in application code; it reaches the bundle only through monaco-editor, so the override is the correct control point. Lock churn is limited to the @babel/* subtree, the browserslist toolchain data packages, and dompurify. Typecheck, production build, and the full frontend test suite (1613 tests) pass. |
||
|
|
60de0ddec2 |
fix(deploy-modal): hide frozen countdown label in inline feedback style (#1534)
In inline deploy feedback style, the auto-close countdown label
("closes in Xs") was visible but permanently frozen. canAutoClose
was already false in inline style (the banner owns the lifecycle),
but showCountdown was driven by !gateHoldsOpen, showing the label
even when no countdown timer could ever start.
Change showCountdown to canAutoClose so the label only appears
when the modal is actually eligible to auto-close (modal style,
succeeded, no observing gate). No gate contracts or modal-style
behavior change.
Adds regression tests: inline succeeded hides the label, modal
succeeded shows it.
|
||
|
|
90c69b1ea4 |
fix: reflect toggle state in scheduled task Enabled/Disabled label (#1533)
The New/Edit Scheduled Task modal always showed "Enabled" next to the task toggle regardless of its on/off state. The label now tracks formEnabled, matching the existing Enabled/Disabled label convention used in the Stacks settings section. |
||
|
|
e1176fbb26 |
fix: make auth canvas scroll actually constrain on short viewports (#1523)
PR #1521 added max-height and overflow-y-auto but the card stayed content- sized because flex items default to min-height:auto and justify-center clipped overflow while html/body stay overflow:hidden. Add min-h-0 and my-auto, use dvh, scroll the auth shell, and pin Enter Sencho outside the preflight scroll region. |
||
|
|
322d337d20 |
fix: polish sidebar and masthead chrome, scroll setup preflight (#1522)
Remove redundant nav and label dots, move desktop masthead status to animated left rails, and let AuthCanvas scroll long preflight content on small viewports. |
||
|
|
da73ee0717 |
fix: scroll auth canvas when preflight content overflows (#1521)
Cap the auth card height and scroll its body so setup preflight checks remain reachable on short viewports without clipping the Enter action. |
||
|
|
1b9a40f874 |
fix: align notification unread badge with panel visibility rules (#1520)
Stack success events were hidden from the panel but still counted unread on the bell and dashboard. Share one visibility helper across badge, panel, and Recent Alerts. Harden mark-all-read against partial API failures. Fixes #1513 |
||
|
|
89b9341568 |
fix: replace Janitor wording with reclaim/prune actions in user-facing copy (#1518)
Rename "Janitor threshold" to "Reclaimable Docker data threshold" in Settings and update the unused-Docker-data alert to point users to the Resources view and the Prune Node Resources scheduled action, which are the real UI surfaces for reclaiming disk space. Remove "janitor" from settings search keywords. The internal docker_janitor_gb database key and JANITOR_* constants are unchanged. |
||
|
|
41dc339c26 |
fix: enforce 1:1 compose path mapping for Pilot agent mounts (#1516)
* fix: enforce 1:1 compose path mapping for Pilot agent mounts Pilot enrollment now generates validated 1:1 bind mounts so every agent path maps to a unique compose directory. Persisted agent paths reconcile during startup to catch drift. Unsafe relative-bind redeploys are blocked before container removal to prevent path escapes. - Add composePathMapping utility with strict path validation - Generate COMPOSE_DIR and validated mounts during Pilot enrollment - Reconcile persisted agent paths during startup bootstrap - Block redeploy when a relative-bind mount would escape the compose root - Default Pilot UI path to /opt/docker/sencho - Update multi-node and pilot-agent documentation - Add regression tests for enrollment, bootstrap, compose-service, and environment-check paths * fix: update E2E enrollment regexes for YAML-quoted token values |
||
|
|
8bec32dea6 |
fix: stack the volume browser panels on phones so the file preview is readable (#1512)
On phone widths the volume browser kept its fixed 260px tree column beside the preview, leaving the preview roughly 54px wide so file content wrapped to one or two characters per line. Below the md breakpoint the two panels now stack into a single column (tree above, preview below) with the tree capped at 40% of the height, so the preview spans the full width. Desktop and tablet layouts are unchanged. |
||
|
|
e9c2c0cf8e |
fix: classify degraded remote stacks as partial on the compatibility path (#1511)
When a remote node's bulk /stacks/statuses is unavailable or returns the legacy plain-string format, the sidebar derived a stack's status from its containers by treating "any container running" as healthy. A stack with one running and one crashed container was shown green UP and excluded from the Down filter. Re-derive status from the per-stack container list in both compatibility cases, mirroring the backend classifier: a stack with a running container and a genuinely crashed one (dead, or exited/restarting with a non-zero code) is now partial, while a cleanly finished one-shot container (exit 0) stays running. Legacy plain-string bulk responses are routed through this path too, since they have already collapsed the degraded case. |
||
|
|
997a6bb79a |
fix: gate cross-node HTTP and stop-by-label on remote RBAC capability (#1509)
An older remote node ignores the forwarded actor-role header (running proxied requests as admin) and ignores the stop-by-label stack allowlist (stopping every label-matched stack). The control could neither detect nor prevent this on a mixed-version fleet. Instances now advertise a cross-node-rbac capability, and the control refuses to act when a remote lacks it: - HTTP proxy: a non-admin user's request is not forwarded to a remote that does not advertise the capability (fails closed when it cannot be determined). Admins are unaffected. - Stop-by-label: a real stop bound to a confirmed stack set is not sent to a remote lacking the capability; the node is reported as needing an upgrade. As defense in depth, a node whose results name stacks outside the confirmed set is failed rather than rendered as a clean stop. Separately, the stop's lock-contention path now reports every confirmed stack as a contention failure (including one that lost its label), so a confirmed stack is never silently dropped and the result is never empty. |
||
|
|
dd76b13d55 |
fix: require node:read for fleet topology reads and hide Fleet without it (#1507)
The fleet overview, configuration, dependency-map, and networking-summary reads were authentication-only, so a role without node:read (deployer) could read node names, host stats, and cross-node topology. They now require node:read, matching the role model where every role except deployer holds it. For parity, the Fleet nav entry is gated on node:read (hiding it from the top nav, mobile menu, and command palette), the Fleet view redirects to the dashboard when reached without it, and the dashboard fleet heartbeat falls back to the single-node restart map for a role that cannot read fleet data. |
||
|
|
1dc12f7da8 |
fix: bind fleet stop-by-label to the exact confirmed nodes and stacks (#1506)
A fleet stop re-matched stacks by label name at execution, so a stack that gained the label between the operator's preview and confirmation could be stopped even though it never appeared in the confirmation. A confirmed node that was deleted after the preview also vanished from the results, letting the remaining successes read as a clean stop. The confirm flow now sends the exact node and stack list resolved in the preview. Each node's stop is bound to that set: only stacks that are still label-matched and confirmed are stopped, and a confirmed node missing from the registry is reported as an explicit failure rather than dropped. |
||
|
|
d6ce60d280 |
fix: make Reduced motion gate overlays, standardize the tab band, and polish fleet/snapshots (#1504)
* fix: address review feedback on motion, fleet band, and snapshots - Reduced motion now also skips the animate-ui overlay open/close animations (dialog, sheet, popover, dropdown-menu) via a shared useReducedTransition that zeroes the transition when reduced motion is active; MotionConfig alone only neutralized transform/layout, leaving the opacity/blur fade. - Fleet tab band: flatten the list's own pill band so the tabs sit in a single full-width band instead of a nested second band. - Fleet Overview: label the node-update button "Node Update"; the add-node button becomes a ChartNetwork icon that opens Settings > Nodes (outline style, matching the node-update button) instead of the add-node modal. - Blueprint deployments empty state: drive the serif headings from the theme heading-style token so Calm drops the italic. - Snapshots: move the per-stack Restore onto the stack header row (right side), matching the ghost action style. * feat: standardize the tab band on Security and add a tab hover state - Apply the Fleet full-width tab band to the Security page (single band, the list's pill background flattened so the tabs sit directly in it). - Add a hover highlight to tabs so the band reads as interactive, not flat; the active tab is unaffected. * fix: drive reduced-motion transitions from the app setting, not the OS query useReducedTransition called framer-motion's useReducedMotion(), which only reflects the OS prefers-reduced-motion media query and ignores our MotionConfig / appearance toggle, so dialogs kept animating with the setting on. Read the app's useReducedMotion selector instead. Verified: with the setting on, the dialog opacity snaps 0->1 in one frame; with it off, it ramps over the spring. * feat: standardize stack-detail body text to text-xs and add a log expand toggle - Bump the anatomy panel body text (rows, field values, warnings) and the structured log lines from 10/11/12px to text-xs (12px), leaving the uppercase kicker labels and the log level/source badges as-is. - Add an expand control next to the log download button that collapses the Command Center so the logs pane fills the left column; toggles back to restore. |
||
|
|
04e69021e0 |
feat: make all security features available on every tier (#1502)
Scan policies, deploy enforcement, the suppression-aware deploy-block toggle, SARIF export, and OpenVEX export now work on Community, matching the rest of the vulnerability-scanning surface that was already free. Backend: drop the tier gate from the seven security routes and from the dashboard configuration-status scan-policies row, so the Dashboard and Fleet config cards stop hiding the Vulnerability scanning row. Reading policies stays auth-only; mutations and exports stay admin-only. Frontend: always show the Policies tab and panel, the SARIF and VEX export actions, and the honor-suppressions toggle for admins. Docs: move scan policies, SARIF, and OpenVEX to every tier across the feature and API-reference pages; clarify that Fleet Sync's cross-node replication remains the paid part. |
||
|
|
05c483f213 |
fix: harden cross-node fleet label actions and guard container reads (#1503)
* fix: harden cross-node fleet label actions and guard container reads
Release-stabilization fixes for the Fleet Actions surface:
- Stop-by-label binds execution to the nodes shown in the confirmed
preview. The real stop sends the confirmed node ids and the backend
restricts the fan-out to them, so a node that was unreachable during
preview and reconnects before the stop can no longer enter execution
and have unlisted stacks stopped.
- Bulk label assign validates each remote node's result against the
stacks it was asked to label: a body whose results are empty, partial,
duplicated, or shaped wrong is a per-node failure instead of reading as
a successful zero-stack assign. The card mirrors this, rejecting a
missing or non-array results body and only reporting success when at
least one stack was assigned.
- Bulk label assign re-reads authoritative per-node stacks and labels on
demand via a Refresh control, and the confirmation lists the affected
node and stack names rather than bare counts.
- The stack-specific and fleet container/stack read routes require the
stack:read permission, matching the generic container and stack routes.
Every shipped role already carries stack:read, so reachability is
unchanged; the guard closes the routes that were auth-only.
Adds unit coverage for the assign-result validator, route coverage for
the stop allowlist and assign membership checks, and authorization
coverage for the newly guarded reads.
* test: assert the confirmed node allowlist in the fleet stop-card test
The stop-card component test pinned the real-stop request body to
{ labelName, dryRun } and broke once the stop began carrying the
confirmed-preview node ids. Update it to expect the nodeIds allowlist
derived from the resolved preview, so the test asserts the binding
rather than the pre-fix shape.
|
||
|
|
b5810a9b55 |
feat: add Reduced motion setting and polish chrome, files, and stack-detail (#1501)
A batch of UI/UX polish: - New independent "Reduced motion" appearance setting (separate from Reduced effects). Drives framer-motion via MotionConfig and clamps CSS transitions via data-motion on <html>; toasts are unaffected. Defaults off (OS preference still honored). - Stack-detail Files tab: rename "Files & Volumes" to "Files", add a persisted word-wrap toggle to the file viewer (default on), and add a fullscreen toggle that collapses the Command Center + Logs column so the editor fills the width. - Create Stack > From Git: remove the nested scroll clamp so the deploy toggle and footer are reachable. - Fleet: full-width tab band with icon-only Refresh / Export Dossier, icon-only Check-for-updates / Add-node on the Overview toolbar, theme-aware empty-state headings (calm drops the italic), and fix the Actions card body overlapping the action-row divider. - Snapshots: restyle Restore and Restore all to the ghost button design used by View / Preview / Download, and right-align the per-stack Restore. - Settings sidebar: App Store gradient active style and standard font size. - Compose Doctor: dismiss the high-risk banner (and clear the tab dot) until the findings change, via a shared fingerprint-keyed hook. - Stack-detail Storage: link the "no recent fleet snapshot" warning to the Fleet Snapshots tab (FleetView tabs are now controlled to support the deep link). |
||
|
|
083442d5ea |
fix: differentiate security action links and add suppression editing (#1500)
Security page UX fixes: - Stop the CVSS x EPSS scatter chart from painting a full-plot "white rectangle" cursor on click (cursor disabled), and prevent click-drag selection on charts. - Differentiate the overview action links: "fixable" links (masthead primary action, review-queue blocker, and the Fixable signal tile) now open the Images tab pre-filtered to fixable findings; the Stale and Failed signal tiles link to the History tab where those scans are listed; Secrets and Misconfigs tiles link to their tabs. The Images tab accepts an initialFilter and exposes a Fixable option in the severity dropdown. - Fix the "Secrets / misconfigs" option wrapping and misaligning in the severity dropdown (single-line option labels, wider trigger). - Add Edit for CVE suppressions and misconfig acknowledgements (reason, scope pattern, expiry), reusing the existing dialog and the existing PUT endpoints; the CVE/rule identity stays fixed. |
||
|
|
ba57c67048 |
fix: persist a one-time schedule's run time so edit and disable keep its year (#1499)
PR #1497 pinned an enabled one-shot's next_run_at on creation, but the chosen instant did not survive two paths: editing reconstructed the date from the yearless cron (current year), and a disabled one-shot nulled next_run_at with no other store, so enabling it later recomputed from the cron. Both moved a future-year one-shot to a different annual occurrence than the date displayed. Persist the one-shot's absolute fire time in a dedicated run_at column (additive, nullable; recurring schedules leave it null). Create and update store run_at independently of the enabled state; next_run_at is derived from it when enabled and null while disabled, so a disabled one-shot keeps its run_at and the enable toggle restores the exact instant from the column rather than the cron. The editor reconstructs a one-shot's date from the persisted run_at, so opening and re-saving without changes preserves the originally chosen year. No behavior change for recurring schedules or fresh installs; the column is added by an additive migration safe for upgrades from v0.92.0. |
||
|
|
60536aa614 |
feat: sortable resource tables and richer dashboard stack-health columns (#1498)
Make the Resources Images, Volumes, and Networks tables sortable with a shared useTableSort hook and SortableTableHead, move the Images scan-history control into the Images tab header, and keep the network List/Topology toggle anchored with Create Network visible in both modes. Rework the dashboard Stack health table: drop the redundant Host column, add sortable Stack/Up/CPU/Mem headers, and add Source (local/git) and Port columns. The status endpoint now labels each stack with its git/local source, computed outside the cache so linking changes show immediately. Extract a reusable CreateNetworkDialog and add a create-network action to the stack-detail Networking tab. |
||
|
|
cf0db36e78 |
fix: fire a one-time schedule on the exact chosen date and time (#1497)
Simple "Once" schedules compiled to a 5-field cron, which has no year field, so the scheduler computed the next run as the next annual occurrence. A date chosen for a later year ran a year early, and a time already elapsed today ran a year late, contradicting the UI promise that the task fires on the chosen date. One-time schedules now send the chosen absolute timestamp (run_at) and the backend pins next_run_at to it instead of the cron-derived next run, so the run fires on the exact selected instant including the year. The Simple-mode validation now compares the full chosen instant against the current time, so a time earlier today is rejected as past rather than silently deferred a year. run_at is validated as a finite, future epoch-millisecond timestamp on create and update. The enable/disable toggle preserves a one-shot's pinned next_run_at (its yearless cron cannot reconstruct the chosen year), so re-enabling restores the exact instant. Recurring shapes and Advanced mode are unchanged (cron stays authoritative). |