Commit Graph

31 Commits

Author SHA1 Message Date
Anso b1decbb32a docs: v1 docs refresh (batch 7) (#1627)
* docs(introduction): refresh screenshots and correct stale nav/tab coverage

Replace all 5 screenshots with fresh 1920x1080 production captures.
Document the shipped Take down stack action, the Compose Labels stack
tab, and the Docker Labels Fleet tab, none of which were mentioned.
Correct the Console nav item to note it is a limited-availability
surface rather than a plain role/tier-gated view.

* docs(quickstart): refresh screenshots and correct preflight, dashboard, and menu drift

Replace all three quickstart screenshots with fresh captures and correct
several claims that drifted from the current UI:

- Document the environment preflight's 7th check (Sencho compose
  location), previously missing entirely from both the text and the
  screenshot alt text.
- Add the Stack health table's Source and Port columns, and note that
  columns are sortable and default to load order.
- Note the masthead's running-container count, live CPU/memory readout,
  and alert count.
- Fix the profile menu list: replace the nonexistent "Feedback" entry
  with "Open New Issue", drop "Appearance" (it lives in Settings, not
  the profile menu), and add the conditional "Billing" entry.
- Note that the sidebar groups stacks by Docker Compose label once
  labels are assigned, with pinned stacks first.

* docs(configuration): document missing env vars and fix JWT secret wording

Add SENCHO_UPLOAD_DIR, TRIVY_CACHE_DIR, SENCHO_MESH_RECONCILE_INTERVAL_MS,
SENCHO_MESH_PROXY_TUNNEL_IDLE_MS, SENCHO_COMPOSE_COMMAND_TIMEOUT_MS, and
SSO_OIDC_CUSTOM_ENABLED, all real environment variables that were missing
from the reference tables. Clarify that the JWT signing secret has no
environment variable at all, generated and stored in the database only,
rather than implying an unused JWT_SECRET var exists.

* docs(stack-management): refresh against current live app and source

Rewrites the Stack Management page against the production node and
frontend source: adds the Compose Labels anatomy tab (new since the
last refresh), the Mute action on the stack header and sidebar
context menu, corrects the update-available banner wording and the
sidebar context menu's lifecycle ordering, notes the Doctor tab's
severity dot and the scan-status banner, and replaces all 14
screenshots with fresh production captures.

* docs(editor): refresh against current live app and source

Replace all 8 screenshots with fresh 1920x1080 production captures (mobile
shot at phone viewport). Document the self-stack protection dialog that
blocks deploy/delete actions on the stack running the current Sencho
instance, the multi-container summary strip and Compact/Detailed density
toggle, the mutually exclusive Expand containers / Expand logs controls,
the Files tab full-screen toggle, and the post-deploy scan-status banner
on the Anatomy panel, none of which were previously covered.

* docs(editor): recapture mobile compose screenshot without redaction

The previous mobile screenshot used the plex stack, whose compose volume
mount includes a real host path that had to be blacked out and overlaid
with placeholder text, leaving a visible seam. Recapture against the
dozzle stack instead, whose only volume is the Docker socket, so the
screenshot needs no editing.

* docs(stack-file-explorer): refresh page against current app and code

Renames "Files & Volumes tab" references to the current "Files" tab
label, documents the full-screen toggle and word-wrap control, expands
the non-browsable-volume reasons to match the current containment
logic (single-file binds, the full protected host-path list, and the
Docker-unreachable named-volume case), documents the 100-item bulk
selection cap and the 5000-file/1 GiB archive limits, corrects the
non-existent DISK_FULL error code, notes that override compose files
are unprotected, and scopes the atomic-write claim to fs-backed roots.
Replaces all 9 screenshots with fresh production captures taken in the
Files tab's full-screen mode, so no other stack UI (health metrics,
logs) appears in the background.

* docs(resources): refresh against current live app and source

* docs(dashboard): refresh against current live app and source

* docs(app-store): refresh against current live app and source

Replaces all 6 screenshots with fresh production captures and corrects
the Environment Variables and About-panel metadata claims to match what
the bundled LinuxServer.io registry actually returns today.

* fix(docs): recapture app-store advanced-tab screenshot without scroll cut-off

The prior capture was taken mid-scroll to reveal the Security checkbox,
leaving the template logo and About text cropped awkwardly at the top.

* docs(global-search): refresh against current live app and source

* docs(deep-links): refresh against current live app and source

- Add App Store, Logs, Update, Console, and Audit to the URL table (previously undocumented)
- Document hub-only URL redirect behavior and cross-link to Multi-Node Fleet
- Note Fleet tab URL segments do not always match their on-screen label (Status/configuration, Map/dependencies)
- Document the no-env-files edge case (Env tab absent, falls back to compose)
- Clarify Settings section-list state is phone-only; desktop always normalizes to /settings/appearance
- Note which top-level views share identical URLs between desktop and phone
- Remove greenfield-violating temporal phrasing ("now has", "as today")
- Replace em dashes in touched bullet list

* docs(appearance): refresh against current live app and source

* docs(stack-activity): refresh against current live app and source

Add the missing "Stack taken down" event category, cross-link the
Suppressed badge to notification mute rules, list the new Compose
Labels tab in the Anatomy panel strip, and replace both screenshots
with current production captures.

* docs(dossier): fix generated-facts wording, add rollback readiness section

Corrects three Dossier tab Generated Facts rows against current frontend
behavior: the Ports count is not filtered to host-published mappings, the
Network row's "bridge" label is fixed text rather than a derived driver,
and missing env var names appear only in the Markdown export, not the
live tab. Adds a Rollback Readiness subsection under Connected Features
(previously only an orphaned Limitations bullet), links to Compose
Networking and Storage Portability, and clarifies that a missing export
section can mean either an older node build or a failed on-demand fetch,
not only a version gap. Recaptures all three screenshots against
production.

* docs(stack-drift): refresh against current live app and source

Fixes the stack detail tab bar description (Compose Labels tab was
missing, Files/Edit compose were wrongly listed as tabs), documents
that re-check only requires read access to the stack (so viewer and
auditor roles can trigger a ledger write), and cross-links the network
findings to the Networking tab's runtime drift section and the Fleet
Overview's Drift filter. Replaces all four screenshots with fresh
1920x1080 production captures.

* docs(compose-doctor): refresh against current live app and source

Corrects the rule count from 31 to 32 and documents the previously
undocumented self-managed-stack guard rule and enforcement, the
per-browser dismiss control for the summary card and Doctor tab dot,
the acknowledged summary state, the full Health-Gated Updates verdict
mapping, and the temporary exposure intent option. Replaces all four
screenshots with production captures.

* docs(compose-networking): refresh against current live app and source

Adds cross-links to the new node-wide Networking operator page, documents
the "view node networking" link and the unset/inherit exposure-intent pill
labels, corrects the stale Resources Hub network-tab reference, and
replaces all screenshots against the production node.

* fix(docs): unbreak stack-activity page render

The screenshot alt text used backslash-escaped quotes, which is
invalid MDX/HTML attribute syntax. Mintlify failed to parse the file
and silently dropped it from routing, so the page 404'd despite being
listed in docs.json navigation.

* docs(environment-guardrails): refresh against current live app and source

Fix the ENV FILES section description: it lists env_file: entries and
project files with an existence problem, not every declared env file.
A cleanly-resolving project file is already named in the Project
Environment File panel above it. Replace all three screenshots with
current production captures.

* docs(docker-label-audit): refresh against current live app and source

Add production screenshots (page had none), a Capability gating
section, a Limitations section, and a Troubleshooting accordion.
Tighten the secret-redaction heuristic description and cross-link
Stack Labels, Compose Doctor, Environment Guardrails, and Node
Compatibility.

* docs(compose-storage): refresh against current live app and source

Captured fresh production screenshots (both prior images were stale)
and fixed a leftover pre-rename card title pointing at the Files tab.

* docs(stack-labels): refresh against current live app and source

Removed the stale trailing-dot sidebar claim (that rendering was
removed from the sidebar in a prior UI pass), documented the new
label-scoped notification muting available from the sidebar, stack
context menu, and Settings panel, corrected the Fleet Actions card
copy to match the redesigned cards, added the previously undocumented
bulk-assign size cap and the node-scoped label bulk-action API, and
replaced all 8 screenshots with fresh production captures.

* docs(sidebar): refresh against current live app and source

Removes the stale per-row label dot claim (removed from StackRow), corrects the update indicator and Updates chip color from orange to fuchsia, and documents Mute/Take down in the context menu, the label group mute kebab, Ctrl+A/Esc bulk-mode shortcuts, pin-eviction toast, and the offline-node skip behavior in cross-node search. Replaces all 8 screenshots with fresh production captures.

* docs(deploy-progress): refresh against current live app and source

Corrected the Scan entry point (node-wide Scan this node from Security
Overview, not a per-stack config scan), added the missing Take down
verb and entry point, broadened recovery actions to cover restart and
rollback failures, fixed the modal status text and raw-output color
claims to match the live UI, and replaced all screenshots with fresh
captures from a throwaway demo stack on the production node.

* docs(atomic-deployments): refresh against current live app and source

* docs(health-gated-updates): refresh against current live app and source

* docs(deploy-enforcement): refresh against current live app and source

Correct the tier note to every-tier (verified against the current
policy gate, which no longer has a paid-only blocking switch), add the
Security Overview deploy-enforcement summary card, tighten the honor-
suppressions default wording, cross-link the separate pre-deploy scan
advisory dialog to avoid confusion with the block dialog, and replace
all three screenshots against the current production UI.

* docs(blueprint-model): refresh against current live app and source

Replaced all 11 screenshots with fresh production captures. Corrected
the volume-destroying-drift Enforce-downgrade claim (appeared three
times): that code path has no call sites in the runtime reconciler, so
any compose edit on a stateful or state-unknown blueprint always
re-enters Awaiting confirmation regardless of drift mode. Corrected
the Create workflow (creation does not trigger an immediate
reconciliation tick) and the Delete workflow (now requires typing the
blueprint name to confirm).

* docs(scheduled-operations): refresh against current live app and source

* docs(blueprint-model): drop tier framing, treat as Community-native

Blueprints carry no tier gate, so "available on every tier" implied a
comparison that doesn't exist. Removed the tier framing from the intro
note, the Prerequisites table, and the Security section; the role
requirement (admin to write, every role to read) already says
everything that matters.

* docs(auto-update-policies): refresh against current live app and source

Documents that every update trigger on this page, Apply now and
scheduled Auto-update tasks alike, runs through the same atomic backup,
build-aware rebuild, and post-update health gate as a manual update
from the stack editor, and is subject to the same deploy enforcement
policy gate. Corrects the readiness-computation description to scope it
to registry-image services and cross-links to Health-Gated Updates,
Atomic Deployments, Deploy Enforcement, and Stack Management. Replaces
the readiness board screenshot with a fresh production capture and
removes three orphaned screenshots left over from the page's prior
CRUD-policy layout.

* docs(auto-heal-policies): refresh against current live app and source

Correct the admin-only prerequisite: viewing policies and history is
open to every signed-in role, only creating/toggling/deleting requires
admin. Rewrite the matching troubleshooting entry to match, note the
overlap behavior between an All-services and a named-service policy on
the same container, and replace all three screenshots with a real
production policy.

* docs(webhooks): refresh against current live app and source

Replaced all three screenshots with fresh production captures (single
webhook create/reveal flow, two-card configured list), verified every
claim against WebhookService/StackOpLockService/registry.ts, and added
the previously undocumented per-stack lock skip behavior for
non-Git-source-sync actions with a matching troubleshooting entry.

* docs(global-observability): refresh against current live app and source

* docs(audit-log): refresh against current live app and source

- Fix nav terminology: Audit is a top navigation tab, not a sidebar tab.
- Correct retention claim: Admiral shows full retained history (default
  90 days), not a fixed 14-day window; the 14-day clamp only applies to
  the Community API, which has no navigation entry point at all.
- Fix settings path: Settings - Operations - Data Retention (previously
  pointed at the now-split Developer Diagnostics section).
- Restore current Recovery Vault naming (was stale Sencho Cloud Backup).
- Expand the tracked-actions list: stack take-down, label management,
  MFA reset, and notification suppression rules were missing.
- Note that assigning the Auditor role itself requires Admiral.
- Replace all four screenshots with fresh production captures (Stream,
  Table, expanded row, Data Retention) reflecting the current five-row
  retention card.

* fix(audit-log): reframe data-retention screenshot to card content only

The prior crop included the Settings sub-navigation panel, which isn't
part of what the surrounding text describes. Retake tightly cropped to
match the page's other screenshots (card content only, no chrome).

* docs(multi-node): refresh against current live app and source

Rewrites the Pilot Agent enrollment flow (now a generated compose.yaml
plus docker compose up -d, not a single docker run command) and the
Settings scope table (current registry: Stacks, Container Alerts,
Docker & Storage, Data Retention, Mute Rules, Recovery, and the
Admiral Account / Recovery Vault renames). Adds a Sencho Mesh
cross-link matching the live add-node form copy. Replaces all eight
screenshots with sanitized production captures.

* docs(pilot-agent): refresh against current live app and source

* docs(readme): refresh GitHub README against current live app

Replace all 9 screenshots (stale top nav showing a removed Console
item and missing the new Networking page); document Take down,
Drift Detection, Environment and Secrets Guardrails, Storage
Portability, Docker Label Audit, Remote Updates, and the node-wide
Networking dashboard; fix a broken non-root-user anchor link; correct
the notification channels list (no email channel exists yet) and the
global search scope (pages, nodes, and stacks, not containers or
services); rename "scan policy packs" to the current "scan policies"
terminology; broaden the App Store bullet to cover custom registries.

* docs(readme): reposition intro copy and refresh badge row

Lead with DevOps/platform/sysadmin framing instead of homelab-first,
drop pre-1.0 "production" language, state plainly that Sencho
provides a UI instead of promotional "does the work you do" phrasing,
and call out that multi-node was part of the architecture from the
start. Swap the Discussions badge for CodeQL, last-commit, open
issues, and live website/docs status badges, and add a blog link.

* docs: retake Dashboard and Global Search screenshots for accuracy

Configuration Status now shows Recovery Vault instead of the stale
Cloud Backup label, and the search palette capture includes the
Networking page entry added after the prior screenshot batch.

* docs: refresh Fleet View page against current app

Fleet View has drifted since its last rewrite: the Docker Labels tab,
Export Dossier action, Networking filter/badge, node label pills and
latency in topology, the Policy sync status row, and the node card
Mute submenu were all shipped but undocumented. The Check Updates and
Add Node actions also moved into the Overview toolbar (renamed Node
Update / Manage Nodes) and no longer sit in the shared action row.

Replaces all five screenshots with fresh production captures and
corrects the masthead's motion description (a calm shimmer on Healthy,
a steady glow on Degraded/Critical, not a pulsing dot). Also fixes a
Fleet Sync limitation that claimed no first-party sync-status panel
exists, now that the Status tab surfaces one.

* docs: refresh Fleet Dossier page against current app

Adds the page's first screenshot, documents the network exposure summary now included per stack, documents the storage-summary omission versus the Stack Dossier export, and adds two troubleshooting entries.

* docs(fleet-federation): deep rewrite for the rollout-approval gate

