* fix(settings): toast and block save when node settings fail to load
Failed GET /settings no longer seeds defaults as the dirty baseline. Shared
loader pins node identity, shows an error callout until an authoritative load
succeeds, and keeps Save disabled across all eight node-scoped sections.
* test(settings): cover load-failure toast and App Store refresh pin
Remove the incorrect settings-load-failure docs image. Assert toast ownership for current vs stale/aborted loads, and pin App Store cache refresh to the PATCH node across a mid-save switch.
* fix(settings): reject malformed settings bodies and skip pre-node bootstrap load
Treat non-object 200 /settings payloads as load failures so null or array bodies cannot seed editable defaults. Do not fetch or toast while the active node id is still undefined, so hard refresh settles to a single failure toast.
* feat: add configurable notification dispatch retries
Allow each node to set 0-3 extra in-process delivery attempts with a fixed
one-second delay for routes, agents, and Test dispatch.
* fix: harden notification retry settings load/save and channel tests
Guard Delivery retries against failed and out-of-order same-node settings responses, and cover Slack/webhook retry classification alongside Discord.
* fix: clear Delivery retries saving state and correct screenshot alt
Separate save-request ownership from value-generation invalidation so a successful PATCH cannot leave Save retries stuck on Saving, reset saving on node switch, and align the Channels screenshot alt with the committed image.
* fix: surface invalid notification retry settings instead of false saved clamp
Align Channels GET handling with the backend strict 0-3 parser so stored values like 9 or 1.5 show as error needing repair, matching runtime fallback to 0 instead of displaying a clamped saved policy.
npm audit was failing CI with a high-severity DoS advisory
(GHSA-3jxr-9vmj-r5cp, plus GHSA-jxxr-4gwj-5jf2 on the frontend) for
brace-expansion, pulled in transitively through eslint -> minimatch.
It is a lint-only dev dependency, never bundled into the runtime
image, but the audit gate blocks on it regardless.
Pins it via overrides instead of accepting the wider dependency bump
npm audit fix proposes, since minimatch already declares a compatible
range for the patched version.
* fix(fleet): isolate corrupt snapshot file decrypt failures
A single damaged encrypted fleet-snapshot row no longer fails detail, restore, or off-site upload for the whole snapshot. Unavailable members are marked, restore is blocked before mutation, and cloud upload fails closed with no PutObject.
* fix(fleet): fail closed on damaged enc snapshot envelopes
Unrecognized enc: payloads no longer fall through as usable plaintext. Only clear legacy prose stays readable; delimiter-byte and similar envelope damage stays unavailable through restore and cloud upload.
* fix(fleet): subordinate legacy enc prose to envelope shape
Legacy exceptions no longer trigger from = or whitespace alone. Encryption-shaped payloads (length and hex density) stay unavailable through restore and cloud upload, while short genuine prose such as enc:hello remains usable.
* fix(fleet): preserve non-envelope enc legacy plaintext
Any non-empty enc: payload that is not encryption-shaped is kept verbatim, including punctuation forms such as enc:hello-world, while envelope-shaped damage remains unavailable.
* feat(blueprints): require confirmed rollout preview before reconcile
Persist place/remove approval with an intent fingerprint and transition matrix so Apply, Retry, ticks, and pin cannot mutate the fleet until the operator confirms the current blast radius. Preview surfaces requirements, health, and informational in-flight rows without executing them.
* fix(blueprints): silence unused retry nodeId lint error
* test(blueprints): harden approval gate coverage and preview clarity
Add real reconcileOne place/remove fan-out and STALE_GUARD regressions, surface reachability in the rollout dialog, align warning totals, and document the fail-closed upgrade pause.
* test(blueprints): cover legacy approval schema migration
Seed a pre-approval database with an enabled Blueprint and live deployment, run production DatabaseService startup, and assert pending null auth columns plus a fail-closed reconcile gate.
* test(blueprints): clarify legacy approval migration fixture
Extract seed/boot helpers so the migration regression reads as a linear upgrade path without changing assertions.
* fix(blueprints): report apply outcomes and gate manual withdraw
Return per-node reconcile outcomes from Confirm Apply, block create preview on unmanaged same-name stacks, and require an approved remove outcome for every manual withdraw or evict.
* fix(blueprints): scope withdraw approval to destructive eviction
Require remove approval only for snapshot/evict confirms and evict_blocked rows. Keep plain stateless standard withdraw as an immediate stop, and update withdraw-route tests to seed remove approval when needed.
* feat: add service-scoped Compose update and restore
Allow updating or rebuilding one declared Compose service on multi-service
stacks without recreating siblings, with recovery snapshots, health-gate
observation, and prune holds for rollback images. Full-stack update paths
and single-service UX stay unchanged.
* fix: sanitize service-scoped update log messages for CodeQL
* fix: address service-scoped update audit findings B-01 through B-07
* fix: complete service-scoped update audit metadata and surfaces
* test: wrap Updates readiness tests for deploy-feedback context
* fix: keep service recovery reachable without Deploy Progress
Make failed service-gate recovery discoverable when Deploy Progress is
disabled or dismissed, suppress stale image-scan notification side
effects, normalize ComposeService line endings, and add focused
regression coverage.
* fix: resurface ContainersHealth density and expand on multi-service stacks
Service grouping hid the summary strip and Compact/Detailed/Expand controls that still applied to multi-container stacks.
* feat: add Apprise as a fourth notification channel
Support keyed and stateless Apprise endpoints with secret-safe public DTOs, fail-closed malformed config, and mode-specific Settings UI. Docs and screenshots updated for four-channel Channels and routing.
* fix: harden Apprise secrets at rest and preserve-on-write saves
Encrypt Apprise endpoint and config with CryptoService so a downgrade cannot leak via SELECT *. Align channel and routing saves so blank destination fields omit config on same-mode URL edits, enforce keyed notify IDs, and keep secrets_redacted truthful.
* fix: harden Apprise route type changes and mixed-version config UI
Require a raw channel_url when switching notification-route types so ciphertext cannot strand under Discord/Slack/webhook. Default missing remote apprise status, replace Channels state on node switch, and exercise the production config-column migrator.
* fix: tolerate stub fleet configuration payloads without agents
Normalize remote Apprise agent status only when notifications.agents is present so successful Pilot/stub fetches stay online instead of throwing into the offline catch path.
* fix: correct TypeScript in configuration normalize tests
* fix: ignore stale Channels agent bodies after node switch
Compare the active node after response JSON parsing so a slow body
cannot overwrite the newly selected node's channel state.
* fix: isolate corrupt Apprise crypto and keep keyed Tags visible
Decrypt failures on one Apprise row no longer 500 agent/route lists or
suppress sibling channel dispatch. Treat public /notify/<redacted> as keyed
so Tags remain editable after reload.
* feat: guide missing external network creation during deploy
Detect missing external networks before Compose runs, prompt or auto-create safe bridge networks, and keep unsupported declarations blocked with trusted deploy provenance.
* test: align deploy context and settings fixtures with missing-network gate
Update caller spies, EffResource expectations, StacksSection save keys, and git-source spy cleanup so CI matches the new deployStack context and auto-create setting.
* fix: drop unused renderError binding in missing-network resolver
Satisfies no-unused-vars so backend ESLint CI passes; callers already key only on model presence.
* fix: use HTTP-safe clipboard helper in missing-network dialog
navigator.clipboard fails on plain HTTP LAN hosts; route copy actions through copyToClipboard so Docker and Compose copy buttons work on self-hosted instances.
* fix: simplify missing-network dialog actions and copy label
Drop the Compose snippet escape hatch, move secondary actions under More, and rename the terminal copy action to Copy create command so the footer is a clear Cancel / Create decision.
* fix(image-updates): treat multi-arch child digests as up to date
Floating tags like redis:8-alpine can store a platform child digest locally
while the registry tag resolves to the parent index. Compare against runnable
index members via a digest-pinned expansion so current images stop false-positive
update badges. Fixes#1630.
* fix(image-updates): preserve UTF-8 in capped GET and fail closed on nested indexes
Accumulate raw Buffer chunks before hashing or decoding so multibyte UTF-8
cannot corrupt content digests. Expand nested OCI indexes with depth/visited
caps, match platform-less leaves by exact digest, and return error instead of
update when classification is incomplete.
* fix: prefer-const lint error in registry-api test
* fix(image-updates): align multi-arch checkNode tests with 2-arg signature
After rebasing onto main (#1640), checkNode no longer takes nodeName. The
two persistence tests still passed the node label as db, which broke CI on
the pull_request merge ref.
* fix(image-updates): guard tag/repo components before registry URL construction, dismiss CodeQL false positive
Add defense-in-depth validation in probeManifestForRef that rejects tag
strings containing URL-injection characters (/ ? # \ null) and repo paths
with .. segments before they reach the outbound HTTPS request. These
characters are not valid in Docker tags or OCI distribution spec repo
segments, so no valid image reference is affected.
Exclude js/request-forgery on registry-api.ts via codeql-config.yml.
Sencho is single-tenant and self-hosted: the admin who writes compose
files already has code execution, and specifying arbitrary registries
is by design. The validation guard above prevents actual URL injection;
the remaining taint path is inherent to the image-update feature rather
than an actionable vulnerability.
Closes CodeQL alerts #531 and #532.
* feat(ui): add Classic, Smart, and Compact desktop navigation styles
Introduce a shared app-nav registry and reachable model so TopBar, the
command palette, and mobile menus share one destination source. Smart bar
is the default; Appearance gains a Navigation subsection with quick links.
* fix(e2e): stop clearing top-nav prefs on every reload
The desktop-navigation suite used addInitScript to wipe mode storage,
which re-ran on reload and undid the classic/compact values under test.
* fix(ui): harden nav PR docs and Compact quick-link coverage
Drop the partial docs-refresh import that left missing image assets and a stale Display screenshot, keep navigation-scoped operator docs against main, and add an E2E path that proves Compact quick-link add, persist, and render after reload.
* fix(ui): polish Compact quick links and menu mastheads
Align Smart/Compact menus with Theme chrome, keep pin labels always visible, and drive add capacity from persisted pins (max five) with a trailing + picker and per-pin remove.
* test(e2e): exact-match Compact Networking pin locator
Avoid Playwright strict-mode clash with Actions for Networking.
* fix(ui): simplify Compact quick link removal and fix + button trailing
Remove the (...) dropdown per quick link in Compact mode. Right-click
context menu remains as the sole on-bar removal affordance. Fix the +
add-button to trail quick links rather than pinning to the far right
by removing flex-1 from the quick-link rail.
* fix(notifications): neutralize satellite-local node names in alert bodies
Fleet-aggregated alerts embedded each instance seed name (often Local)
while the hub badge already named the remote. Drop identity prefixes and
use type-aware local wording so attribution stays on the badge.
* fix(docs): correct image-update default check cadence
Operator docs still said six-hour polling; the seeded default is two
hours in interval mode, and the cadence is configurable or cron-based.
* test(notifications): assert hub stamps roster name on neutral remote bodies
Cover the fan-in path that attaches hub roster identity while leaving the
satellite message body unchanged.
Remove the leftover paid-only blockingEnabled switch so enabled
block-on-deploy policies enforce on every tier, matching the
documented every-tier security surface. Existing Community policies
begin blocking immediately with no migration.
* feat: add node-scoped Networking operator page
Adds a Networking view with overview, topology, inventory, and findings.
Shared aggregate reads back the page; Resources keeps prune and redirects here.
Includes fail-closed network delete guards, operator docs, and /nodes/:slug/networking routing.
* fix: rename unused variable n to _n to satisfy no-unused-vars lint
* fix: keep top bar search clickable when nav grows
* feat: complete Compose-first Networking Phase 2 operator assistant
* fix: move networking action visibility helper out of component module
* feat(networking): complete Compose-first Networking operator page
Finish the node-scoped Networking page (Overview, Networks, Topology,
Findings) with design-system parity and correct finding semantics.
- Rebuild detail sheets on SystemSheet/SheetSection; align the tab band,
masthead, and mobile tone with Fleet and Security.
- Encode the host-mode and exposure severity matrix; fix collision counts
so intentional shared externals are not flagged; add one typed drift
predicate shared by inventory, topology, badges, and overview counts.
- Preserve per-container attachments and IPs on topology node clicks;
drawer-only click with an explicit logs action; ownership and boolean
filters; bound large graphs before layout.
- Aggregate cached Compose Doctor findings into the Findings tab with
honest source labels, structural merge and dedupe, staleness
reconciliation, and a shared exposure-context helper both engines use.
- Networks tab: privacy-safe service search, precise ownership counts,
schema v3 with version-2 adapters on every endpoint, pre-confirm delete
reasons, and the shared sortable table with an internal scroll region.
- Interop: Fleet node-card networking signal with pending-intent
navigation, stack-to-node backlink, and Dossier/Drift deep links.
- Enrich sanitized inspect with an allowlisted connected-container list;
fetch topology once and filter client-side.
- Docs and tests across every new finding kind, adapter, and flow.
* fix(networking): correct drift count, exposure fail-soft, and inspect crash paths
Address code-review findings on the Networking page implementation:
- Fix the Overview drift count to use the shared drift-kind predicate instead
of a hardcoded list that omitted external-network-missing.
- Gate Compose Doctor's unclassified-exposure and reverse-proxy-undocumented
rules on exposure-context availability, so a DB read failure no longer
fabricates findings (mirrors the live engine's existing fail-soft behavior).
- Guard the per-stack exposure-intent read in topology aggregation so a
transient DB failure degrades to unknown intent instead of failing the
whole response.
- Harden the network detail drawer against a partial inspect payload from an
older remote node, and log the real error instead of a bare catch.
- Remove now-duplicated severity-rank and drift-kind helpers in favor of the
shared modules; drop dead backend-only exports; widen the frontend schema
version type to a plain number instead of casting past a literal type.
- Add coverage for the delete-guard precedence, the full host-mode severity
matrix, the schema-2 compatibility adapter, and the sanitized connected-
container allowlist; tighten two tests that were not exercising the
behavior they claimed to.
* fix: add missing onOpenNodeNetworking prop to FleetView experimental test
The added required prop on FleetViewProps broke the merge-build when the
test file (on main but not on this branch) was compiled against the
updated FleetView interface.
Reframe docs, OpenAPI registries copy, and License trial/expiry helpers
so Community is the full AGPLv3 product and Admiral leads with current
assurance (Hardened Build, Recovery Vault, support, governance).
Mesh, Secrets, and Host Console stay limited-availability without
Admiral sales framing.
Fleet-aggregated reclaim alerts showed Node Local in the body while the
badge already named the remote. Use a node-neutral message so identity
comes from the hub badge; leave stored rows unchanged.
* fix(ui): default Reduced Motion on Calm and quiet decorative rails
Calm (and Reset to default) enables Reduced Motion; Signature clears it. Manual Motion stays independent of the visual-style card. Reduced Effects also stops decorative masthead rail animations. Related to #1614; does not close that issue.
* test(e2e): fix Calm Motion specs racing empty-create compose edit
Stop appearance init-script reseeding after mid-test mutations, and wait for Save and Deploy / mobile compose editor after empty create instead of assuming Anatomy Edit compose.
* feat: add developer-mode startup and stack hydration timing
Instrument boot-to-list and detail hydration with commit-aligned milestones, truthful request stages, and destination/gateway debug duration logs so performance work is guided by measurements.
* fix: redact stack names and complete hydration request stages
Stop logging stack identifiers in containers debug timing, and record state_dispatch (plus detail fetch spans) so copied reports match the advertised stage breakdown.
Remove the MonitorService 6-hour version-check cooldown so node_update_available
fires when the shared version cache observes a published release, matching the
Fleet update button. Dedup and publish-pending gating still prevent spam.
* feat: add Admiral Hardened Build channel and business assurance surfaces
Introduce Studio Saelix entitlement-backed Hardened Build switching, a
single-flight image operation coordinator, Recovery Vault naming, Admiral
Account settings, and typed Fleet update failures while preserving Community
custom-repo and targetless pull-current updates.
* fix: harden image-op paths and clear CI CodeQL/pilot flake
Validate operation IDs before filesystem use, use hostname checks in Fleet
fetch mocks, sanitize registry probe logs, and swallow expected TCP teardown
errors in the pilot reverse-route post-handshake test.
* fix: sanitize image-op docker config write and probe logs
Allowlist-copy registry host keys and base64 auth before writing the
temp DOCKER_CONFIG, and log registry probe failures with a fixed message
so CodeQL no longer flags network-to-file and log-injection mediums.
* fix: address Admiral Hardened Build audit blockers
Expose imageChannel so hardened Fleet peers still POST for typed rejection, claim community updates before 202, terminalize helper failures, gate Hardened on paid, and align support/docs/e2e wording.
* fix: terminalize image ops on helper survival and aborted claims
* fix: prevent recreating persist from overwriting helper-exit failure
* test: assert helper-exit failure lands before recreating persist
* fix: keep current pointer when acknowledging a stale image operation
The AGPLv3 relicensing change left a duplicated url mapping key under
info.license, which breaks YAML parsing for any Mintlify docs preview
on any branch (error duplicated mapping key).
Release publish runs leave steps.release.outputs.pr empty, so fromJSON crashed
even when the credit step was gated off. Credit external issue openers with an
inline thanks suffix on logical changelog bullets instead of a Thanks section.
Keep fatal API lookup logs status-only.
* docs: align README and overview docs with current AGPLv3 tier model
Community is now described as the complete free product, with a single
consolidated Admiral section replacing scattered per-bullet tags that had
drifted out of sync with the actual gates (security scanning, private
registries, and most of RBAC/audit moved to Community over time). Remove
promotion of Sencho Mesh, Fleet Secrets, and Host Console, which are no
longer discoverable by default. Align the Fleet Secrets, Host Console, and
Sencho Mesh sections in the features overview with the limited-availability
framing already used elsewhere on that page.
Refresh every README screenshot to the current UI and add new captures for
Security, Blueprints, Scheduled Operations, and Compose Doctor.
The introductory tier-framing sentence and docs navigation placement for the
now-hidden surfaces are intentionally left for a later, dedicated pass once
the Admiral positioning work settles.
* docs: restructure README around the open-source Community product
Drop "What Sencho is not (yet)", fold its known-limitations pointer into
the Documentation section. Rewrite Telemetry and data handling to drop the
Admiral license-validation detail, since this file is the open-source
entry point and Admiral customers work from their own agreements. Move
the Admiral section to just before Documentation, community, and license
so the README leads with the Community product throughout.
Gate Routing, Secrets, Host Console, and Mesh dashboard/settings surfaces on the existing useExperimental readiness flag so immature operator surfaces stay out of the default UI while paid and admin backend gates remain unchanged.
* docs: relicense Sencho to AGPLv3 and reframe Community positioning
Replace BSL with AGPLv3 for the public Community product, update contributor
and licensing copy for Community-focused contributions, and surface source
and license links in Settings About.
* docs: clarify CLA scope and Community contribution framing
* fix(ui): split About link constants and harden AboutSection test selectors
* fix(ui): lower-overhead Reduced effects path for constrained GPUs
Disable backdrop-filter glass and solidify chrome fills when data-effects is reduced (Calm default), with Appearance warning and docs for constrained rendering devices. Related to #1614.
* test(ui): harden Reduced effects glass e2e and docs wording
Parse Color Level 4 slash alpha so opacity asserts are not vacuously true, cover dialog/toast/overlay both ways with stable data-sn hooks, and soften troubleshooting copy that overstated the unmeasured diagnosis.
* docs(ui): lead constrained-GPU guidance with Reduced Motion
Rename the troubleshooting section to neutral GPU framing, point Appearance and docs at Reduced Motion first, and show the callout whenever Motion is off so Calm or Reduced effects alone do not hide the validated workaround.
The Timeline lane marker lacked shrink-0, so inside the fixed 80px label
column the widest labels flex-shrank their color dot to zero width. The
Maintenance and Lifecycle dots collapsed and rendered as no dot at all,
while the shorter labels kept theirs. Add shrink-0 so every lane dot
renders at a consistent size.
Maintenance was the only label wide enough to fill the 80px column on its
own, which is what left its dot no room. Rename it to Upkeep so all five
labels fit and align. A registry test now pins each lane's key, order,
and label, and asserts every lane carries a color, so a future rewrite of
the action registry cannot silently rename a lane or drop a dot color.
Replace the desktop 8-column table with card-like mobile rows on phones,
following the ImagesTab pattern. Each row shows a 44px checkbox for compare
selection, a severity dot, truncated image ref with trigger and timestamp
meta, count tags (total vulns + fixable, at most 2), and a chevron.
Secret/config-only scans use the FINDINGS dot and suppress the clean tag,
matching the getSeverityKey classification from severityStyles.
Extract a shared HistoryStateMessage component to deduplicate the
loading/error/empty state blocks between the mobile and desktop branches.
* fix: polish stack health table, security card heights, and scanner-gate copy
- Remove state dot column from Stack Health rows; state still signaled
via row tinting and sparkline color
- Reposition Update available badge adjacent to stack name and track
the theme accent (--brand) instead of static fuchsia
- Equalize Top exploit-risk findings and Severity x exploitability
card heights via h-full in a stretching grid
- Add resolution prop to CapabilityGate so scanner-gated tabs show
"Install a scanner from the Scanner setup tab." instead of the
misleading "Upgrade the node" message
* fix: scope Resources scroll area to table contents only
- Remove bg-background from the outer wrapper so search/filter bars
no longer sit on a dark strip
- Replace outer ScrollArea with a plain div so search and filter
controls stay fixed while only table rows scroll
- Add ScrollArea (max-h-[62vh]) inside each table card for Images,
Volumes, and Networks tabs
* fix: remove static bg-background from Security table scroll areas
Images and History tab tables used bg-background on their ScrollArea,
forcing a near-black fill that didn't adapt to the theme. Removing it
lets the parent card's bg-card show through, matching the Resources
table pattern that correctly respects DIM, OLED, and Light modes.
* fix: collapsible search icon and transparent Combobox across Resources and Security
- Replace always-visible search inputs on Resources tabs (Images,
Volumes, Networks) and Security Images (desktop + mobile) with
Fleet-style collapsible search: an icon button that expands to
a full input on click, collapses on blur when empty
- Add [&>button]:!bg-background to Security Images severity
Combobox, matching Fleet's transparent dropdown design
* fix: remove redundant orange badge from Unmanaged tab label
The count was shown twice: once as inline text in the tab and again
as an orange bg-warning pill badge. The inline text remains; the
duplicate badge is removed.
* fix: outline buttons invisible in Light mode
- Changed outline variant border from border-input to border so it
uses --card-border, which maintains a visible 0.035 lightness gap
from --background in Light mode (was 0.005, essentially invisible)
- Boosted Light-mode --button-inner-glow from a 4% white inset to
a 10% inset plus a subtle drop shadow for the physical-key feel
* fix: bump table cell horizontal padding from 8px to density-aware token
Changed TableHead and TableCell px-2 (8px) to px-[var(--density-row-x)]
which resolves to 20px comfortable / 16px compact. Matches the density
system used by StackHealthTable and gives every shadcn-based table
proper breathing room on both edges.
* fix: expand collapsible search before typing in ImagesTab test
The test was written for the old always-visible search input. Now that
the search bar collapses to an icon, click it first to expand before
interacting with the input.
* fix: reorder Settings Infrastructure to Nodes, Stacks, Fleet
Move the Stacks section directly below Nodes so the Infrastructure
sidebar reads Nodes, Stacks, Fleet. Pure array-element relocation in
the settings registry; the sidebar renders in array order.
* fix: simplify pilot enroll command and add Step 2 copy button
Step 2 of the pilot-agent enrollment modal now shows "docker compose up
-d" instead of the redundant "-f compose.yaml" form, since Step 1
already has the operator save the file as the default compose.yaml. Add
an inline copy button next to the command so it can be copied without
selecting the text by hand.
* fix: stack Fleet/Security tab heading actions on mobile
The shared FleetTabHeading kept its title and action buttons on one row
at every width, so on phones the Security Suppressions heading and its
Export VEX / Add suppression buttons were crammed together. Stack the
heading and actions vertically below the md breakpoint and restore the
original row layout at md and up, so desktop is unchanged.
* feat: add node selector to mobile Home masthead
The mobile Home page showed the active node as static text, so switching
nodes was only possible from the Stacks page. Render the same compact
NodeSwitcher in the Home masthead kicker slot, giving a second place to
view and switch the active node with the identical popover and Manage
nodes action.
* feat(security): add triage status and OpenVEX justification parity
Share triage options across SuppressionsPanel and the scan-sheet suppress dialog, require justification for not_affected and false_positive, and document the fields.
* fix(security): use design-system Select for triage dropdowns
Replace native selects so OpenVEX justification options use themed popover content instead of unreadable OS option lists in dark mode.
* fix(security): keep triage justification Select controlled
Pass an empty string instead of undefined so Radix Select does not flip between uncontrolled and controlled when the placeholder is shown.
* feat(resources): show multi-stack usedByStacks on images
Classify images with a deduped sorted stack reverse index, surface chips in the Images table and inspect sheet, and clear node-bound sheet selection on active-node change.
* feat(registries): add exact-ID tag browser with non-401 failures
Add GET /api/registries/:id/tags using credentials for that registry row only, map upstream auth failures to 424, and surface a Registry tags section on the image inspect sheet.
* fix(registries): distinguish unreachable hosts from auth failures
Map auth transport errors to REGISTRY_UPSTREAM (502), surface registry list-load failures in the tag panel, document Used by and Registry tags, and add parser coverage.
* fix(registries): drop unused RegistryTagsPanel __test export
The non-component export tripped react-refresh/only-export-components and failed Frontend lint in CI.
Classify images with a deduped sorted stack reverse index, surface chips in the Images table and inspect sheet, and clear node-bound sheet selection on active-node change.
* feat(resources): bind prune to fingerprinted itemized plans
* fix(resources): repair prune plan volume usage and preview list
Source volume RefCount from docker df, keep preview rows from flex-shrinking, tighten managed image attribution and becomesFree, and stop audit summaries from claiming success on rejected prunes.
* feat(scheduler): include node identifier in remote proxy error messages
Remote lifecycle proxy errors from postToRemoteStack,
executeUpdateRemote, postToRemoteContainer, and getRemoteContainers
now include a stable node label (name and numeric id) so a hub
operator reading run history can correlate failures to a node without
cross-referencing the task table.
The prefix matches the existing containerNotFoundMessage pattern in
the same class. Transport-level failures (DNS, ECONNREFUSED, timeout)
also receive the node label. A startsWith guard prevents double-
prefixing within the same method. The restart fan-out catch relies
on the inner postToRemoteStack error for node context so the label
appears exactly once.
* fix(scheduler): pilot-aware remote proxy no-target error context
Use formatNoTargetError for null proxy targets so pilot tunnel disconnects
are not misdiagnosed as missing credentials. Standardize the failure prefix
to Remote node name/id, share require/rethrow helpers, and cover pilot
no-target plus remote container action failures in tests.
* feat: add compose discovery for setup preflight and sidebar empty state
Expose read-only compose discovery via GET /api/stacks/discovery and setup
diagnostics. Replace the blank sidebar with path-aware discovery and move
adopt into a dedicated dialog with a three-tab Create Stack flow.
* test: assert post-setup handoff via sessionStorage read-back
The Setup preflight test spied on Storage.prototype.setItem to check the
post-setup adopt handoff. When the jsdom storage probe fails and the test
harness swaps in its in-memory storage stub (which does not extend Storage),
that stub's setItem never touches Storage.prototype, so the spy records zero
calls and the assertion fails even though the component wrote the value.
Read the value back with sessionStorage.getItem instead, matching how every
other storage test in the suite asserts. This is robust to both the native
jsdom storage and the in-memory fallback.
* fix(setup): surface compose discovery as a preflight check row
Drop the Setup discovery banner and non-working Review button. Show
counts as a pass row in EnvironmentChecks (Setup only) and keep
Enter Sencho as the handoff that opens adopt when candidates exist.
* test(setup): cover zero-count discovery row omission
* fix(stacks): widen adopt scan to any yaml and rename into place
Homelab layouts often use nginx.yml or plex.yml. Surface those for
adopt (except overrides), rename to compose.yaml on move so stacks
register, and reset the confirm UI when a move fails.
Non-admin users no longer see Update buttons in Fleet View; the UI shows
read-only "Available" badges instead. The accordion describing a 403-click
path was orphaned by the frontend role-gating that shipped in an earlier PR.
The Note above the troubleshooting section already states the admin-role
requirement in plain language.