* 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
* 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.
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.
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.
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.
The GitHub Sponsors PR (#1535) was merged as feat, which is not
accurate: it only touches FUNDING.yml and README/SUPPORT links, with
no codebase changes. This forces the next release back to a patch
bump instead of the minor bump release-please computed from that
commit.
Release-As: 0.93.3
Adds the github key to FUNDING.yml and links GitHub Sponsors from README and SUPPORT so both channels are visible; the org still needs to complete GitHub's Sponsors onboarding for the button to render.
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.
Remove redundant nav and label dots, move desktop masthead status to animated left rails, and let AuthCanvas scroll long preflight content on small viewports.
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
GitHub Releases appear before docker-publish.yml finishes pushing images.
Probe Docker Hub and GHCR manifests before advertising a version as available.
Sanitize registry probe debug logs for CodeQL log-injection.
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.
* 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
* docs(introduction): refresh for the redesigned UI and replace screenshots
Bring the Getting Started Introduction page in line with the current
product:
- Add the Security top-level view to the navigation list and a
dedicated Security section with a new screenshot.
- Correct the Fleet tab names (Snapshots, Status, Map, Deployments,
Routing, Federation, Actions, Secrets).
- Split Settings out from security and list the current nine setting
groups (Security graduated to its own view).
- Refine the navigation paragraph so role, tier, and local-vs-remote
context read accurately.
Replace all four existing screenshots (Home, stack workspace, Fleet,
Resources) with fresh captures of the redesigned UI and add a Security
overview screenshot.
* docs(configuration): document advanced env vars and clarify deployment vs runtime config
Add an Advanced environment variables section (TRIVY_BIN, SENCHO_MESH_SUBNET,
GITSOURCE_MAX_CLONE_BYTES, SENCHO_PUBLIC_URL, SENCHO_COMPOSE_STALL_TIMEOUT_MS) and
reframe the intro to separate deployment-time configuration from the runtime
settings that live in the in-app Settings Hub. Cross-link the pilot-agent
variables to the Pilot Agent page instead of duplicating them.
* docs(sso): refresh SSO Setup Guide and SSO & LDAP reference for the redesigned UI
Refresh both SSO documentation pages against the current product and the
redesigned settings UI.
- Correct the navigation path to Settings -> Access -> SSO on both pages.
- Fix the "Require 2FA on SSO sign-in" toggle location to
Settings -> Personal -> Account.
- Describe the login-page experience (the Local / LDAP toggle and the
branded OIDC buttons under the "Or continue with" divider) and the SSO
panel masthead (SCOPE, PROVIDERS, ENABLED).
- Replace all six SSO screenshots with fresh captures of the redesigned UI.
* docs(features): refresh the Features Overview page for the redesigned UI
Rewrite docs/features/overview.mdx to mirror the current Features
navigation grouping (Stacks, Deployment, Resources, Observability,
Fleet, Automation, Security & Identity) and add the recently shipped
capabilities surfaced in the redesign: Stack Dossier, Drift Detection,
Compose Doctor, Compose Networking, Environment & secrets guardrails,
Storage portability, Health-Gated Updates, Fleet Dossier, and the
dedicated Security page.
Correct stale claims (the file explorer now gates writes on stack edit
permission, not an admin role; downloads are a read action; bulk label
assign now spans nodes) and standardize the tier callouts so partly
paid features read as "Admiral adds X". Replace the three pre-redesign
screenshots and add a Security overview banner, all captured from a
populated fleet.
* docs(features): refresh the Appearance page for the redesigned UI
Add fresh screenshots and a troubleshooting section to the Appearance
page, verified against the live product.
- Add four screenshots: the Theme card (live preview, mode, accent, and
fine-tune sliders), the top-bar quick switcher, the Typography card,
and the Display card.
- Refresh the Density screenshot used by the Settings reference page.
- State that the quick switcher also covers text size, and that the
contrast, border, and glow sliders stay in Settings.
- Add a Troubleshooting accordion covering per-browser persistence,
resets to defaults, cross-operator scope, and the quick-switcher
versus full-Settings split.
* docs(introduction): refresh screenshots and correct stale content
* docs(reference): refresh the Settings Reference page for the redesigned UI
Replace all seven stale screenshots with fresh 1920x1080 captures.
Add five new screenshots for the sections that previously had none.
Content changes:
- Sidebar table: rename Infrastructure "Fleet Mesh" entry to "Fleet";
add "Image update checks" to the Automation group list
- Fleet section: rename heading to match registry label; add the
Documentation snapshots subsection (snapshot_documentation toggle)
- Container Alerts: add screenshot
- Image update checks: add the full section (Registry checks table,
scheduling mode, interval presets, cron expression support)
- Stacks / Deploy Guardrails: add screenshot
- Recovery: add the full section (System health snapshot, Environment
preflight checks, Safe actions, Command-line recovery table)
* docs(sso): refresh screenshots for SSO quickstart and feature pages
* docs: refresh Features Overview screenshots and content
Replace all 4 hero screenshots with fresh 1920x1080 production captures.
Correct security posture state names (Action needed / Monitoring / Secure),
add the Policies tab to the Security section tab list, mention the Simple
mode in Scheduled operations, and update all alt text to match the new
screenshots.
* docs: refresh Appearance page screenshots and correct quick-switcher scope
Replace all four Appearance screenshots with fresh production captures.
Fix the quick-switcher control list: remove fonts (not present in the
popover), add visual style and readability which are. Add Log chip color
to the Display section. Update all screenshot alt text to match new
captures.
* docs: refresh stack management page with current UI and anatomy tabs
* docs: fix convert-tab-error screenshot with fully visible error toast
* docs: convert troubleshooting section to AccordionGroup format
* docs(quickstart): refresh screenshots and align dashboard description
Replace all three first-boot and dashboard screenshots with current UI.
Add Security to the top navigation list, update gauge and Stack health
descriptions to reflect sparklines and column detail, and align
Configuration Status wording with the Introduction page.
* docs(editor): rewrite anatomy panel, replace all screenshots
- Correct the anatomy panel tab inventory: the panel has eight tabs
(Anatomy, Activity, Dossier, Drift always; Environment, Networking,
Doctor, Storage when the node advertises the matching capability), not
three as previously documented
- Add table describing all eight tabs with capability gates and links to
dedicated feature pages
- Add anatomy-tabs.png screenshot showing the scrollable tab row
- Note the Doctor severity dot (red for blocker, amber for high-risk)
- Remove the stale Markdown-export subsection; Dossier and Activity are
now covered in the tab table
- Replace all six stale screenshots with fresh 1920x1080 captures
- Replace the compose diff preview screenshot
* docs(files): refresh Files & Volumes screenshots and fix context-menu alt text
Replace all 9 stale screenshots on the Files & Volumes page with fresh
captures from the production node. Fix three alt-text strings that did
not match the live UI: removed hardcoded octal value 644, and added the
Duplicate, Copy to, and Move to entries missing from the context-menu
alt text.
* docs: rewrite Stack Activity page with full event categories and fresh screenshots
Expands the event category table from 5 to 10 entries to cover drift
detected, drift resolved, update started, health gate passed, and health
gate failed. Adds a live-disconnected-state section, a background-actor
attribution table, and a corrected troubleshooting accordion covering the
WebSocket reconnect case. Replaces both stale screenshots with fresh
1920x1080 captures from the production node.
* docs(drift): rewrite drift detection page with screenshots and full coverage
Full rewrite of the Drift Detection feature page. Adds two previously
undocumented finding types (network-undeclared, network-missing), expands
the temporal section to distinguish the raw-file hash from the parsed-model
hash, documents the two-layer spatial-engine and ledger architecture,
explains when the ledger is reconciled (post-deploy vs manual re-check vs
tab open), adds Activity timeline integration note, introduces a Limitations
section (no background scanner, port-range caveat, history cap, advisory-only
enforcement), expands Troubleshooting from five entries to seven using the
AccordionGroup convention, and adds four production screenshots.
* docs(drift): use CardGroup for Related section
* docs(dossier): rewrite Stack Dossier page with full feature coverage
* docs(networking): rewrite Compose Networking page with full feature coverage
* docs(doctor): rewrite Compose Doctor with full 30-rule reference, screenshots, and cross-links
* docs(networking): add production screenshots and correct alt text
Adds 7 production screenshots for all sections of the Compose Networking
page and updates the four placeholder alt texts written before screenshots
were taken to match what the actual images show (arr-net external badge,
swag service with 443/tcp and 80/tcp, single-service exposure intent row).
Also adds the full-panel overview image at the top of the page.
* docs(environment-guardrails): rewrite with project env file, env file status, and screenshots
* docs(storage): rewrite Storage Portability page with screenshots and full coverage
Rewrites compose-storage.mdx from a 61-line sketch into a complete
reference page. Key additions: Where to find it section with screenshot,
full storage inventory section documenting all mount type/access/status
chips and the Linux owner display, expanded portability verdict section
with per-reason detail and edge-case caveats (read-only binds, symlink
escapes, anonymous volume risks), snapshot coverage section with admin
scope and remote-node behavior, Findings in Doctor cross-reference, and
six troubleshooting accordions covering tab visibility, bind status,
external named volumes, render errors, and snapshot coverage states.
Adds two production screenshots: storage-tab.png and storage-node-bound.png.
* docs(stack-labels): rewrite with accurate permissions, capability gate, dry run, live preview, and color conflict docs
* docs: rewrite Stack Sidebar page with accurate feature coverage
Rewrites the Stack Sidebar documentation page to match the current UI.
Key changes:
- Fix branding header description (shows logo + version, not just version)
- Fix bulk mode icon description (stacked-rows, not square)
- Add cross-node search section (fan-out behavior, Other nodes section,
unreachable-node warnings, click-to-switch navigation)
- Update Labels submenu description (inline New label creation, Manage labels link)
- Note that Delete only appears when the user has delete permission
- Remove the auto-update implication from Schedule task description
- Rewrite the Activity ticker section with the full 6-state priority cascade
table; remove the non-existent IDLE state; correct pulsing-dot behavior
- Replace all 7 stale screenshots with fresh production screenshots
- Add new sidebar-cross-node-search.png screenshot
* docs(atomic-deployments): refresh screenshot and document project env files, rollback readiness, and recovery actions
* docs(atomic-deployments): fix rollback permission visibility and banner string accuracy
The Rollback menu entry is hidden by the frontend when the user lacks
stack:deploy; it never appears and does not 403. Fixed the step-4
narrative and troubleshooting accordion to match.
The rollback-failure banner emitted by ComposeService is
'=== Rollback failed. Manual intervention may be required ==='
(period, capital M). Fixed both occurrences in the page.
Updated the Settings navigation path from the nonexistent
'Roles & Access' to the real 'Access'.
* docs(deploy-progress): rewrite with health gate, inline style, and 9 fresh screenshots
Add health gate section covering all four states (observing, passed, failed,
unknown) with exact UI banner text and the configurable observation window.
Expand the inline style section with full band content, 4s auto-dismiss, and
pill handoff. Add Scanning as a supported entry point. Replace all 6 existing
screenshots and add 3 new ones (modal-health-gate, inline-banner, setting-style).
Add two health gate troubleshooting accordions. Add Related CardGroup linking to
health-gated-updates, stack-activity, deploy-enforcement, and atomic-deployments.
* docs(health-gated-updates): refresh screenshots and correct signal row order and label
* docs(deploy-enforcement): rewrite with fleet replication, honor suppressions location, scan-failed dialog state, and fresh screenshots
Adds the Fleet policy replication section covering control/replica behavior,
Managed by control node banner, and Demote to control. Documents the exact
location of the Honor suppressions toggle (bottom of Policies tab). Expands
the block dialog section with the scan-failed row state. Updates all three
screenshots to the current visual design. Restores the Admiral license note
and corrects the policy-card scope description.
* docs(app-store): rewrite with mobile layout, fresh screenshots, and registry admin note
- Replace all 5 stale screenshots with 1920x1080 production captures
- Add app-store-mobile.png showing the status masthead layout
- Document mobile single-column layout in a new Mobile subsection
- Note that the featured hero has its own Deploy button
- Mark the category rail as desktop only with a cross-link to Mobile
- Add admin-account requirement to the custom registry section
- Add Related CardGroup linking vulnerability scanning, deploy progress, deploy enforcement, and resources
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.
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.
The cross-node capability gate cached its verdict, so a remote replaced by older
code at the same URL stayed trusted until the cache expired, reopening the
non-admin HTTP escalation and the over-broad stop. The probe now hits the
remote's live /api/meta on every gated action (concurrent calls deduped, never
cached across requests, fail-closed), so a downgraded remote is detected
immediately.
Two stop-result gaps are also closed:
- A remote stop result must now cover exactly the confirmed stacks (one per
stack, no extras, no omissions), not merely exclude extras, so a dropped
confirmed stack is no longer accepted as clean. runLocalLabelStop reports one
result per confirmed stack even when the label has vanished, so a current
remote always satisfies the check.
- The local stop exception path now reports the full confirmed set, so a
confirmed stack that lost its label is not dropped when the local stop throws.
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.
A remote WebSocket upgrade was forwarded to the target node before any role
check, and the forwarder authenticates the connection to the remote as an
admin-gated console_session. A non-admin could therefore open a remote
container-exec or host-console socket that the local handlers reject.
The hub now applies the same gate before forwarding: logs and notifications
stay open to any authenticated user, every other path (container exec, host
console) is admin-only, and machine node_proxy tokens are rejected on the
interactive paths. This covers proxy-mode and pilot-agent remotes, which share
the forward path.
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.
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.
Proxied requests authenticated to a remote node previously ran as admin
regardless of the originating user's role, so a non-admin using the UI
against a remote node could reach admin-only handlers there.
The forwarding primary now asserts the user's role on a trusted header
that the remote honors only for node_proxy/pilot_tunnel bearers (the same
trust model as the license tier header), and the gateway overwrites the
header on every proxied request so a client cannot smuggle it. An absent
header keeps admin for direct instance-to-instance and background service
calls; an unrecognized role fails closed to read-only.
* 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.
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.
* 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.
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).
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.
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.
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.
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).
Opening the New Schedule modal from a stack's Schedule action, or editing
a stack-targeted task, set the node and the stack, but the node-change
effect then ran and cleared the stack, forcing the user to reselect it.
Move the stack-clear out of that effect into the Node picker's change
handler so it only fires on a user-driven node change. A programmatic node
set from prefill or edit now preserves the stack.
The schedule editor exposed only a raw 5-field cron input, which is
unfriendly for the common "daily at 3am" or "one-time next week" cases.
Add a Simple mode (now the default) that builds the cron from a
frequency and time: Once, Hourly, Daily, Weekly, and Monthly. Advanced
mode keeps the raw cron input as an escape hatch.
- Simple mode compiles to the existing cron_expression on save; no
schema or scheduler changes.
- One-time schedules reuse the existing delete-after-run flag: selecting
Once turns it on and locks it so the task runs a single time (cron has
no year field, so without it the task would repeat yearly).
- Editing a task opens in Simple mode when its cron maps to one of the
simple shapes, otherwise in Advanced; switching a custom cron to Simple
warns that it will be replaced.
- Day-of-week uses frosted toggle chips and the time uses hour/minute
selects so the controls match the rest of the editor.
When the advertised version changed and the follow-up release-notes request
failed (non-OK response, network rejection, or unparseable JSON), the previously
loaded notes, version label, and link were never cleared, so the changelog kept
showing the prior version's notes as if they described the new advertised update
and did not retry. The settled-version latch then suppressed an automatic
refetch.
The changelog now drops the loaded notes, version, and link the moment it
commits to fetching for a different advertised version, so any non-matching,
null, or failed result falls through to the empty state with the online
changelog link. The skeleton covers the in-flight gap, and the settled-version
latch is preserved so a failure does not loop.
The changelog recorded every settled release-notes fetch as loaded for the
advertised version and rendered whatever notes came back, without checking that
the returned version matched. Because the version lookup and the release-notes
lookup use independent caches (and the version can fall back to Docker Hub while
notes are GitHub-only), a drifted response could show one version's notes under
a different advertised update.
The changelog now binds strictly: notes render only when the endpoint confirms
they belong to the advertised latest version; a mismatch falls through to the
empty state with the online changelog link. The settled-version latch is kept so
a mismatch does not loop the fetch.
The changelog tab fetched release notes once and held them in component state
without tying them to a version, and the endpoint did not report which release
the notes belonged to. When a newer release surfaced while the sheet stayed
mounted, reopening the changelog could show the previous version's notes, and a
GitHub/Docker Hub fallback or independent cache timing could leave the notes out
of sync with the advertised latest version.
The release-notes endpoint now returns the release version (normalized
tag_name). The changelog keys its loaded notes to the advertised latest version,
refetching when that version changes, and labels the notes with the version they
belong to so the displayed content is always explicit.