Federation's cordon and pin controls no longer take effect by
themselves: the reconciler requires a confirmed rollout preview
(Apply now -> Confirm Apply) before it mutates the fleet, and pinning
or unpinning always clears a blueprint's approval. Rewrote the mental
model, step-by-step, lifecycle table, security section, limitations,
practical workflows, troubleshooting, and cross-links to reflect that
gate. Also corrected the audit-log claims: cordon/uncordon/pin are not
filterable by a node.cordon/blueprint.pin action taxonomy, only by the
free-text search box against the method, path, and summary.

Replaced 4 screenshots and added 3 new ones (production fleet plus an
isolated instance to capture the rollout preview dialog in both a
safe and a blocked state), and fixed one stale pin-workflow sentence
in blueprint-model.mdx that the same audit surfaced.

* docs(fleet-federation): drop tier-availability framing

The feature isn't tier-gated, so calling out "every tier" reads as an
unnecessary advertisement. Removed the tier note and the Community/
Admiral prerequisite row, and reworded two role-visibility sentences
that had conflated tier with role.

* docs(fleet-actions): deep rewrite for the v2 action-card redesign

Rewrites the page against the shipped Fleet Action Card redesign: a
single cyan rail with per-card action-class chips instead of the old
per-card rose/purple/amber rails, plus the live blast-radius preview,
dry-run, and confirmed-target binding mechanics that replaced the old
static warning banners. Documents two previously-undocumented
endpoints (match-preview, prune/estimate), the preview-confirm-execute
model shared by all three cards, per-node locking, timeouts, and the
version-gated confirmed-target contract for mixed-version fleets. All
five screenshots recaptured against the live production UI.

* docs(fleet-actions): fix nested quote in screenshot alt text

* docs(fleet-sync): refresh against current app and document proxy gap

Retake all screenshots against the live fleet (control authoring view,
a genuine replica showing the read-only banner, and replicated
suppression rows) and document a previously-unwritten behavior: scan
policies, CVE suppressions, and misconfig acknowledgements are fetched
localOnly and are not proxied through the node switcher like most
other Security page tabs, so viewing a remote's Fleet Sync state
requires signing into that instance directly.

* docs(fleet-backups): refresh against current app and note new integration points

Replaces all 6 screenshots with fresh 1920x1080 production captures and
corrects several drifted details: the Cloud Snapshots panel's pagination
and per-item delete action, the exact Recovery Vault storage-mode label,
and the real per-file size-cap behavior (an oversized compose.yaml skips
the whole stack; an oversized .env is dropped but the stack still
captures). Documents two entry points that were missing from the page:
the pre-update snapshot checkbox in Health-Gated Updates and the snapshot
coverage nudge on the Storage Portability tab. Expands the single warning
banner description in the detail view to cover all four banner types,
notes the Snapshots tab and Recovery Vault are hub-only, and adds a
Where Fleet Backups fits table cross-linking the six adjacent features.

* docs(remote-updates): refresh against current app and add hardened-channel notes

Replace all screenshots with fresh 1920x1080 production captures and fix the
Node Updates trigger label (Check Updates -> Node Update). Add the Changelog
tab, correct the reconnect overlay's stale 'Update timed out'/Try Reloading
copy to the current Taking longer than expected/Reload to check text, and
document the Admiral Hardened Build channel's carve-outs (pinned-but-not-blocked,
entitlement-gated local update path). Disambiguate this page from the
unrelated top-level Update (Health-Gated Updates) nav tab.

* docs(node-compatibility): refresh against current app and expand capability list

Replace all screenshots with fresh production captures (node switcher,
capability lock card, connection test panel) and swap the lock-card example
from a now-hidden Host Console to Audit Log, which is directly reachable.
Bring the capability table from 26 to 35 entries to match the current
registry, add a Mute Rules row, and split out fine-grained fallback
capabilities (update-guard, service-scoped-update, cross-node-rbac,
stack-down-remove-volumes) with their non-lock-card fallback behavior.
Fix the compose-networking row, which incorrectly claimed to also gate the
node-level Networking overview. Note that Pilot Agent nodes never advertise
host-console regardless of version.

* docs(security): refresh against current app and document scan-node launcher

Replaces all screenshots with fresh production captures, documents the
Scan this node launcher and the third Scanner setup toggle (pre-deploy
scan advisory), tightens the Compose risks example list and the Policies
block-condition description to match the live app, and adds an On a
phone section.

* docs(vulnerability-scanning): refresh for exploit intel and risk-based policies

Documents exploit intelligence (KEV/EPSS evidence tags), the redesigned
risk-based scan policies (severity/known-exploited/fixable block
conditions replacing the single max-severity field), and the new
Findings badge state. Replaces all screenshots with fresh production
captures and adds a Secrets tab example.

* docs: refresh CVE suppressions page

Retook all screenshots against production (prior ones predated the
triage-status/OpenVEX UI and the edit capability). Documented the
suppression edit flow, which the page previously described as
delete-and-recreate only. Corrected the remote-node banner copy on the
Suppressions tab and added a screenshot of it. Cross-linked the
Misconfig acknowledgements panel that now sits on the same tab.

* docs(two-factor-authentication): refresh screenshots and document rate-limit layers

Replace all 11 non-count-variant screenshots with fresh production captures.
Document the throttled sign-in state precisely (kicker/hero/caption change,
not just the input), that a replayed TOTP counts toward the lockout counter,
and the separate per-network-address sign-in rate limit that sits in front
of the per-account MFA lockout.

* docs(api-tokens): refresh screenshots and document service-scoped deploy actions

Deploy Only now authorizes eight lifecycle POSTs (was six): the per-service
update and restore endpoints were missing from the scope table. Universal
restrictions gained a row for image channel management, which was already
rejecting API tokens in code but undocumented. Replaced all five screenshots
with fresh captures showing the current three-scope create flow and a
populated list with one token of each scope.

* docs(upgrade): refresh against current app and remove legacy-version framing

Fixes migration-list inaccuracies (registry credentials were never
plaintext, unlike node API tokens), rephrases the SSH/TLS and JSON-config
migration bullets to drop version-numbered legacy framing, adds a GHCR
mirror note matching the quickstart pattern, and cross-links the Hardened
Build entitlement-gated update path so digest-pinned installs aren't sent
down the manual docker pull steps.

* docs: refresh Backup & Restore against current backend

Broadens the encryption.key warning to cover everything CryptoService
actually encrypts (node tokens, Git source and Fleet Secrets, SSO/MFA,
Recovery Vault, fleet snapshot contents), not just registry credentials.
Adds the built-in backupData CLI as a no-host-tooling alternative to the
sqlite3 .backup command, cross-linked to Emergency command-line recovery.
Corrects node-token migration guidance: tokens are signed with a secret
stored in sencho.db and remain valid after a host move, so no
regeneration is required.

* docs: refresh Recovery guide against current Settings page and CLI

Documents the Settings · Recovery page's full System health / Environment /
Safe actions / Command-line hub instead of only its environment-preflight
section, adds the missing "Sencho compose location" preflight check, adds the
SSO/OIDC/LDAP lockout scenario now that disableSso.js exists, and corrects the
rollback description (exact UI label, backup-required and image-layer caveats).

* docs(emergency-cli): sync in-app page description, add CLI operational details

Aligns the "in-app Recovery page" summary with the freshly refreshed Recovery
guide (System health / Environment / Safe actions / Command-line recovery, SSO
providers, one-click command download). Adds constraints verified against the
CLI source: password/username minimums, the valid SSO provider identifiers with
an example, and a note that diagnostics.js always reports Docker as unreachable
(it runs without a live Docker connection, unlike the in-app page).

* docs(troubleshooting): refresh against current backend and nav

Corrects several claims that had drifted from the implementation:
network name validation (underscore is not a valid leading character),
the remote-update delayed-failure window (3 minutes, not 90 seconds),
and an unsubstantiated version-compatibility check on the Admiral 403
entry. Removes two entries describing legacy pre-v0.39 node behavior
that no longer applies to any currently shipped build. Replaces stale
"Profile > Settings > X" navigation with the current Settings hub
paths, and "wifi icon" with the current Test Connection label. Adds
Pilot Agent awareness to the remote-offline entry with a cross-link to
its dedicated troubleshooting section, and trims the docker-run
converter entry to point at the fuller, already-current version on the
Stack Management page instead of duplicating it with a broken anchor.

* docs(verifying-images): document the :dev GHCR integration tag

Verified the existing cosign/SBOM/VEX/tag-policy content against
docker-publish.yml and docker-preview.yml; all accurate. Added the
previously undocumented :dev/:dev-<sha> integration tag published on
every push to main via docker-dev.yml.

* docs(trivy-setup): sync install guide with node-scoped scanner and current Trivy upstream docs

Documents that Trivy installs independently per node, adds the TRIVY_BIN
override and non-PATH mounting option, fixes the deprecated apt-key install
flow and RHEL repo gpgkey placement, notes the Exploit intelligence toggle
for air-gapped hosts, and replaces the stale Scanner setup screenshot.

* docs(two-factor-admin): document lockout recovery and self-reset gotcha

Verified the reset flow, CLI recovery, and SSO toggle claims against
current source and replaced the stale admin-reset modal screenshot.
Added the failed-attempt lockout behavior (undocumented despite being
promised in the page description) and a warning about resetting your
own 2FA from the Users list, which signs you out immediately unlike
the self-service disable flow.

* fix(docs): make Settings and Self-hosting discoverable in the sidebar

The Reference and Operations groups used root pages that were only
reachable by clicking the ambiguous group label, and that click also
triggered an unwanted double action (expand plus navigate). List both
pages as explicit sidebar entries and disable global drilldown so group
headers only expand or collapse.

* docs(settings): sync reference page with current Settings Hub

Renamed License to Admiral Account throughout (Hardened Build channel
switch, image channel display, DURATION pill), corrected the password
policy, documented the new Appearance navigation and log-chip-color
controls plus font options, noted Mesh data plane is not on every
installation, fixed the label cap (50, not 100), and replaced every
screenshot with a fresh capture from the production node.

* fix(docs): apply the sidebar toggle-only fix to Start here and Product guide

These two groups had the same click-to-navigate-and-expand pattern as
Reference and Operations. Flattening them is safe here too: the
Product guide root page has no directory listing depending on it, and
the Start here root page's own hand-authored Next steps CardGroup
already covers the same links the auto-generated listing duplicated.

* docs(licensing): refresh licensing page for Hardened Build and sales-led Admiral pricing

Admiral pricing moved from self-serve checkout to a contact-sales model, and
a new Hardened Build image-channel switcher shipped in the Admiral Account
settings page; neither was reflected in the docs. Also documents the
lifetime-license edge case (no Manage subscription button, no Billing row)
and refreshes all four screenshots against the current UI.

* docs(security): sync reference page with current API-token and encryption scope

Verified every claim against the live implementation: the API-token universal
restrictions list was missing MFA management, Recovery Vault, and image
channel management; the encryption-at-rest field list was missing Recovery
Vault credentials; added a note on the password strength indicator's
recommended-vs-enforced distinction. Refreshed the SSO settings, API tokens,
and audit log screenshots against the production node.

* docs(settings): fix password policy and session-invalidation claims

Cross-checked against auth.ts while verifying the same claims on the
security reference page: the enforced minimum is 8 characters (the
12+/mixed-case/number text is a frontend strength hint, not a validated
rule), and a password change invalidates every other session for the
account rather than leaving them valid.

* docs(contact): sync contact channels with sales-led pricing and current support gating

