Commit Graph

6 Commits

Author SHA1 Message Date
Anso 3fed67a3f1 fix: assorted UI polish across Home, Security, and Resources pages (#1590)
* 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.
2026-07-11 20:55:37 -04:00
Anso 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.
2026-07-05 05:30:56 -04:00
Anso 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
2026-06-29 14:35:50 -04:00
Anso 65a69d9ecc fix(nodes): never send stored node tokens to clients (#1281)
Node read endpoints now return a client-safe projection that omits the
stored api_token and exposes a has_token boolean instead, so a node's
long-lived proxy credential is never serialized to a browser or API
token client. The token stays encrypted at rest and is read server-side
only by the components that need it (the remote proxy, the connection
test, and the mesh dialer).

The edit form opens the API Token field blank, and a blank value keeps
the existing credential, so saving an edit without retyping the token no
longer clears it; a non-empty value rotates it. The backend enforces the
same rule defensively.

Node management actions (add, edit, delete, test connection, generate
node token) are gated in the UI to match their server-side permission
checks, so operators no longer see an action the API would reject. The
test-connection route also gains the missing server-side permission and
token-scope guards.

Also validate the x-node-id header and fall back to the default node for
malformed values instead of an obscure 404, and return 400 (not 500)
when deleting the default node.
2026-06-02 10:26:31 -04:00
Anso 3ad6ea9c5d feat(pilot): make Docker Compose the canonical pilot enrollment payload (#1121)
* feat(pilot): make Docker Compose the canonical pilot enrollment payload

The Add Node dialog for a pilot-agent now returns a Compose snippet
instead of a single-line docker run command, and the enrollment dialog
walks the operator through a save-and-up flow. The compose project name
and container name align with what SelfUpdateService looks up at boot,
so a Compose-deployed pilot can be updated remotely through the Fleet
view without intervention on the remote host.

Docs (pilot-agent, remote-updates) were rewritten to match.

* test(e2e): align pilot enrollment spec with Compose payload

The spec was written against the docker-run payload; it now asserts the
Compose YAML the dialog renders.
2026-05-20 02:05:21 -04:00
Anso 16774ae515 feat(fleet): add node management actions to Fleet Overview (#1064)
* refactor(nodes): extract node create/edit/delete modals into useNodeActions hook

Pulls the inline Add/Edit/Delete/Pilot-enrollment modal stack out of
NodeManager.tsx and into a reusable useNodeActions() hook in
components/nodes/. Settings continues to consume the same modals via
this hook, with an onTestResult callback used by Settings to render the
existing connection-detail panel after a successful test.

The hook also extends the auto-test-on-save behavior so that saving a
proxy-mode remote node from the Edit dialog re-runs the connection test
when the API URL or token has actually changed (skipped when only name
or compose dir was edited).

* feat(fleet): surface Add/Edit/Delete node actions on Fleet Overview

Adds an admin-only Add node button to the right of Refresh on the Fleet
header, opening the same Add Node dialog used by Settings. Each node
card's three-dot menu now exposes Edit node and Delete node items
(routed through the shared useNodeActions hook) alongside the existing
Cordon item, so operators can manage node lifecycle without leaving the
Fleet page.

The card kebab is shown to admins regardless of tier; Cordon stays
Admiral-only. Delete is hidden on the local default node. After any
Add/Edit/Delete the Fleet overview refetches so the grid reflects the
change immediately.

* docs(fleet): document Add/Edit/Delete node actions on Fleet Overview

Updates the Action buttons table to cover the new Add node entry point
on the Fleet header, and adds a new Node actions menu section
describing the per-card Edit/Delete/Cordon items, their tier and
permission gating, and the auto connection test that fires after
saving a proxy-mode remote.
2026-05-15 19:54:23 -04:00