Replace the retired contact@sencho.io with hello@sencho.io (the address actually
used in the website footer and the pricing page's Get in touch CTA), narrow the
licensing@sencho.io scope to existing-license activation/billing/refunds now that
new Admiral conversations route through hello@sencho.io, drop the stale LICENSE-file
and in-app upgrade-prompt claims (the AGPLv3 relicense removed both), correct the
Support settings path and add the published response-time targets, and remove the
unsubstantiated bug bounty mention.
2026-07-21 09:13:12 -04:00
Anso 0cd03c6f87 chore(ui): hide Mesh, Fleet Secrets, and Host Console behind experimental discovery (#1624)
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.
2026-07-13 11:55:50 -04:00
Anso 9b3f5c5a90 docs: relicense Sencho to AGPLv3 and reframe Community positioning (#1623)
* 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
2026-07-13 03:49:29 -04:00
Anso 7848ce339a docs: remove stale fleet update 403 troubleshooting accordion (#1607)
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.
2026-07-09 20:01:33 -04:00
Anso 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.
2026-07-04 21:20:20 -04:00
Anso 315e8b6379 feat: add node update alerts with changelog tab and skip-version handling (#1463)
* feat: add node update alerts with changelog tab and skip-version handling

- Add node_update_available notification category with blue/brand bell dot
- Route node_update_available notifications to Fleet -> Node updates sheet
- Add Changelog tab to NodeUpdatesSheet with GitHub release notes
- Add per-node skip-version persistence (node_update_skips table)
- Skip hides update CTA on node card and sheet; re-surfaces on newer version
- Skipped nodes excluded from Update all backend filter
- Add pulsating dot indicator on Changelog tab when updates available
- Always-visible View changelog action in notification row bottom
- Admin-only for all mutating controls (skip, unskip, update)
- Backend tests for skip-version semantics (15 tests)
- Update fleet-view.mdx, remote-updates.mdx, and OpenAPI spec

* fix: address audit findings - nested button, stale changelog, semver normalization, mobile intent

- Move View changelog button outside routable button (sibling element)
- Fix aria-label for node_update_available notification rows
- Support ?recheck=true on release-notes endpoint
- Invalidate release notes cache on forced recheck
- Store normalized semver (semver.valid strips v prefix)
- Skip fleetUpdatesIntent on mobile (desktop only)
- Add v-prefix normalization test

* fix: restore View changelog on same line as timestamp, opposite sides

The button is always visible at the bottom right of the notification card,
on the same row as the timestamp (just now), using justify-between layout.

* fix: update tests for node_update_available category and release-notes fetch

- Backend: monitor-service tests now expect node_update_available instead of system
- Frontend: NodeUpdatesSheet tests mock release-notes API call to prevent undefined then()

* fix: resolve ci lint failures
2026-06-26 00:07:51 -04:00
Anso bef51a979f feat(fleet): tidy the Overview toolbar and shorten tab labels (#1361)
- Move the Add node button into the Overview toolbar beside the
  Grid/Topology toggle so it sits with the view it acts on instead of
  showing on every Fleet tab. It stays admin-only.
- Collapse the node search to an icon button that expands to the full
  input on click and collapses again on blur once the query is empty,
  reclaiming toolbar width. An active query keeps it open.
- Match the sort-direction toggle and the sort dropdown to the outlined
  dark fill of the Filters button for a consistent toolbar row.
- Swap the Check Updates icon to the refresh-with-dot glyph.
- Rename two tabs: Fleet Actions to Actions, Dependencies to Map
  (display labels only; internal keys unchanged).

Update the feature docs to the new tab labels.
2026-06-12 02:10:21 -04:00
Anso af4083175c feat(fleet): add read-only dependency map tab (#1324)
* feat(fleet): add read-only dependency map tab

Add a fleet-wide Dependencies tab to Fleet view that maps how stacks,
services, networks, volumes, and ports relate, with flags for missing
dependencies, port conflicts, orphaned resources, and cross-stack shared
resources. Read-only; filterable by stack, node, and flag; collapsed by
default with a list-view fallback at scale.

The graph is derived at request time from Docker and compose metadata, so
no new table or persisted state is introduced. A per-node graph endpoint
feeds a hub aggregation endpoint that fans out across the fleet and
degrades gracefully, surfacing unreachable or unparseable nodes inline
while the rest of the map still renders.

* fix(fleet): harden dependency map flag detection and remote merge

Address review findings on the dependency map:
- Port-conflict detection now does pairwise host-scope overlap, so an
  unrelated bind on the same port and protocol but a different specific host
  IP is no longer flagged, and the flag lands on the exact scoped port node.
- A running service's depends_on target is only considered satisfied when it
  is actually running, so a crashed (exited) dependency is surfaced while a
  deliberately stopped stack stays quiet.
- Declared external networks and volumes are reported missing when they do
  not exist on the host instead of being assumed present.
- The hub deep-validates each remote node-graph payload before merging, so a
  reachable-but-malformed remote degrades to a single node error rather than
  failing the whole fleet map, and the validation failure is logged.
- Searching or filtering on a network, volume, or port now also reveals the
  services that claim it and their stacks.
2026-06-06 11:18:36 -04:00
Anso ce08a593d7 feat(settings): reorganize the settings hub into domain groups (#1321)
* refactor(settings): split System Limits and regroup the hub

System Limits had grown into a grab-bag of host alert thresholds, Docker
cleanup, and mesh data-plane controls under one mislabeled section. Split it
into Host Alerts, Docker & Storage, and Fleet Mesh, and split Developer into
Developer Diagnostics and Data Retention. Reorganize the sidebar into ten
domain groups: Personal, Access, Infrastructure, Monitoring, Notifications,
Automation, Organization, Security, Operations, Help.

Each section now saves only its own keys, so a concurrent edit in one section
no longer clobbers another. Data Retention sends the audit-log window only on
a paid plan, matching the field's existing visibility, so a Community save no
longer fails on a key the operator cannot set. NumberChip moves to a shared
module and the toggle reuses the existing shared component. The /settings API
is unchanged.

* test(settings): cover registry structure and per-section save payloads

Add structural invariants for the ten-group registry (every item maps to a
real group, ids are unique, the System Limits and Developer splits land in the
right groups with the right gates, renamed labels and the Registries paid gate
hold) and per-section payload tests asserting each split section patches only
its own keys, including the Community path where Data Retention omits the paid
audit-log key.

* docs(settings): document the regrouped settings hub

Rewrite the settings reference for the ten-group layout, replace the System
Limits page with Host Alerts, Docker & Storage, and Fleet Mesh, and document
the prune-on-update, reclaimable-space banner, and mesh auto-recreate settings
that were previously undocumented. Update the Settings navigation breadcrumbs
across the feature docs and refresh the affected screenshots.

* fix(settings): show Access sections as instance-global, not operator-scoped

License, Users, SSO, and API Tokens are instance-global settings but the
masthead scope label rendered them as operator-scoped because it keyed off the
old Identity group. Only Personal sections (account, appearance) are
operator/browser-scoped now; everything else reads as global.

Also add a compile-time exhaustiveness guard to the section switch so a future
SectionId added without a matching case fails the build instead of silently
rendering a blank panel.

* docs(settings): remap remaining settings breadcrumbs to the new groups

Update the navigation breadcrumbs that still pointed at the removed Identity,
Alerts, and Advanced groups: API Tokens and Users now sit under Access, Webhooks
under Automation, Labels under Organization, App Store under Infrastructure,
Appearance under Personal, and scan policies under Security > Vulnerability
Scanning. Correct the settings reference scope note so Access reads as global.

* docs(settings): remap renamed-section breadcrumbs across feature docs

Sweep every feature, operations, getting-started, and reference page for
navigation paths that still named the renamed settings sections, and point them
at the current ones: Security becomes Security > Vulnerability Scanning,
Notifications becomes Notifications > Channels, Routing becomes Notifications >
Notification Routing, and Developer becomes Operations > Developer Diagnostics
(with its retention windows under Operations > Data Retention). App Store moves
under Infrastructure and the four-group overview in the getting-started intro is
rewritten to the ten groups. Separators each page already used are preserved.
2026-06-05 23:01:37 -04:00
Anso 28ea610e81 refactor(masthead): remove stat-tile hover tooltips (#1319)
Drop the cursor-following hover tooltips from the masthead stat tiles: the RUNNING
tile on the Home dashboard (the managed / external / exited breakdown) and the
CONTAINERS tile on the Fleet view (the running / total split). Both revert to plain
stat tiles, matching the CPU and MEM tiles beside them. The dashboard and fleet docs
are updated to match and the orphaned screenshot is removed.
2026-06-05 21:53:14 -04:00
Anso 865d792874 feat(pricing): collapse to two tiers (#1309)
* feat(pricing): collapse to two tiers (Community + Admiral)

Collapse Sencho's pricing from three tiers (Community / Skipper / Admiral)
to two: a generous free Community tier and a single paid Admiral tier. The
Skipper tier is removed.

Now free in Community: auto-heal, auto-update, scheduled operations,
webhooks, notification routing, Fleet Actions and bulk operations, SSO
preset providers (Google / GitHub / Okta), unlimited users with admin and
viewer roles, and deploy safety (atomic deploys, auto-rollback, and
one-click rollback).

Admiral (paid) is focused on running and governing a fleet: blueprints,
Fleet Secrets, deploy enforcement, vulnerability report export, audit log,
host console, private registries, mesh networking, node cordon, managed
cloud backup, LDAP / Active Directory SSO, and the advanced RBAC roles
(deployer, node-admin, auditor) with per-resource scoped assignments.

Internally the license variant distinction is removed so tier is binary
(community / paid). License validation still verifies the Lemon Squeezy
store and product before granting paid status.

Docs and the contributor guide are updated to the two-tier model.

* docs(pricing): correct licensing page to two-tier pricing and tidy stale tier wording

The licensing docs page kept the old Admiral pricing plus a Founder
Lifetime column and an Enterprise paragraph after the two-tier collapse.
Update it to $12/month or $99/year, drop the lifetime and Enterprise
content, and link to the pricing page for current pricing.

Also fix stale "Skipper" wording in CLA.md, SUPPORT.md, one test title,
and three test comments. Historical CHANGELOG entries and the
retired-Skipper license-guard test are intentionally left as-is.

* docs: align licensing and SSO pages with the two-tier model

Correct the SSO overview so the Google, GitHub, and Okta presets read as
available on every tier, matching the provider table; only LDAP and Active
Directory require Sencho Admiral. Remove the lifetime-plan references from the
licensing, settings, and troubleshooting pages so they reflect subscription-only
Admiral pricing.

* fix(rbac): omit scoped permissions from /me on the Community tier

Scoped role assignments only take effect on the paid tier, but GET /api/permissions/me returned them unconditionally, so a downgraded instance with leftover assignments rendered per-resource affordances the API then rejected with 403. The endpoint now mirrors the permission middleware and includes scoped permissions only on the paid tier. Adds a regression test covering the downgrade case.

* docs: use custom-pricing wording on the contact page

The two-tier model has no Enterprise tier; reword the contact page's enterprise pricing/deals to custom pricing/deals so it does not imply a tier that no longer exists.
2026-06-04 17:45:53 -04:00
Anso d8f73f8203 feat(fleet): show stack-label filtering in Fleet View on every tier (#1268)
* feat(fleet): show stack-label filtering in Fleet View on every tier

Stack labels and their assignments are a Community feature: the per-node
label reads are available to any authenticated user and are already used in
the per-node Stacks view. Fleet View, however, only fetched the fleet-wide
label palette and per-stack chips when the instance was on a paid tier, so a
Community user who had labelled their stacks saw no label dots on node cards
and no Tags filter in the Overview toolbar.

Drop the paid gate on the fleet label fetch so the palette, the per-stack
chips, and the Tags filter render for everyone who has labels. Node-level tag
aggregation (used for topology grouping) stays paid and is unchanged.

* fix(fleet): surface update-status failures and soften the reconnect timeout

Three reliability fixes in the Fleet View update path:

- The fleet update-status poll swallowed fetch errors in an empty catch, so a
  failing poll left a silently stale table with no breadcrumb. It now logs the
  failure (both thrown errors and non-ok HTTP responses) without toasting on
  every tick, and keeps the last-known statuses.
- The local-update reconnecting overlay declared "Update timed out" after five
  minutes, which falsely reported failure when a large image pull simply ran
  longer than the reconnect window. It now shows a non-failure "Taking longer
  than expected" state with a "Reload to check" action, and the timeout is a
  named constant that mirrors the backend update timeout.
- The fleet overview fan-out already logged a node that failed to report; the
  update-status and update-all fan-outs now log the rejected node and reason
  too instead of discarding it.

* test(fleet): backfill Fleet View hook, component, and update-tracker coverage

Adds unit and component coverage for the previously untested Fleet View
surface: all six Overview hooks (overview, update-status, polling cadence,
preferences, fleet labels, node labels), the NodeCard, OverviewTab,
OverviewToolbar, NodeUpdatesSheet, UpdateStatusBadge, and ReconnectingOverlay
components, and the FleetUpdateTrackerService state transitions.

* test(fleet): assert update-status poll preserves last-known statuses on failure

Adds an explicit case that seeds statuses from a successful poll, then fails
the next poll, and verifies the table keeps the seeded statuses (and logs
without toasting) rather than relying on the implementation implicitly.
2026-06-01 13:20:36 -04:00
Anso 519a59ed2e feat(fleet): open Fleet Actions tab to Community (admin-only) (#1153)
* feat(fleet): open Fleet Actions tab to Community (admin-only)

Removes the requirePaid guard from the five Fleet Actions endpoints
(fleet-stop, fleet-prune, match-preview, prune/estimate, bulk-assign)
and drops the matching isPaid parent gate on FleetActionsTab so Community
admins can run fleet-wide bulk operations. requireAdmin stays on every
endpoint; operator and viewer roles still 403 on apply.

Tests flipped from "403 PAID_REQUIRED on community" to positive
"reachable on community + admin" assertions. Docs (fleet-actions,
fleet-view, licensing, overview, stack-labels) rewritten to state the
admin-role requirement once and drop the prior Skipper framing.

* fix(fleet): apply audit findings from PR #1153 review

- stack-labels.mdx: fix the page intro that still framed fleet label
  actions as "Operators on a Skipper or Admiral license". The cards are
  now Community + admin, so the intro reads "Admins also get a pair of
  fleet-wide actions".
- Collapse redundant role-rule statements on the two affected pages.
  fleet-actions.mdx now states the admin gate once in the lead-in Note
  and again only in the troubleshooting accordion (the Prerequisites
  row was duplicative). stack-labels.mdx trims the "Limits and rules"
  bullet to the value-add half (label authoring is open to every role)
  and drops the Fleet Actions repetition.
- Strip now-no-op mockTier('paid') calls from non-tier tests across the
  three fleet test files, plus the test-wide default in the
  fleet-action-card-endpoints beforeEach. Those mocks were misleading
  after the routes stopped consulting tier; if a future change re-adds
  requirePaid the tests will fail loudly instead of silently passing.
2026-05-22 01:27:27 -04:00
Anso 60f893a81f feat(fleet): move bulk Remote OTA updates to Community tier (#1151)
Drops `requirePaid` from `POST /api/fleet/update-all` so Community admins
can dispatch bulk node updates. Per-node OTA was already Community-
reachable (admin-only); this completes the move so the full Remote OTA
surface ships at Community.

Frontend mirrors the backend: removes `canBulkUpdate` from
NodeUpdatesSheet so the "Update all (N)" affordance is purely data-
driven on `updatableRemoteCount > 0`.

Docs realigned to drop fence-spec and Skipper-only phrasing on the
Update all bulk action:
- features/licensing.mdx: Community line now lists Remote OTA (per-node
  and Update all); Skipper Fleet Actions parenthetical drops "bulk
  update all"
- features/remote-updates.mdx: Note rewritten to role-only requirement
- features/fleet-view.mdx: Update all (n) bullet drops the tier clause
- features/overview.mdx: Fleet View and Remote updates blurbs drop the
  Skipper/Admiral fences
- operations/upgrade.mdx: Note rephrased without naming tiers

Test coverage:
- fleet.test.ts: tier-gating spec flipped to assert Community access
- fleet-pilot-update.test.ts: bulk-OTA dispatch suite now spies tier
  to Community so it doubles as a regression guard
2026-05-21 23:54:45 -04:00
Anso 08caa914ce docs: v1 docs refresh (batch 2) (#988)
* docs(atomic-deployments): refresh page around current UI and behavior

Rewrites the page to match the v1 docs refresh template. Corrects
several factual errors against the current code, fills in missing
detail, and adds a screenshot of the rollback overflow menu.

Notable corrections:
- Scheduled tasks do not run atomically; only stack editor Deploy and
  Update, App Store installs, webhook triggers, and image auto-updates
  pass the atomic flag through to ComposeService.
- Rollback lives in the stack editor's More actions overflow menu, not
  on the action bar directly. The backup timestamp renders as a
  sub-line of the menu item.
- Health probe is a 3-second window with an exit-code check on every
  container labelled with the compose project name; describe this
  exactly rather than as 'waits briefly'.
- Document where backups live (DATA_DIR/backups/<stack>/), why they
  are kept outside the compose folder, and that the slot is one per
  stack with overwrite semantics.
- Document the four streamed log markers users see in the deploy
  progress modal during the atomic flow.
- Add a troubleshooting accordion group covering missing menu entry,
  late crashes outside the probe window, manual-intervention message,
  and the single-slot retention edge case.

* docs(deploy-enforcement): refresh page for v1 and align with current enforcement paths

Update the page to match the current pre-flight gate behavior, the v1 modal chrome on the
block dialog, and the AccordionGroup troubleshooting pattern used across the v1 docs.

Drift items corrected:
- Replace the broken vulnerability-scanning/deploy-blocked-dialog.png reference with three
  fresh captures under docs/images/deploy-enforcement/ (policy list, policy editor, block
  dialog).
- Drop "Recreate from the stack actions menu" and the git-source apply pre-flight claim;
  neither path runs the gate.
- Add bulk label deploy and the auto-update scheduler to the enforced code paths, with a
  dedicated subsection for the auto-update interaction (alert-and-skip, not 409).
- Drop the false claim that severity chips in the block dialog are clickable; the dialog
  is informational.
- Document the compose-parse-fails-closed branch with its synthetic violation label.
- Refresh dialog copy to reflect the v1 ModalDestructiveHeader (kicker, title, button
  variants).
- Convert the troubleshooting Q&A into AccordionGroup blocks and add accordions for the
  compose-parse-error case and the auto-update-skipped case.
- Quote the verbatim audit-log summary format.

* docs(blueprints): refresh against v1 UI and add federation/state-review coverage

* docs(git-sources): refresh page against v1 UI and current behavior

Rewrites the page against the v1 docs refresh template (Note tier-gate,
sectioned anatomy, AccordionGroup troubleshooting), aligning prose with
the live UI labels and the current code paths.

Corrections:
- Authentication toggle reads "Public (no auth)" / "Personal Access
  Token" (not "None"), and apply mode "Auto-write files" (not
  "Auto-write").
- Diff dialog kicker is GIT . PULL PREVIEW; local-edits state opens an
  Overwrite local edits? confirmation modal whose primary button is
  Overwrite and apply.
- Sidebar pending indicator is a small GitBranch icon, not a brand-color
  dot, and the image-update dot takes priority over it on the same row.
- Pending update banner appears in the panel; Review re-fetches the
  commit and opens the diff (no client-side payload caching).

Adds coverage for:
- Anatomy of the panel (pending banner, form, last-applied stat strip,
  footer actions).
- 10-second webhook debounce window.
- Pending compose/env content is encrypted at rest in the database, not
  just the token.
- Auth/host failures map to HTTP 400, never 401, so they do not sign
  the user out.
- Per-stack lock serializes pull, apply, and create-from-git so a
  webhook firing during a manual apply waits rather than racing.
- Compose validation has a 10-second budget; clone fetches have a
  30-second timeout.
- New troubleshooting accordion for Pending commit has changed since
  this pull was fetched.

Recaptures all five screenshots from the v0.74.x production node,
signed in as admin: panel, create-from-git tab, pull-preview diff
dialog, sidebar GitBranch pending icon, webhook Action select with
Git source sync highlighted.

* docs(stack-labels): refresh page for v1 sidebar grouping and fleet-action surface

- Lead with the v1 behavior the previous page did not cover: the sidebar
  groups stacks under collapsible label headers (PINNED first, label
  buckets sorted by stack count desc then name asc, UNLABELED last)
  with a count chip per group. Trailing colored dots on each row
  (max 3 + N overflow, paid-only) supplement the headers.
- Drop the stale claim that a label-pill filter bar lives between
  search and the stack list; that UI no longer exists.
- Drop the right-click-on-pill bulk actions table (Deploy all / Stop
  all / Restart all). The legacy per-node action endpoint stays in
  the backend but no longer has a UI binding, so the page documents
  only what users can click today.
- Document the two Skipper+ Fleet Action cards: Stop fleet by label
  (name match across nodes, autocomplete, per-node breakdown,
  HTTP 429 on per-node concurrency) and Bulk label assign (per-node,
  replace semantics, clear on empty selection).
- Document the inline 'New label' form inside the stack right-click /
  three-dot Labels submenu, the Settings - Advanced - Labels masthead
  N/50 stat, the LABELS - NEW / EDIT modal kickers, and the
  LABELS - DELETE - IRREVERSIBLE confirmation copy verbatim.
- Document the Fleet Overview Tags multi-select filter (filters by
  stack labels aggregated across nodes), with cross-link to fleet-view.
- Capture every screenshot fresh from production signed in as admin:
  sidebar-grouping, context-menu-labels, inline-create-form,
  settings-labels, create-label-dialog, fleet-tags-filter,
  fleet-actions. Drop the now-stale sidebar-with-labels,
  sidebar-filtered, and bulk-actions-menu captures.

* docs(dashboard): refresh page for v1 layout (status masthead, gauges, fleet heartbeat, restart map)

Aligns docs/features/dashboard.mdx with the redesigned Home tab. Replaces the obsolete
Recent Activity feed coverage with the actual DashboardActivityCard split (Fleet Heartbeat
when remote nodes are registered, Stack Restarts (7d) otherwise) and recaptures every
screenshot from the v0.74.x production node.

* docs(global-search): refresh page for v1 palette

- Note tier and role gating on the Pages list (Auto-Update, Console,
  Schedules, Audit) so the prose matches what the top bar exposes.
- Document the ACTIVE chip on the currently active node row.
- Document the 50-result cap counter and the Searching... loading state.
- Mention the ~250 ms debounce and clarify that filename matching
  includes the file extension.
- Replace stack screenshot with a redesigned capture and add empty-state
  Pages and Nodes captures showing the ACTIVE chip.

* docs(global-observability): refresh page for v1 layout (masthead, signal rail, filter strip, paused-resume chip)

Full rewrite against the current Logs tab and the v1 docs refresh template
(hero Frame, sectioned anatomy, AccordionGroup troubleshooting, refresh-cadence table).

Replaces the single overview screenshot with seven captures under
docs/images/global-observability/ (overview, masthead, signal-rail,
filter-strip, feed-bands, paused-resume-chip, error-only-filter), all from
the v0.75.x production node signed in as admin with PII scrubbed
(profile chip patched to AD, in-feed LAN IPs and third-party hostnames
substituted via DOM injection while the stream was paused).

Aligns prose with the actual UI labels and code:

- Masthead kicker reads LIVE LOGS · NODE · <NAME> with LOCAL for the
  local node; state word toggles Streaming / Idle / Offline; SESSION
  uses uppercase letter suffixes (1H 43M / 0M 12S) per formatUptime.
- Signal rail tile counts are scoped to the 2000-entry buffer and reset
  with Clear; CONTAINERS is buffer-bound, not a monotonic accumulator.
- Filter strip controls quoted verbatim (Stacks · All / Stacks · n,
  segmented controls All / Out / Err and All / Info / Warn / Error).
- Feed row anatomy: severity dot, timestamp, brand-cyan container name
  with stack/container tooltip, message tinted by source. Row tint
  follows detected level, which is regex-based, so an STDOUT line
  containing ERROR: still classifies as ERROR.
- Day bands: NOW, Nm AGO, Nh AGO, calendar date.
- Empty states: two-tier kicker over caption (Awaiting events / No matches).
- Pause keeps the SSE buffer filling up to the 2000-entry cap; resume pill
  reads <n> NEW · RESUME and counts the queue, not total arrivals during
  the pause.
- Download filename and row format quoted: sencho-logs-<ISO8601>.txt and
  [<ISO>] [<stack>/<container>] <LEVEL>: <message>.

Documents behavior the previous page never covered:

- Active-node scoping; node switch resets the stream and the buffer.
- SSE primary transport with 30-second server heartbeat and a 5-second
  polling fallback against /api/logs/global (server-capped at 500 lines
  per snapshot).
- Initial replay of the last 500 lines per container when the SSE
  connection opens, so the feed has context immediately.
- Display limits (2000 client buffer, 300 rendered rows, Showing last
  300 of N overflow notice).
- Refresh cadence table covering UI tick, flush cadence, polling
  cadence, SSE heartbeat, sparkline window, and the Idle threshold.

Adds a seven-accordion troubleshooting block (Offline state, gray Idle
dot, ERROR-without-tint, growing Resume pill, Clear-cutoff lag,
node-switch buffer drop, fleet-wide aggregation expectations).

Tightens the closing Note so it makes clear that Notification Log
Retention does not govern this live container stream.

* docs(alerts-notifications): refresh page for v1 and absorb notification-routing

Full v1 template rewrite of /features/alerts-notifications. Bundles in
the entire Notification Routing page so a reader sees channels, routing,
per-stack rules, and retention in one place; deletes the standalone
notification-routing.mdx and points all five cross-link sites at the new
in-page anchor.

* docs(alerts-notifications): drop "What's not in scope" section

The page should describe what Sencho does, not enumerate what it does
not ship. Users find missing integrations through the Webhook section
and the routing matcher reference; the explicit disclaimer added noise
without adding guidance.

* docs(audit-log): refresh page for v1 layout, expanded action list, troubleshooting accordion

- Clarify that the search/method/date filter strip lives in Table view only.
  Stream view always shows the unfiltered chronological feed.
- Fold the total-entries readout into the card subtitle wording where it
  actually renders, instead of describing it as a separate header element.
- Sharpen the Peak hour off-hours window to the literal 08:00 to 17:59
  working window the tile keys off, plus the 5% / 20% failure-rate tints.
- Note that the Actors tile names a sample actor alongside the new-IP count.
- Expand the example actions list to cover surfaces that have shipped since
  the last edit: per-service stack lifecycle, node cordon/uncordon, fleet
  replica role changes, Sencho Cloud Backup operations, Fleet Secrets, and
  blueprint federation pin updates.
- Correct the Settings path: Settings · Developer · Data retention card,
  Audit log input, Save settings button.
- Add a Troubleshooting AccordionGroup matching the rest of the v1-refresh
  pages: missing tab, filter scope, anomaly thresholds, export cap, and
  retention pruning.
- Replace all four screenshots with fresh captures of the current UI.

* docs(multi-node): refresh page for v1 layout, pilot agent mode, refreshed table columns

Rewrites docs/features/multi-node.mdx against the current product. The previous page predated the v1 Settings hub redesign and the Pilot Agent enrollment model, so it documented only the Distributed API Proxy add-node flow and missed the new Mode, Endpoint, and Labels columns on the Nodes table.

Restructures the page into 13 sections: intro, How it works, the local node, Choose a remote mode (decision table comparing Pilot Agent vs Distributed API Proxy), Add a remote node: Pilot Agent (three steps plus re-enrollment), Add a remote node: Distributed API Proxy (three steps), Switching between nodes, the Nodes table (full column reference), What Settings apply per node (verified against settings/registry.ts), License enforcement across nodes, Editing and deleting nodes, Security (token security, transport encryption, why no application-layer TLS), and Troubleshooting (AccordionGroup matching the v1 template used on audit-log, atomic-deployments, and deploy-progress pages).

Refreshes seven screenshots against the production node signed in as admin, scrubbing IPs and usernames before capture: full Nodes panel overview, Generate Node Token card with a placeholder token, Add node modal in Pilot Agent mode, Add node modal in Distributed API Proxy mode (with the inline plain-HTTP warning visible), Edit modal showing the Regenerate enrollment token card for a pilot agent, Pilot enrollment modal with the docker run command, refreshed node switcher popover, and a close-up of the table columns. Drops the obsolete add-node-form.png, http-warning.png, and per-node-scheduling/ folder.

* docs(fleet-view): refresh page for v1 layout, expanded tabs, cordon, sheet-based updates

- Aligns the Overview, Status, and Node Updates content with today's UI:
  the masthead's `The fleet` headline plus CPU / MEM / CONTAINERS stat tiles,
  the eight-tab strip (Overview, Snapshots, Status, Deployments, Traffic,
  Federation, Fleet Actions, Secrets) with per-tier visibility, and the
  Check Updates surface that is now a system sheet rather than a modal.
- Documents the toolbar (search, sort, filter popover with Status / Type /
  Severity / Tags sections) and the Grid / Topology segmented control
  including the topology graph's status pill (Online / Critical / Offline),
  connector colouring, ReactFlow controls and minimap.
- Documents the per-card surfaces that were missing from the prior page:
  Cordoned badge with cross-reference to Fleet Federation, fleet stack
  label dots in the drill-down, container drill-down rows (state dot,
  badge, image, status, open-in-editor hover button), and the Admiral
  three-dot Node actions menu for cordon / uncordon.
- Documents the Node Updates sheet anatomy (Recheck and Update all (n)
  header actions, four summary cards, node table columns, Update flow,
  reconnecting overlay timing, admin enforcement) and the GitHub Releases
  with Docker Hub fallback resolution path with its 30-minute cache.
- Replaces every stale screenshot with a fresh capture (overview,
  topology, drill-down, status tab, node updates sheet) and removes the
  obsolete files plus the empty docs/images/fleet/ folder.
- Reformats troubleshooting as an AccordionGroup matching dashboard,
  multi-node, and audit-log refreshes.

* docs(fleet-backups): refresh page for redesigned fleet and settings UI

Replace all six screenshots with current production captures. Update
content to reflect the new fleet header card, eight-tab layout, full-
page Cloud Backup settings with header stats, and corrected navigation
paths. Add cloud backup rows to the access control table.

* docs(fleet-backups): convert troubleshooting to AccordionGroup pattern

Match the foldable-accordion pattern used across the v1 docs refresh
batch. Merges the standalone Cloud Backup troubleshooting subsection
into a single Troubleshooting section at the bottom of the page with
seven accordions covering skipped nodes, two restore failure modes,
three cloud-upload failure modes, and a diagnostic logging entry.

* docs(remote-updates): refresh page for v1 sheet, accordion troubleshooting, factual fixes

Rewrites the page against the v1 docs refresh template (Note tier gate,
sectioned mechanism deep-dive, Frame screenshots with detailed alt text,
inline AccordionGroup troubleshooting), bringing it in line with the
recently-refreshed fleet-view, fleet-backups, dashboard, and audit-log
pages.

The page is repositioned as the mechanism deep-dive (prerequisites, what
runs on a node during an update, completion and failure detection,
recovery actions). The full UI tour for the Node updates sheet remains in
fleet-view so the two pages stop overlapping; remote-updates now links
into fleet-view#node-updates instead of restating the table anatomy.

Captures three screenshots from the production node, signed in as admin:
fleet-node-updates.png shows the Node updates sheet with eight nodes and
seven remote updates available; local-update-confirm.png shows the
LOCAL · UPDATE alert dialog with the Cancel and Update & restart buttons;
node-card-update-available.png shows the Opsix card with the Update
available pill and the Update to v0.76.7 outline button.

Corrects several factual claims that no longer matched the current code:

- The remote early-fail threshold is about 3 minutes, matching
  EARLY_FAIL_MS in backend/src/routes/fleet.ts, not 90 seconds.
- The Recheck button sits in the sheet header, not the footer.
- The component is a SystemSheet, so the page now consistently calls it
  the Node updates sheet instead of a dialog, with lowercase "Node
  updates" and lowercase "Update all (n)" matching the live UI.
- Reconnecting overlay polls /api/health every 3 seconds, not "every few
  seconds".
- The local Failed badge surfaces as soon as the helper writes its error
  file, by the 3-minute mark at the latest.

Documents the LocalUpdateConfirmDialog kicker, title, body, and CTA
verbatim, the Triggering... loading state on the Update buttons, the
four completion signals the gateway accepts (version change, process
startedAt change, offline-then-online transition, version at or above
the comparison target after 15 seconds), and the 60-second auto-clear
of the Updated badge.

Drops references to two screenshots that never existed
(fleet-node-updating.png, fleet-node-failed.png); the in-flight and
failed states are described in prose instead, the same way fleet-view
handles them.

* docs(scheduled-operations): refresh page for v1 timeline, fleet-wide update action, sheet-based run history

Rewrites the Scheduled Operations page against the v1 template
(Note tier gate, sectioned anatomy, Frame screenshots, AccordionGroup
troubleshooting) applied to sibling pages in this batch. Captures
seven fresh screenshots against the production node signed in as
admin (timeline, all-tasks, action-picker, create-restart,
create-prune, create-scan, run-history) and removes every legacy
PNG.

Documents the new "Auto-update All Stacks" action that was absent
from the page, extends the Skipper allow-list to all four Skipper
actions (Auto-update Stack, Auto-update All Stacks, Fleet Snapshot,
Vulnerability Scan) and clarifies that the action picker hides
operations the active tier cannot run.

Corrects several factual claims that no longer matched the code:

- Scheduled scan completion is `info`/`scan_finding` on a clean run
  and `warning`/`scan_finding` when findings are present (not
  `info`/`system` as previously stated). Cross-link now points at
  `alerts-notifications#vulnerability-scanning`.
- Lifecycle actions (auto_backup, auto_stop, auto_down, auto_start)
  execute against the local Sencho instance only; only Auto-update
  Stack / All Stacks have a remote-proxy code path. The page
  reinstates the guidance to schedule remote lifecycle operations
  from that node's own UI.
- Run history lives in a right-side sheet with a "Schedules ›
  <task> › Runs" breadcrumb and a Download CSV secondary action.
- Timeline masthead is described in terms of the v1 visual
  (`NEXT 24 HOURS` kicker, italic display heading, monospace date
  range, right-anchored Next pill with countdown, glowing cyan now
  rail, six-tick bottom axis).

* docs(rbac): refresh RBAC & user management page against v1 template

Bring /features/rbac onto the v1 docs refresh template (Note tier gate,
sectioned anatomy, Frame screenshots, AccordionGroup troubleshooting).
Recapture five screenshots from the production node signed in as admin
and remove the three stale captures under docs/images/rbac/.

Corrections vs. the prior page:
- Deployer no longer claims node:read in the permission matrix; the
  backend grants only stack:read and stack:deploy.
- Add the system:registries row (container registry management).
- Document the form as inline below the Add user button (not a modal).
- Note the (you) marker on the signed-in admin's row and the disabled
  delete icon on that row.

Additions:
- Settings nav location and hub-only visibility.
- 2FA reset row action with verbatim modal kicker, title, and body.
- Five-failure / 15-minute MFA lockout behavior and admin reset recovery.
- Token-version session-security table covering deletion, role change,
  password change, and admin 2FA reset.
- SSO password-fields-hidden line quoted verbatim and the per-provider
  Require MFA toggle.
- Audit-log emissions list for every user-management mutation.
- API tokens cross-link explaining the user-vs-machine boundary.
- Scoped permissions section retightened: scoped role picker is
  Deployer / Node Admin / Admin only; resource type is Stack or Node.

AccordionGroup with eight troubleshooting entries covering missing nav,
greyed role options, seat-limit errors, unexpected sign-outs, scoped
deployer mismatches, missing shield icon, re-locking MFA accounts, and
SSO role drift at provisioning.

* docs(2fa): refresh two-factor authentication and admin guide against v1 template

Bring /features/two-factor-authentication and /operations/two-factor-admin
onto the v1 docs refresh template (Note tier gate, sectioned anatomy, Frame
screenshots with descriptive alt text, AccordionGroup troubleshooting,
verbatim modal copy with kicker callouts). Recapture every screenshot under
docs/images/two-factor-auth/ from a fresh session and add six new captures
for surfaces the prior page did not document.

Corrections vs the prior pages:

- Panel rename: Settings -> Account & Security is now Settings -> Account,
  under the Identity group of the settings sidebar. Replaced every
  occurrence on both pages.
- Enrol dialog titles match the current modal: Pair your authenticator,
  Confirm the pairing, Save your recovery codes (was: Set up 2FA, Confirm,
  Save your backup codes). Step rail 01 PAIR / 02 CONFIRM / 03 ARCHIVE
  documented.
- Manual-entry affordance is the always-visible Secret manual entry row
  with a copy icon, not the toggleable Can't scan Show secret key link.
- Confirm step auto-submits on the sixth digit; no submit button. Verified
  in MfaChallenge.tsx and MfaEnrollDialog.tsx and called out explicitly.
- Authenticator-app list trimmed to match in-app copy (1Password, Bitwarden,
  Google Authenticator, or any TOTP app). Authy and Microsoft Authenticator
  dropped because the dialog does not mention them.
- Disable dialog: kicker SECURITY MFA DISABLE, title Turn off two-factor,
  destructive header, Disable button. Replaces the prior Disable 2FA
  paragraph that did not describe the dialog chrome.
- Regenerate dialog: two-step flow with kicker SECURITY BACKUP CODES, Confirm
  identity then New recovery codes, with the verbatim PREVIOUS CODES HAVE
  BEEN INVALIDATED warn rail on the show step. Documented that the dialog
  only accepts a TOTP, not a backup code.
- Per-user SSO toggle label corrected: Require 2FA on SSO sign-in (was:
  Require 2FA even when signing in via SSO). Added the per-provider vs
  per-user distinction on both pages (admins can also enable Require MFA
  on the SSO provider config, which is independent of the per-user toggle).
- Admin reset modal: verbatim USERS RESET 2FA kicker, Reset 2FA for
  <username> title, full-body copy reproduced. Documented that the reset
  bumps the target's token version and invalidates active sessions.

Additions:

- Sign-in throttle: five failed verifications lock the account for 15
  minutes, server returns 423 with Retry-After, UI shows the Retry in MM:SS
  countdown plus Rate limited label. Lockout recovery section explains
  that the counter only clears on a successful sign-in, so retries after
  the window expires re-lock immediately.
- Account panel anatomy section enumerates the three rows (Authenticator
  app, Backup codes, Require 2FA on SSO sign-in) plus the destructive
  Disable 2FA link, and the masthead 2FA on / BACKUP N left chips.
- Recovery codes section now covers all three count states (3 plus, 1 to 2,
  0) with verbatim helper text, tone, and the standalone No backup codes
  left callout that renders at zero. New screenshots for the 2-remaining
  and 0-remaining states.
- Cross-references to the admin operations page (CLI fallback, token version
  rotation, what a reset changes in the DB), the SSO page, and the RBAC
  page (per-provider Require MFA toggle, SSO auto-provisioning).

Troubleshooting on the feature page rewritten as an AccordionGroup with
nine entries: clock drift, wrong account selected, QR will not scan, lost
phone with no codes, lost codes with authenticator, ran out of codes,
unexpected SSO prompt (with both toggle causes), repeated lockout after
the window expires, missing shield icon on Users panel.

The admin operations page also gains the SSO + 2FA two-toggles table so
administrators can answer the per-user vs per-provider question without
context-switching between pages.

Six new images added; six existing images replaced. Total 14 captures.

* docs(rbac,host-console): drop enforcement-boundary detail from tier-gate notes

Operator-facing docs should state tier or role requirements once, in plain
customer-facing language, and leave the enforcement chain to the source.
Two surfaces on the v1-refreshed pages over-specified the gate:

- `features/rbac.mdx::Scoped permissions`: the Note enumerated both the UI
  hide on Skipper and the `/api/users/:id/roles` write rejection. The first
  half ("Scoped permissions require Admiral.") is the operator-relevant
  fact; the rest reads as a fence specification, which is awkward for an
  open-core product where the gate is readable in source anyway. Trimmed
  to just the tier claim.

- `features/host-console.mdx::Availability`: the paragraph already says
  who can use the console and that the Console tab is hidden on Community
  or Skipper. The trailing "Attempting to access the console endpoint
  directly without the correct license or role is rejected" is the same
  bypass-prevention coda. Dropped.

No functional behavior change; the gates themselves are untouched.

* docs(sso): refresh SSO & LDAP authentication page against v1 template

Rewrites docs/features/sso.mdx against the v1 docs refresh template (intro
+ tier callout, sectioned Configuration anatomy, Frame screenshots,
AccordionGroup troubleshooting), bringing it in line with the previously
refreshed two-factor-authentication and rbac pages on this branch.

Recaptures all four screenshots from the production node signed in as
admin: sso-settings (overview with the five collapsible provider cards),
sso-settings-ldap (LDAP form expanded), sso-settings-oidc (Google form
expanded), sso-settings-custom-oidc (Custom OIDC form expanded with all
eleven fields).

Refreshes the Settings UI section to match the redesigned panel: each
provider is a collapsible card with an Active badge on the header, an
enable / disable toggle pill, and a footer with Save, Test Connection
(green check or red X next to the button), and Remove (only after a
config has been saved). Documents the static callback-URL helper that
sits below all five cards.

Clarifies that the per-OIDC claim mapping environment variables
(SSO_OIDC_*_ID_CLAIM, *_USERNAME_CLAIM, *_EMAIL_CLAIM) are accepted for
Google, GitHub, and Okta, not just Custom OIDC. The Settings UI hides
those fields on the presets because the defaults match.

Converts the troubleshooting section to an AccordionGroup with five
entries (Test Connection discovery failure, issuer validation error,
wrong username or missing email after sign-in, invalid redirect URI,
SSO buttons missing on the login page). Cross-links the operations
troubleshooting page for setup-time errors.

Tightens the LDAP TLS env var note to spell out the literal string
'false' requirement. Syncs the Combining SSO with 2FA section to use
the live toggle label 'Require 2FA on SSO sign-in'.

* docs(sso): drop the Community-tier Custom OIDC workaround tip

The Tip walked through how a Community-tier operator could integrate
Google, GitHub, or Okta by pointing Custom OIDC at the provider's
discovery URL, bypassing the Skipper preset gate. Operator docs should
state the tier rule once and stop; they should not describe how to
circumvent it.

The tier matrix above the removed block already names which providers
are paid; the Custom OIDC row already lists "any spec-compliant OIDC
provider" as its scope. That is enough.

* docs(vulnerability-scanning): refresh page for v1 UI and corrected tier mapping

The page was last revised before the v1 visual redesign and before the
tier-mapping changes shipped in v0.81.2 (open Community access to
secret scanning, compose misconfig scanning, scan history, and scan
comparison). This refresh:

- Rewrites the tier matrix to match the shipped Community / Skipper /
  Admiral split. Secret detection, compose misconfig scanning, scan
  history, scan comparison, and misconfig acknowledgements are now
  correctly marked as Community. Scheduled fleet scans, scan policies
  with block_on_deploy, SBOM, SARIF, and Trivy auto-update stay paid.
- Drops two stale Notes that said secret detection and compose
  misconfig scanning required Skipper or Admiral. The page now states
  each tier requirement once, in plain language.
- Refreshes all six existing screenshots from the production node:
  resources-badges, scan-details-sheet, scan-history-sheet,
  scan-compare-sheet, security-settings, app-store-toggle.
- Adds a new scan-config-button screenshot showing the stack-page
  overflow menu where Scan config now lives.
- Describes the scan drawer header accurately: Re-scan + Compare + CSV
  + SARIF as top-level buttons, with SBOM as a separate button below
  the summary.
- Updates the compose misconfig flow to point at the stack overflow
  menu (not the Deploy controls).
- Converts the troubleshooting section to a single AccordionGroup per
  the v1 template, and audits each entry for legacy phrasing and the
  removed tier claims.
- Adds a TRIVY_BIN reference to the How it works section so operators
  know about the host-binary override.

* docs(cve-suppressions): refresh page for v1 UI and corrected suppression specifics

- Recapture all three screenshots from the production node signed in
  as admin under `docs/images/cve-suppressions/` (`settings-panel`,
  `create-dialog`, `suppressed-row`). The previous file referenced
  three image paths that did not exist in the repo.
- Align prose with the actual UI labels:
  - Dialog kicker `SUPPRESSIONS . NEW`, title `New suppression`.
  - Field labels match the form: `CVE or advisory ID`, `Package
    (optional)`, `Image pattern (optional)`, `Reason`, `Expires in
    (days, optional)`.
  - Remove confirmation reads `Remove suppression` with kicker
    `SUPPRESSIONS . REMOVE . IRREVERSIBLE`.
- Factual corrections:
  - Fleet sync truncation cap is 5,000 rows (not 10,000).
  - State the admin-role requirement once in the lead Note.
  - Drop references to a `Fleet . Sync status` page and a `Reanchor`
    button; neither exists in the UI. The reanchor flow is an admin
    API call and is documented in /features/fleet-sync.
  - Sharpen the specificity scoring section (package + image scores
    3, package only 2, image only 1, neither 0) so the order matches
    the read-time filter logic.
  - Note that the image-pattern glob is case-sensitive.
- New coverage:
  - Suppressing directly from a scan result, including which fields
    are read-only in that inline flow and when to fall back to
    Settings to broaden scope.
  - The `replicated` and `expired` row badges in the panel.
  - Hovering the package column on a suppressed row to surface the
    Reason.
  - Two distinct read-only modes: viewing a remote node from the hub
    (panel hidden, banner shown) versus signing into a replica
    instance (panel visible, read-only).
  - SARIF export carries suppressions through as
    `kind: external, status: accepted`, cross-linked to the
    Vulnerability Scanning page.
- Convert troubleshooting to AccordionGroup with six entries; update
  the truncation entry to reflect the 5,000-row cap.

* docs(private-registries): refresh page for v1 UI and fleet-wide credential model

Rewrites the page against the v1 docs template (Note tier gate, opening Frame,
sectioned anatomy, AccordionGroup troubleshooting) and replaces every
screenshot with a fresh capture taken against the current product.

Corrects several factual claims that no longer matched the current code:

- Registries are stored once on the control instance and applied fleet-wide,
  not configured per node. The old Multi-node behavior section and the
  matching troubleshooting entry described a per-node model that the product
  no longer has.
- The Registries section is hidden on remote nodes (global scope) and on
  Sencho versions that do not surface the feature. New troubleshooting
  entries explain both visibility states.
- The feature is admin-only on Admiral. Non-admin operators do not see the
  section even on Admiral; previous copy implied any Admiral license user
  could manage credentials.
- Registry endpoints are not reachable from API tokens; only an admin
  browser session can manage credentials. The Security section now states
  this without naming internal route paths.

Documents UI behavior the previous page omitted: the inline form (not modal),
the four type-specific form variants, the Docker Hub read-only URL field, the
destructive delete confirmation with its stack-pull warning, the masthead
REGISTRIES count, and the empty-state callout copy.

Screenshots replaced:
- registries-overview.png: section with one configured GHCR card and the
  masthead stat at one.
- registries-empty.png: empty state with the Add registry button and callout.
- registries-add-form.png: inline form with the Docker Hub default and the
  read-only URL field.
- registries-ecr-form.png: form switched to ECR, showing the AWS Region
  field and the relabelled AWS credential inputs.
- registries-card-detail.png: card close-up with the three action icons and
  the metadata row.
- registries-delete-confirm.png: destructive ConfirmModal with the kicker,
  title, and stack-pull warning body.
- registries-with-entry.png removed (superseded by registries-overview.png
  and registries-card-detail.png).

* docs(auto-update): refresh readiness page for v1 redesign

Bring the Auto-Update Readiness doc in line with the shipped UI:

- Replace the hero screenshot with a fresh capture of the redesigned
  board (italic-display hero, brand-cyan accent, per-node groups with
  local/remote pills, dashed-border changelog separator).
- Rewrite the card-anatomy list. Drop the rollback-target bullet (the
  field exists in the backend payload but is not rendered). Add the
  "Rebuild available" inline label and the primary-image / multi-service
  count line.
- Rewrite the risk-tags table as a risk-badges table using the actual
  badge labels and colors emitted by the UI (Safe / Review / Blocked
  with the corresponding icons; Digest rebuild for non-semver tags).
- Add an Empty state section and document the per-node group header.
- Tighten the hero subtitle paragraph to match the actual UI string
  (only major-bump count is surfaced separately; preview failures are
  not).
- Fix workflow step 4: major-bump apply path is the stack lifecycle
  Update action, not the Schedules editor (a scheduled task hits the
  same block).
- Add the 2-minute manual-refresh cooldown to the Recheck workflow.
- Remove the broken cross-link to the non-existent
  /features/image-update-detection page and inline the 6-hour cadence
  fact from ImageUpdateService.INTERVAL_MS.
- Convert troubleshooting to AccordionGroup format per the troubleshoot
  ing convention used on /features/deploy-progress.
- Sync the Auto-Update entry in /features/overview.mdx to the new
  badge labels and the corrected hero-counter description.

* docs(auto-update): fix Auto-Update entry point in Workflow step 1

Workflow step 1 said "Open the Auto-Update view from the sidebar." The
Auto-Update view is opened from the top nav strip (alongside Home,
Fleet, Resources, App Store, Logs, Schedules, Console, Audit). The
sidebar carries the stack list and the per-stack right-click / kebab
context menu that toggles auto-updates on or off; it does not house
the Auto-Update top-level view.

* docs(auto-update): trim enforcement detail from per-stack control note

State the tier requirement once and stop, per Directive 27. The
"The toggle does not appear on Community" sentence enumerates the
enforcement effect of the gate, which the source already reflects;
operator docs do not need to narrate it.

* docs(auto-heal): refresh page for v1 UI and policy hardening

Rewrite Auto-Heal Policies docs against the current Stack Monitor
sheet: corrects the Max restarts / hr field label, documents the
per-policy enable toggle, the consecutive-failures pill, the full
Recent activity action set (including Docker unavailable), the 30s
evaluation cadence, multi-node behavior, notification dispatches,
and the dashboard Configuration status counter.

Replaces the broken /images/auto-heal-policies/policy-sheet.png
reference with three fresh screenshots captured against a live
node: the sheet on the Auto-heal tab, a single policy row, and
the expanded Recent activity panel.

* docs(webhooks): refresh page for v1 UI, correct tier and add Git source sync

- Fix tier note: gate is Skipper or Admiral, management is admin-only.
- Update Settings path to Settings -> Alerts -> Webhooks; document the
  read-only Node field and the green secret-reveal callout.
- Add the missing Git source sync action and the git-pull override value.
- Refresh the configured-webhooks card description: action/stack/node
  badges, On/Off toggle, copy URL, and the Recent executions disclosure.
- Tighten the trigger section with a constant-time signature check note
  and a status/body/meaning response table.
- Add an Accordion troubleshooting block covering common signature
  failures, the 404 case, no-op actions on 202, and git-pull prereqs.
- Re-capture all three screenshots from the v1 UI.

* docs(webhooks): wrap troubleshooting accordions in AccordionGroup

* docs(sidebar): refresh page for v1 redesign with filter chips, bulk mode, row anatomy, and troubleshooting

Rewrites the Stack Sidebar page against the live v1 sidebar and the v1
docs refresh template (Frame screenshots, Note tier callouts,
AccordionGroup troubleshooting). Recaptures all four existing
screenshots and adds three new captures: filter chips, row anatomy,
and bulk mode.

Adds coverage for features the previous page omitted entirely: the
ALL / UP / DOWN / UPDATES filter chips with their counts cap and
collapse toggle; bulk mode (B key, sticky toolbar with Start / Stop /
Restart, and Update gated on Skipper or Admiral); stack-row anatomy
(status pill, label dots with +N overflow, image-update dot vs Git
source icon priority, hover kebab); the Auto-update toggle, Schedule
task, and Open App entries in the context menu; the B shortcut for
bulk mode.

Corrects three claims that no longer matched the code or UI:
Auto-Heal is gated on Skipper or Admiral, not universal; the global
Ctrl+K opens the command palette, not the sidebar search box; the
activity footer kicker reads LIVE / IDLE with the verbatim copy from
SidebarActivityTicker. Documents the in-menu ↗ and L › glyphs as
visual hints rather than global keybindings to match
useStackKeyboardShortcuts.ts.

* docs(sidebar): trim enforcement-effect sentence from context-menu tier note

State the Skipper / Admiral requirement once and stop, per Directive 27.
The "They do not appear in the menu on Community" clause described the
enforcement effect alongside the gate, which the directive bans in
operator-facing docs.

* docs(host-console): refresh page for v1 UI and clarify shell metadata

Rewrite the Host Console page to match the current Cockpit layout
(masthead + terminal well + chip strip), replace the legacy PowerShell
screenshot with a fresh bash capture, and document the masthead tone
states, kicker, metadata pills, and session/heartbeat behavior. Trim
the security section to state the tier and role rule once.

* docs(licensing): refresh page for v1 UI, corrected pricing, and trial flow

Rewrites the Licensing & Billing page to match the redesigned v1
Settings layout. The previous draft still described the legacy
Settings Hub: in-app "Upgrade your plan" Skipper/Admiral cards,
"Start monthly trial" / "Start annual trial" buttons, the
"Have a license key?" field, "Manage Subscription" with a capital S,
"Deactivate License" as the button label, and the license-active.png
asset rendering the literal "Sencho Pro" string in the card title.
None of that exists in the current product.

- Refreshes the Plans table to the live pricing on sencho.io/pricing
  and adds an Enterprise mention with the floor price ($3,500/year).
  Skipper now $11.99 annual / $14.99 monthly / $449 lifetime, Admiral
  now $69.99 annual / $89.99 monthly / $2,499 lifetime.
- Rebuilds the Feature breakdown from a code-level audit of every
  requirePaid, requireAdmiral, requireScheduledTaskTier, and
  requireTierForSsoProvider call site in backend/src/routes, not
  from the marketing page. Notable code-grounded items: CVE
  suppressions on Community (no requirePaid guard), manual fleet
  snapshots on Community (scheduled snapshots on Skipper),
  Sencho Mesh under Admiral (entire mesh.ts router is requireAdmiral),
  and scheduled-task tiering names update/scan/snapshot as the
  Skipper subset with everything else under Admiral.
- Rewrites the Free trial flow end to end. The previous steps told
  operators to click in-app "Start monthly trial" or "Start annual
  trial" buttons; no such buttons exist. The new flow starts on
  sencho.io/pricing, switches to the Annual or Monthly tab, clicks
  "Start 14-day trial" on the Admiral card, completes the Lemon
  Squeezy checkout (card-required, no charge before day 14), and
  pastes the issued key into Settings -> License -> License key.
- Adds a new "The Plan section" anatomy block describing the masthead
  SCOPE / PLAN / DURATION (or RENEWS, TRIAL, STATUS) stat pills and
  the Plan card fields (Customer, Product, masked License key, status
  helper).
- Adds a new "License states" reference table covering
  Community / Trial / Active subscription / Active lifetime /
  Expired / Disabled, what each surface renders, and which of the
  Plan / Activate / Pricing sections is visible in each state.
- Corrects every UI label that drifted: section heading is Activate,
  field label is License key (not "Have a license key?"), buttons are
  Manage subscription (lowercase s) and Deactivate (not "Deactivate
  License"), and the action-row hint reads "Lemon Squeezy manages
  billing".
- Documents the redesigned profile dropdown: identity header with
  initials chip, role badge, and tier badge, then Settings,
  conditional Billing, Documentation, Feedback, an Appearance
  segmented control, and Log Out. Billing only appears when the
  license is an active non-lifetime subscription.
- Replaces all four screenshots under docs/images/licensing/:
  license-admiral-active.png (production Admiral lifetime view),
  profile-menu.png (redesigned popover), and two new captures for
  the Community-tier surfaces (license-activate-section.png,
  license-community.png). Removes the stale license-active.png
  (legacy "Sencho Pro" card) and profile-billing.png (legacy
  dropdown).

* docs(settings-reference): refresh page for v1 UI with new sections and masthead

Rewrites docs/reference/settings.mdx against the current Settings Hub so a reader
encounters an accurate map of every section. Adds the previously missing **Cloud
Backup** and **Security** sections, restructures **System Limits** into Host
thresholds and Docker hygiene subsections (GiB units, "Global crash capture"
toggle), fixes the Account password minimum to 8 chars and documents the
two-factor subsection, refreshes License/Routing/Webhooks/App Store with the
field labels actually rendered today, and documents the masthead pills
(SCOPE/NODE, EDITED, plus the per-section stats like 2FA, PLAN, CHANNELS, ROUTES,
WEBHOOKS, LABELS, TRIVY, POLICIES, PROVIDER, USED, SNAPSHOTS, DEV MODE).

Replaces five existing screenshots that predated the v1 redesign and adds five
new captures: Account with the 2FA card, License panel, System Limits with both
subsections, Security with the Trivy installer, and Cloud Backup with Sencho
Cloud Backup provisioned. All shots taken against the production node.

* docs(licensing): drop billing-provider name from operator-facing copy

The previous draft named the third-party billing provider in nine
places (checkout, receipt email, error toast verbatim, Customer /
Product field descriptions, the action-row hint, the billing portal,
and the validation API). Operator docs don't need to advertise which
vendor sits behind the checkout, billing portal, and validation
calls. Rewrite each instance to describe what the operator sees and
does without naming the upstream service.

* docs(node-compatibility): refresh page for v1 UI with lock card visuals and current capability list

- Replaces the legacy "dim + blur + pill overlay" description with the
  current CapabilityGate behavior: a centered lock card with an Unplug
  icon, title "<feature> is not available on this node", and a body line
  that names the node and its running version.
- Corrects the tier-interaction section: on the wrong tier the entry
  point is hidden entirely, so the lock card only appears for users who
  already cleared the license gate.
- Documents the public /api/meta endpoint, the 5-minute success cache,
  the 30-second failure cache, and the lazy-fetch behavior visible in
  the switcher (the version pill appears once a node has been visited).
- Refreshes the capability table against the current CapabilityRegistry
  list, adding container-exec and vulnerability-scanning, with a note
  that vulnerability-scanning is only advertised when Trivy is installed.
- Adds three production screenshots captured on the live fleet:
  switcher popover with mixed-version pills (one node on v0.76.9, rest
  on v0.81.11), a real lock card on an older pilot agent, and the
  Connection Details panel from Settings · Nodes.

* docs(security): refresh security architecture page for v1 UI

Add Fleet Secrets and Webhook signatures cards plus tier-matrix rows for
shipped-but-undocumented features. Rename SSO presets from "one-click" to
"preset providers" (presets still require OAuth-app provisioning on the
upstream IdP). Update settings paths to the v1 middle-dot convention:
Settings · Users, Settings · Account, Settings · Developer · Data retention.

Extend the encryption-at-rest list with registry credentials and Fleet
Secrets bundle payloads (both sealed with the same AES-256-GCM data key)
and clarify the password section with bcrypt cost factor 10.

Add a Webhook signature authentication subsection covering the per-webhook
HMAC-SHA256 secret, one-shot display, masked preview thereafter, and
constant-time comparison on inbound triggers.

Replace the API Tokens screenshot with a fresh capture against the v1
Settings · Identity · API Tokens panel.

* docs(security-advisories): retire reference page

The reference/security-advisories page does not survive the v1 docs
refresh:

- Misuses the term "Security Advisories", which industry-wide refers to
  published notices for confirmed product CVEs (ID, severity, affected
  versions, fix version, remediation). The retired page was a narrative
  changelog of internal hardening work between v0.19 and v0.25.2.
- The narrative is also frozen at v0.25.2 (April 2026) while current
  release is v0.81.11. Refreshing it would require backfilling ~56
  release entries' worth of hardening copy.
- The framing is uniformly "improved from prior behavior" (minimum 8
  characters up from 6, 1-year token expiry previously without expiry,
  CORS previously allowed all origins, users should upgrade promptly).
  Sencho has not shipped publicly; there are no users to address as
  upgraders.

All operationally relevant content already lives elsewhere: the
security architecture page covers the current posture, verifying-images
covers the supply-chain attestations, cve-suppressions covers operator
acknowledgment, vulnerability-scanning covers the in-app scanner, and
contact + the security architecture page both surface the disclosure
path. Published Sencho-product advisories, when any exist, will appear
on the GitHub Security tab, which is already linked from those pages.

Inbound-link audit returned a single hit on the nav entry itself; no
other doc, README, or operator artifact deep-links the slug.

* docs: rewrite Pilot Agent page with deep architecture and operations reference

Reframes docs/features/pilot-agent.mdx as the architecture-and-operations
companion to the operator walkthrough in Multi-Node Management. Adds a
mental model section, an explicit security and trust model, a full agent
env-var reference, an honest limitations list, and a 5-item FAQ. Verifies
every constant and label against the current backend source. Refreshes
four production screenshots (admin login, scrubbed) and resolves the
previously-broken /images/pilot-agent/enrollment-dialog.png reference.

Adjacent edits keep the cross-linking coherent:
- multi-node.mdx adds a one-line forward link to the rewritten page
- security.mdx adds a Pilot Agent tunnel credentials subsection

* docs(fleet-federation): deep rewrite with production screenshots

Rewrites the Fleet Federation page against the v1 docs refresh template
following the recent fleet-view, pilot-agent, and multi-node refreshes.
Doubles the page length (92 to 204 lines) while keeping the cut-line v1
MVP scope: operator-driven placement controls (cordon + pin) for
Blueprints, no expansion into mesh/sync/pilot territory.

What changed:

- Adds four production-captured screenshots under docs/images/fleet-federation/:
  the Federation tab with a cordoned node populated, the node-card kebab
  menu showing the Cordon node entry, the cordon confirmation dialog
  with a reason filled in, and a node card displaying the Cordoned pill.
- Expands the page to eleven sections: opening summary, philosophy
  (kept), key capabilities, prerequisites, step-by-step usage with
  embedded screenshots, behaviour and lifecycle table, security and
  audit, limitations and non-goals (expanded), practical workflows (new:
  OS patching, host-to-host migration, gateway pinning), troubleshooting
  (eight accordions, up from five), and a Where Federation fits
  cross-link table.
- Documents the exact production UI strings observed: the cordon
  dialog description, the uncordon confirmation copy, the reason field
  cap (256 chars), and the audit log action names (node.cordon,
  node.uncordon, blueprint.pin).
- Documents the audit visibility surface so operators know how to
  filter the Audit view for cordon and pin history.
- Adds eight cross-links to sibling pages (Fleet View, Multi-Node,
  Pilot Agent, Mesh, Fleet Actions, Fleet Sync, Blueprints, Licensing)
  with one-line scope contrasts so newcomers can place Federation in
  the broader fleet picture.
- Tightens lifecycle table to operator-relevant terms (no DB column
  names) and audit section to operator-facing wording (no middleware
  names), keeping the page operator-focused rather than
  implementation-focused.

Validation:

- Captured screenshots against the production node logged in as admin,
  using Playwright MCP. Cordoned and pinned actions reverted; audit log
  confirmed the matched cordon/uncordon pair.
- Verified every cross-link target exists in the v1-refresh worktree
  (/features/fleet-view, /features/multi-node, /features/pilot-agent,
  /features/sencho-mesh, /features/fleet-actions, /features/fleet-sync,
  /features/blueprint-model, /features/licensing).
- Compliance: no em dashes, no PII, no "previously"/"used to" framing,
  no fence-spec language, tier rule stated once in plain language.

* docs(fleet-federation): drop fence-spec phrasing in the open-core context

Sencho is open-core: anyone can clone the repo and read the tier gate.
Operator docs that name exactly where the UI gate sits ("hidden at the
Community and Skipper tiers", "lower-tier users do not see the toggle",
"only the Federation tab is gated") work as a dig-target for a
tech-savvy reader and undercut the open-core posture. Directive 27
already bans enforcement-chain spelling; the open-core threat model
makes the same phrasings risky even when they describe UI surfaces
rather than route guards.

Removes three instances of the pattern on this page:

- Top Note callout: drops "The tab is hidden at the Community and
  Skipper tiers." Keeps the one-line requirement: "Federation is an
  Admiral feature. Cordon and pin actions require an admin user role."
- Security and audit section: drops the sentence enumerating which UI
  affordances are hidden from which tiers. Keeps the customer-visible
  behavior (the Cordoned pill stays visible at every tier as a
  read-only signal).
- Troubleshooting "Federation tab is not visible" accordion: rewrites
  to lead with the requirement and the role check, drops the
  "Federation is hidden by design" and "only the toggle and the
  Federation tab are gated" phrasings.

Other claims on the page unchanged; rule is still stated once in plain
language at the top of the page.

* docs(fleet-sync): deep rewrite with production screenshots

Replace fleet-sync.mdx with a verified end-to-end reference. The previous
page named two replicated resources but the code syncs three, described a
sync-status panel and a fleet-vs-node scope picker that do not exist in
the shipped UI, and was missing prerequisites and several edge cases.

Highlights of the rewrite:

- Names all three replicated resources (scan policies, CVE suppressions,
  misconfig acknowledgements) and treats them uniformly.
- Drops the sync-status-panel and node-scope-picker UI claims; both move
  to the Limitations section as honest caveats.
- Adds prerequisites covering the paid-tier requirement on the control,
  admin-role requirement, proxy-mode remotes, and reachability.
- Expands lifecycle coverage: per-node serialised pushes, add-node
  backfill, monotonic pushedAt, per-resource watermarks, identity-drift
  notifications, the 5000-row truncation cap, stale-target warnings,
  audit-log entries on the replica.
- New "Where Fleet Sync fits" closing table cross-linking to Fleet View,
  Multi-Node Management, Pilot Agent, Vulnerability Scanning, CVE
  Suppressions, Fleet Federation, Fleet Actions, and Licensing.
- Two fresh production screenshots: control Security panel and the
  "Scanner is per-node" callout shown when proxying to a remote.

* docs(fleet-actions): deep rewrite with production screenshots

Three cards are documented end to end: Stop fleet by label, Bulk label
assign, and Prune Docker resources fleet-wide. Adds the execution-path
distinction (control-orchestrated fan-out vs single-node proxy), per-card
behaviour and partial-failure semantics, prerequisites, limitations,
practical workflows, an Accordion troubleshooting section, and a Where
Fleet Actions fits comparison table linking the surrounding Fleet view
features.

Corrects the prior page's tab-neighborhood claim, confirm-dialog wording,
autocomplete-vs-request scope, and missing batch ceiling. Replaces the
ten-day-old single screenshot with five fresh production captures under
docs/images/fleet-actions/.

* docs(fleet-secrets): deep rewrite with production screenshots

Full rewrite of /features/fleet-secrets matching the fleet-actions
structure. Replaces the sparse v1 page (no Frames, inline Q&A) with a
gold-standard layout: opening Frame, single Note for the tier gate,
'What it covers' table, mental model, prerequisites, create + edit +
versions + push (Target / Preview / Results) sections each with a
production Frame, Import from stack section, behaviour and lifecycle
table, audit-trail mapping with the six exact audit strings,
limitations and non-goals, practical workflows, AccordionGroup
troubleshooting, and a Where-it-fits cross-link table.

Adds six fresh production screenshots under
docs/images/fleet-secrets/ : overview, create, versions, target,
preview, and results.

Documents the Import-from-stack flow (depends on the bundle editor's
new Import action) and uses the post-rename 'Send' wording on the
bundle-row action (depends on the aria-label fix).

Corrects three factual drifts vs the code: env-key regex described as
'letter or underscore, then letters/digits/underscores; case-
sensitive' to match ^[A-Za-z_][A-Za-z0-9_]*$ ; documents only the
'ok' and 'failed' status pills (the 'skipped' enum value is unused);
replaces the bogus 'stack not found' troubleshooting entry with the
real 'env file not declared' cause.

Drops the fence-spec phrasing 'The tab is hidden on Community.' per
Directive 31; the tier requirement is now stated once in plain
language.

* docs(sencho-mesh): deep rewrite with mental model, lifecycle, security, screenshots

Replace the feature-reference page with a deep product + technical guide.

Adds:
- Opening hook framing audience and problem (cross-node service-to-service
  without a separate VPN or service-mesh sidecar).
- Mental model: three moving parts (sencho_mesh bridge, alias registry,
  cross-node transport) with direction-of-flow described in prose.
- Key capabilities, prerequisites, step-by-step usage with inline screenshots.
- Full lifecycle section covering opt-in, opt-out, sticky stack-stopped state,
  peer reconnect, and the proxy-mode bridge with its real default (persistent,
  env-override for idle).
- Security and trust boundaries split into authentication, inbound exposure,
  encryption, audit, and app-layer caveats.
- Limitations and non-goals: one-alias-per-port, port 1852 reserved,
  central-relay for remote-to-remote, shared 1024-stream pool with the Pilot
  tunnel, no L7, host-network unsupported, in-memory activity log.
- Three concrete workflow examples and a complete troubleshooting accordion
  (every data-plane reason, every probe stage, every unreachable cause) plus
  a Common questions FAQ.
- Where Mesh fits CardGroup linking Pilot Agent, Multi-Node, Federation,
  Licensing.

Corrections vs prior text:
- Tab is labelled Traffic in the UI (not Routing); all navigation references
  updated.
- Proxy-mode bridge default is no idle close (env-overridable to opt into idle
  teardown); prior 5-minute-teardown claim removed.
- Audit trail scope tightened: only opt-in / opt-out write durable rows;
  tunnel-state and probe events live in the in-memory activity log.

Adds seven production screenshots under docs/images/sencho-mesh covering
Table view, opt-in sheet, graph (Tunnels and Aliases edge modes), Diagnostics,
activity log, and per-stack topology.

* docs(blueprints): add missing detail-state-review screenshot

Captures the Blueprint detail sheet with a deployment row in the
"Awaiting confirmation" status (stateful first-deploy gate), to fix the
broken image referenced at blueprint-model.mdx:132. mint broken-links
now reports zero broken references.

* docs(blueprints): deep rewrite with mental model, lifecycle, security, prerequisites

Restructures the Blueprints page against the v1-refresh template used by the
recently-refreshed mesh, secrets, and atomic-deployments pages. Adds a mental
model, prerequisites table, lifecycle and status-transition map, security and
trust boundaries section, practical workflows, common questions accordion,
and a Where Blueprints fits CardGroup. Removes the internal-style rollout
and watch-plan section. Replaces all nine production screenshots with fresh
captures against the production node signed in as admin, and adds two new
captures (federation pin policy table, stateless eviction dialog). Rewrites
the tier-gate Note to drop the fence-spec phrasing that violated Directive
31. Every retained claim is anchored to current backend or frontend code.

* docs(pilot-agent): recapture enrollment dialog with compose payload

Replaces the pre-0.84 docker-run capture with the current dialog (Compose
file, two-step instructions, "Copy compose file" button) and refines the
alt text to describe the captured content. URL and token redacted to
placeholder values during capture.
2026-05-20 08:43:18 -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
Anso 2e82eb44fe feat(fleet): multi-mode topology with label grouping and persisted positions (#1054)
The Topology view now offers three layouts so the canvas adapts to how the
fleet is organised, not the other way around:

- Hub: the gateway anchored on the left with remotes radiating right
- Grouped (Skipper+): remotes cluster by their primary node label, with the
  local node in its own cluster and unlabeled remotes in an Unlabeled cluster
- Free (Skipper+): drag any node; positions persist per browser via local
  storage

Node cards gain label pills (Skipper+), a cordon banner with reason tooltip,
a latency chip for online remotes, and a stale pilot-heartbeat glyph. All
fields are sourced from /api/fleet/overview, which already returns cordon,
latency, and pilot timestamps; no backend changes required.

Community continues to see the single Hub layout (without the toolbar, no
gating cues), matching the visibility principle that paid affordances are
hidden from lower tiers rather than displayed as locked teasers. The
backend /api/node-labels route is already requirePaid, so the data gate is
honoured end to end.

Includes unit tests for the layout module (hub/grouped/free coverage) and
the preferences hook (round-trip plus corrupt-JSON fallback).
2026-05-15 08:59:52 -04:00
Anso ccad5c925b feat(nodes): hide hub-only views when active node is remote (#1007)
* feat(nodes): hide hub-only views when active node is remote

Fleet, Schedules, Audit, Logs, and Auto-Update operate on hub-owned state
(node registry, fleet schedules, centralized audit, fleet-wide log
aggregation, fleet-wide update preview). When the active node is remote,
proxying those surfaces would show that remote's own disconnected state
instead of the hub's. Hide them from the nav strip and force-redirect to
Home if one was open during the node switch.

Backend hubOnlyGuard middleware sits between nodeContextMiddleware and the
remote proxy and rejects /api/scheduled-tasks, /api/audit-log, and
/api/notification-routes with 403 + HUB_ONLY_ENDPOINT when nodeId resolves
to a remote, closing the script-bypass path the UI gating cannot reach.

Settings sub-sections were already gated via the hiddenOnRemote registry;
this extends the same model to top-level views.

* docs(nodes): note hub-only visibility on Fleet, Schedules, Audit, Logs, Auto-Update

Each of the five hub-only feature pages now points readers to the
canonical "What top-level views show when a remote node is active"
section in multi-node.mdx, so users landing directly on a feature page
understand why the nav item disappears when they switch to a remote node.
2026-05-08 22:54:58 -04:00
Anso ecf4dd5d52 feat: open security basics, manual fleet ops, and basic fleet management to Community (#930)
Realign tier guards to the user-stated philosophy: Community covers
deploy/monitor at scale plus security basics, Skipper adds automation
and advanced fleet management, Admiral keeps enterprise control.

Community now includes:
- Trivy install / uninstall / update from the Settings Hub (admin role)
- CVE suppressions CRUD (admin role; replicates fleet-wide)
- Manual image scan with vuln, secret, and misconfig results
- Stack-config scan, scan comparison
- Manual fleet snapshots: create, list, view, restore, delete
- Per-node Sencho self-update (Check Updates + per-node Update)
- Fleet Overview search, sort, filters, node-card expand, auto-refresh

Stays paid:
- Scan policies with block_on_deploy enforcement (Skipper+)
- SBOM (SPDX, CycloneDX), SARIF export (Skipper+)
- Bulk Update All across the fleet (Skipper+)
- Scheduled snapshot create (now Skipper, was Admiral)
- Trivy auto-update toggle, fleet-wide policy push (Admiral)

The Settings -> Security tab is unhidden by setting the registry tier to
null. The SecuritySection no longer early-returns a PaidGate; the policy
list, Add Policy button, and policy dialogs are wrapped in {isPaid && }.
The Fleet view drops isPaid gates on the Snapshots tab, Check Updates
button, per-node update handlers, OverviewToolbar grid controls, the
NodeCard expand affordance, and the auto-refresh notice. The
NodeUpdatesSheet receives a canBulkUpdate prop and gates the Update All
button on it. useFleetUpdateStatus and useFleetPolling drop their isPaid
guards so polling runs for Community; useFleetOverview drops the isPaid
wrap on the filter and sort path.

Backend route guards are flipped per the matrix above. The scheduler
tick and requireScheduledTaskTier add 'snapshot' to the Skipper+ branch.
Backend test assertions are inverted for the now-Community endpoints
and a positive Skipper-snapshot-task test is added.

Documentation across features/, api-reference/, and operations/ is
updated to reflect the new tier mapping.
2026-05-05 12:54:26 -04:00
Anso d7d8f9bfe8 feat(dashboard): replace 24h charts with Configuration Status and Recent Activity (#785)
* feat(dashboard): replace 24h charts with Configuration Status and Recent Activity

The 24-hour CPU/Memory area charts summed per-container metrics normalized
to each container's CPU quota, producing numbers that bore no honest
relationship to host load. The live ResourceGauges strip already shows
accurate host-level stats, making the historical charts both inaccurate
and redundant.

This commit replaces that row with two side-by-side cards:

- **Configuration Status**: aggregates every toggleable feature on the
  active node (notification agents, alert rules, routing rules, auto-heal,
  auto-update, webhooks, scheduled tasks, MFA, SSO, vulnerability scanning,
  cloud backup, and alert thresholds) into a single at-a-glance card.
  Tier-locked rows display an upgrade indicator instead of a value.
  Each row is clickable and navigates to the relevant settings section.
  Data refreshes every 60 s and immediately on state-invalidate events.

- **Recent Activity**: lists the ten most recent notification-history events
  for the active node (deployments, image updates, auto-heal actions, scan
  findings, cloud backup events, system notices) with category icons and
  relative timestamps. Refreshes every 30 s.

New backend endpoints:
- GET /api/dashboard/configuration - per-node feature status with locked/
  requiredTier markers so the frontend renders upgrade chips without extra
  calls. The endpoint sits after authGate and before the remote proxy so
  remote-node requests are transparently forwarded.
- GET /api/dashboard/recent-activity?limit=N - thin wrapper over
  DatabaseService.getNotificationHistory.
- GET /api/fleet/configuration - fleet-wide fan-out using the same
  Promise.allSettled dead-node-tolerant pattern as /fleet/overview.
  Exposed as the new "Status" tab on the Fleet page (after Snapshots).

Shared utilities:
- visibilityInterval and formatCount extracted to frontend/src/lib/utils.ts
  so the three polling hooks and two components share a single copy.

* docs(dashboard): fix stale alt text referencing removed historical charts
2026-04-26 18:42:28 -04:00
Anso d95e154aeb feat(fleet): interactive topology with ReactFlow hub-and-spoke layout (#713)
Replace the static SVG fleet topology with a ReactFlow canvas laid out
via dagre. Each node renders as a rack card with status pill, type
badge, CPU/MEM/DISK bars, and stack/running counts. Pan, zoom, drag,
and minimap are enabled; user-dragged positions persist across the
30-second poll so live metric updates no longer reset layout.
2026-04-20 16:16:02 -04:00
Anso c3b06f4b13 feat(fleet): add aggregate masthead and local-vs-remote topology (#677)
Introduce a status masthead above the node grid summarising fleet-wide
CPU, memory, container, and alert counts with a coloured rail that
reflects overall health. Pin the local node at the top of the grid with
a cyan accent rail and a Local badge so it is never confused with a
remote. Add a Topology view toggle that plots the local node on the
left with remotes radiating out, and colour-codes connector lines by
link health.
2026-04-18 02:06:56 -04:00
Anso d23c6779af fix(fleet): harden remote node updates with admin enforcement, expiry fix, and diagnostics (#542)
- Fix completed-entry auto-expiry using resolvedAt instead of startedAt
- Add admin role enforcement to update trigger and update-all endpoints
- Add missing error field in rejected-promise fallback for update-status
- Fix rejected promises in update-all losing node names
- Harden frontend recheck button with try/catch/finally error handling
- Align frontend isValidVersion with stricter regex validation
- Add diagnostic logging gated behind developer_mode
- Extract resolveTracker helper to centralize terminal state transitions
- Add 15 new fleet test cases covering auth, tier gating, input validation, and admin roles
- Document admin requirement and troubleshooting in fleet-view docs
2026-04-12 22:07:41 -04:00
Anso 1702dabb7a fix(fleet): add auth middleware, input validation, and design system compliance (#536)
Add authMiddleware to all 13 fleet endpoints that were previously
accessible without authentication. Add NaN validation for parseInt
params, stackName validation on snapshot restore, and description
length cap on snapshot creation. Clean up updateTracker entries on
node deletion to prevent memory leaks.

Replace hardcoded colors with design system tokens, swap Select for
Combobox, replace overflow-y-auto with ScrollArea, fix card styling
(shadow-card-bevel, border tokens). Fix stale container data by
always refetching on stack expand with a loading guard against
concurrent requests.

Add operational logging for state-changing fleet operations and
diagnostic logging gated behind Developer Mode. Add 20 fleet tests
covering auth enforcement, input validation, tier gating, and
snapshot CRUD lifecycle.
2026-04-12 20:28:18 -04:00
Anso 368bef20d3 fix(fleet): detect updates via GitHub Releases instead of gateway self-comparison (#454)
* fix(fleet): detect updates via GitHub Releases instead of gateway self-comparison

The fleet update check compared each node's version against the gateway's
own version, so the local node could never appear outdated. Now fetches
the actual latest release from GitHub Releases API with a 30-minute
in-memory cache and thundering-herd protection. The Recheck button
invalidates this cache via ?recheck=true to force a fresh lookup.

* docs(fleet): update docs to reflect GitHub Releases version detection

Replace "Gateway version" references with "Latest version" to match the
new label. Document that version comparison uses the latest GitHub release
rather than the gateway's own version, and that Recheck refreshes the
cached latest version.
2026-04-09 12:21:37 -04:00
Anso 6fff2c2d35 fix(fleet): resolve self-update compose file access and improve completion detection (#441)
The self-update feature failed on remote nodes because SelfUpdateService
ran `docker compose -f <host_path>` inside the container, where the host
compose file path does not exist. The fix splits the update into two
steps: (1) pull the latest image directly via `docker pull`, and (2)
spawn a short-lived helper container that mounts the compose directory
from the host and runs `docker compose up --force-recreate`.

Additional changes:
- Use execFileSync/execFile with argument arrays instead of shell strings
  to eliminate shell injection surface from Docker label values
- Add Signal 4 completion detection: mark update as completed when the
  remote version matches the gateway version (with 15s elapsed guard)
- Extend early failure heuristic from 90s to 3 minutes for slow pulls
- Distinguish "node unreachable" from "node lacks self-update capability"
  in error messages; use silent skip in update-all to avoid res crashes
- Add requireAdmin guard to POST /api/system/update
- Handle comma-separated compose config file paths (multiple -f flags)
- Update fleet docs with self-update mechanism, troubleshooting entries
2026-04-08 14:59:11 -04:00
Anso a55d1245f8 fix(fleet): resolve version detection pipeline for Docker builds (#402)
* fix(fleet): resolve version detection pipeline for Docker builds

The Dockerfile backend-builder stage was missing a COPY of the root
package.json, causing generate-version.js to fall back to "0.0.0-dev"
at build time. At runtime, the filesystem walk also failed (root
package.json not in the final image), producing the string "unknown"
which the frontend rendered as "vunknown".

Changes:
- Dockerfile: copy root package.json into backend-builder stage
- CapabilityRegistry: return null (not "unknown") for unresolvable
  versions; add isValidVersion() type guard; normalize remote meta
  responses to strip "unknown"/"0.0.0-dev" sentinel values
- Fleet endpoints: hoist gateway version validation outside per-node
  loops; treat unresolvable remote versions as "potentially outdated"
  instead of silently marking them up to date
- FleetView: guard all version display points (card badge, update
  button, gateway label, modal columns) via shared formatVersion()
- EditorLayout, CapabilityGate: use shared isValidVersion utility
- New frontend/src/lib/version.ts shared utility
- Docs: add troubleshooting section for version display edge cases
- Screenshots: updated Fleet Overview and Node Updates modal

* docs: update fleet node updates screenshot with live remote node
2026-04-06 03:03:56 -04:00
Anso f516275834 refactor(licensing): replace Pro branding with Community/Skipper/Admiral tiers (#375)
Eliminate all references to "Pro" across backend, frontend, and docs.
Internal tier value renamed from 'pro' to 'paid'; user-facing text now
uses the thematic tier names (Community, Skipper, Admiral).

- Rename LicenseTier 'pro' to 'paid' in backend and frontend types
- Rename requirePro guard to requirePaid, error code PRO_REQUIRED to PAID_REQUIRED
- Rename ProGate.tsx to PaidGate.tsx with updated copy
- Fix: trial users can now see upgrade/purchase cards in Settings
- Update all docs and openapi.yaml to use correct tier names
2026-04-05 05:59:36 -04:00
Anso a1804c8fbe docs: comprehensive review and refresh of all documentation (#374)
* docs: comprehensive review and refresh of all documentation pages

Reviewed every doc page against the current app state after the v0.38
dashboard redesign. Updated content, fixed inaccuracies, and refreshed
all screenshots at 1920x1080.

Pages updated:
- introduction: expanded feature list to 25 items across 6 subsections
- quickstart: fixed docker run command (Docker Hub, auto JWT, COMPOSE_DIR)
- configuration: replaced personal paths with generic /home/user/docker
- sso-quickstart: fixed Settings navigation reference
- sso: added SSO_LDAP_DISPLAY_NAME env var
- overview: added 8 missing feature sections (labels, API tokens, schedules, etc.)
- dashboard: complete rewrite for new health bar, gauges, stack health table
- stack-management: updated for UP/DN indicators, rollback button, split actions
- editor: rewritten for two-column layout, inline stats, embedded terminal
- resources: updated Quick Clean docs, added network topology and inspect
- app-store: updated categories, deploy sheet details, permission gate, settings
- openapi.yaml: fixed YAML parsing error on line 1831

Screenshots refreshed: 14 images across 6 feature areas.

* docs: review and update observability, console, multi-node, and compatibility pages

- Global Observability: fix log format fields, add download button docs,
  split display limits into memory buffer vs rendered rows, correct
  settings labels
- Host Console: remove internal implementation details per security
  docs policy, add stack directory behavior, expand header bar docs,
  remove unverified scrollback claim
- Multi-Node: add Compose Directory field, document connection test
  details panel, fix edit/delete node behavior, simplify token security
  section, remove internal details
- Node Compatibility: add missing self-update capability, remove
  internal endpoint paths and cache TTL, move from Features to
  Reference group in navigation
- Refresh all screenshots for the redesigned UI (7 images)

* docs: review and refresh fleet, remote updates, labels, alerts, routing, and webhooks pages

- Fleet View: added node updates modal, container detail, version/update/critical badges, Tags filter
- Remote Updates: removed internal details, added capability cross-link, fast polling
- Stack Labels: three creation methods, two assignment methods, 10 colors, bulk actions screenshot
- Alerts & Notifications: fixed metric labels, added notification popover detail, status banner
- Notification Routing: HTTPS requirement, rule card layout, channel terminology fix
- Webhooks: corrected license tier to Admiral, matched action labels to UI, removed internal security details, added local-only note
- Troubleshooting: centralized entries from remote-updates, stack-labels, notification-routing
- Refreshed all screenshots at 1920x1080, removed 11 orphaned images

* docs: review and refresh RBAC, user management, and atomic deployments pages

- RBAC: added missing Auditor role (5th role), updated permission matrix, fixed license tier references, documented username/password validation rules, self-deletion protection
- Atomic Deployments: added "Which operations are protected" section covering webhooks/schedules/app store, removed internal backup path, fixed license tier to Skipper/Admiral
- Screenshots: cropped to dialog element per updated strategic cropping guideline, removed 2 orphaned images

* docs: review and refresh fleet-wide backups and audit log pages

Update fleet-backups page to reflect current inline create form, add
scheduled snapshots section, document the detail view and restore dialog,
expand RBAC table to all five roles. Update audit log page to document
expanded row detail fields, pagination, refresh button, and data retention
screenshot. Replace all screenshots with fresh captures at 1920x720.

* docs: review and refresh API tokens and private registries pages

- API Tokens: clarify Full Admin scope, add Managing tokens section
  with card details, document revocation confirmation dialog, add
  usage tracking to security model, refresh screenshot
- Private Registries: add Managing registries section with card details
  and action buttons, document edit behavior, fix URL auto-fill
  description, remove encryption algorithm name per security policy,
  fix grammar, refresh both screenshots

* docs: review and refresh auto-update policies, scheduled operations, and SSO pages

- Auto-Update Policies: document all 8 table columns, expand action buttons,
  add "All Stacks" wildcard option, fix field labels, add CSV export and
  pagination details, refresh screenshots
- Scheduled Operations: fix System Prune target description, add Task List
  table columns, restructure create dialog fields with action-specific
  annotations, rewrite execution history with column table, refresh screenshots
- SSO: remove encryption algorithm name per security policy, add LDAP and
  OIDC configuration field tables, document provider card controls (Save,
  Test Connection, Remove, Active badge), refresh screenshots
- Move SSO troubleshooting entries to centralized troubleshooting page

* docs: review and refresh licensing & billing page

Update upgrade card feature lists to match actual tier gating (Skipper:
fleet view, webhooks, labels, atomic deployments, backups, auto-update
policies; Admiral: scoped RBAC, SSO, audit log, host console, API
tokens, private registries, scheduled operations). Add flex layout to
align upgrade card buttons at the bottom. Replace stale screenshot with
fresh community and active license captures. Add feature breakdown
subsection and profile menu billing shortcut to docs.

* docs: review and refresh settings reference and security advisories pages

Settings Reference: add 5 missing sections (SSO, API Tokens, Registries,
Labels, Routing), expand Users from 2 to 5 roles, fix System Limits and
Developer field labels to match UI, restructure Developer into Streaming
and Data Retention sub-tables, update App Store and Support sections,
refresh overview screenshot.

Security Advisories: restructure into versioned sections (v0.25.x
hardening and v0.19-v0.24 CVE remediation), expand from 3 bullet points
to 10 specific improvements, fix GitHub URL from SaelixCode to AnsoCode,
redact internal details per security docs policy.

Remove "Sencho Pro" product name from all three pages, replaced with
tier names (Community, Skipper, Admiral).

* docs: review and refresh troubleshooting page, remove architecture and development guides

- Rewrote forgotten password section to remove exposed SQL and table names
- Updated all Settings navigation paths to Profile > Settings > X
- Fixed network topology from "tab" to "view mode", added Pro license note
- Updated Prune Networks to current "Prune Dead Networks" label
- Corrected update check cooldown from vague to 2 minutes
- Consolidated two network creation error sections into one
- Removed hardcoded version reference (v0.34.0)
- Replaced em dashes throughout
- Deleted architecture.mdx (exposes internal implementation details)
- Deleted development.mdx (contributor guide belongs in repo, not public docs)
- Removed both pages from docs.json navigation

* docs: review and refresh operations pages (backup, upgrade, self-hosting, troubleshooting)

Backup & Restore:
- Added missing encryption.key to all backup/restore procedures
- Added Warning about restoring db without matching encryption key
- Added cross-reference to Fleet-Wide Backups for paid tiers
- Removed false claim about no built-in backup scheduler
- Updated cron example to include encryption key copy

Upgrading Sencho:
- Removed internal migration details (table names, column specs, encryption algorithm)
- Replaced with high-level migration summary per security docs policy
- Added encryption.key to pre-upgrade backup command
- Updated version pinning example from 0.25.3 to 0.38.0
- Added Remote Updates cross-reference for Skipper/Admiral users

Self-Hosting Best Practices:
- Removed JWT_SECRET from env var table (auto-generated, not an env var)
- Removed PORT from env var table (hardcoded to 3000, not configurable)
- Added API_RATE_LIMIT to env var table (actually exists in code)
- Fixed listen port description from "configurable" to "fixed"
- Updated resource recommendations based on measured footprint audit
- Removed su-exec reference (internal implementation detail)
- Upgraded data directory Note to Warning with file names

Troubleshooting:
- Fixed "Pro features" heading to "Paid features" with correct tier names
2026-04-04 21:32:51 -04:00
Anso 32a7d53b2b feat: RBAC, atomic deployments, fleet backups, and licensing (Pro) (#185)
* feat: add RBAC viewer accounts, atomic deployments, and fleet-wide backups (Pro)

Introduces three Pro-tier features:

- RBAC: Multi-user system with admin/viewer roles, user management UI,
  automatic migration from single-admin credentials, viewer restrictions
  across the entire UI (read-only editor, hidden action buttons)

- Atomic Deployments: Pre-deploy file backup to .sencho-backup/, automatic
  rollback on health probe failure, manual rollback button, health probes
  added to stack updates, webhook-triggered deploys use atomic rollback

- Fleet-Wide Backups: Point-in-time snapshots of compose files across all
  nodes (local + remote), stored centrally in SQLite, per-stack restore
  with optional redeploy, graceful handling of offline nodes

* fix(settings): use correct ProGate prop name in UsersSection

* fix(settings): remove unused isPro prop from UsersSection

* fix(auth): fetch user info after login and setup so isAdmin is set correctly

* feat(pricing): revise pricing strategy and enforce variant-based seat limits

Raise Personal Pro from $49/yr to $69/yr with 3 viewer seats (up from 1).
Add $15/mo billing option for Team Pro. Mark lifetime pricing as a
90-day early-adopter offer. Store Lemon Squeezy variant_name on
activation/validation and enforce seat limits server-side per variant.

* feat(licensing): add Lemon Squeezy checkout, webhook, and billing portal integration

Server-side checkout URL generation (POST /api/checkout) with admin email
pre-fill and instance_id custom data. HMAC-SHA256 verified webhook endpoint
(POST /api/webhooks/lemonsqueezy) handling order, subscription, and payment
lifecycle events for automatic license activation. Customer billing portal
link stored from webhook events and exposed via GET /api/billing/portal.
In-app checkout buttons in Settings with manual license key fallback.

* fix(licensing): exempt Lemon Squeezy webhook from auth middleware

The catch-all auth middleware on /api/* was blocking the public webhook
endpoint. Added /webhooks/lemonsqueezy to the exemption list alongside
/auth/* and /webhooks/:id/trigger.

* feat(pricing): update pricing to final live rates

Personal Pro: $7.99/month, $69.99/year, $249 lifetime.
Team Pro: $49.99/month, $499.99/year, $1,499 lifetime.
Added personal_monthly checkout variant across backend, frontend, and website.

* refactor(licensing): remove server-side checkout/webhook for self-hosted model

Sencho is self-hosted — each user runs their own instance, so there is
no central server to receive webhooks or hold the store API key. Replaced
in-app checkout buttons with a "View Pricing" redirect to sencho.io and
kept manual license key activation as the primary flow.

- Delete LemonSqueezyService (checkout, webhook, HMAC verification)
- Remove POST /api/checkout, GET /api/billing/portal, POST /api/webhooks/lemonsqueezy
- Remove raw body parser and auth exemption for webhook route
- Remove all LEMONSQUEEZY_* env vars from .env.example
- Replace checkout buttons in SettingsModal with single "View Pricing" button
- Simplify LicenseContext checkout to open sencho.io pricing page
- Update licensing docs to reflect website-based purchase flow

* chore: normalize em-dashes to hyphens across codebase (linter)

* chore: remove accidentally tracked directories from index
2026-03-26 21:58:24 -04:00
Anso 0630f57ca8 feat(fleet): add Pro fleet management features and container drill-down (#174)
Add fleet health summary cards, container-level drill-down, node sorting
and filtering, fleet-wide search, and critical node detection to the
Fleet View. Fix ProGate to wrap placeholder content and add error toasts.
2026-03-25 23:09:53 -04:00