* chore: remove CLAUDE.md and MANUAL_STEPS.md from git tracking These files contain internal project instructions that should not be public. Both are now in .gitignore to prevent re-tracking. * chore: remove build output and generated files from git tracking Remove backend/public/ (frontend build artifacts) and backend/cookies.txt from tracking. Add demo-video/ and website/ to .gitignore. * docs: align mintlify theme with website and app branding Update colors to brand cyan, add Geist font, dark mode default, gradient background, navbar with GitHub link and CTA, and footer matching the website structure. * chore: add shadcn MCP server configuration * feat: add stack context menu, tier icons, centered logo, and support section - Add right-click context menu on sidebar stacks with full actions (alerts, check updates, deploy, stop, restart, update, delete) - Mirror same actions in the three-dot dropdown menu - Create TierBadge component with Globe/Crown/Users icons for Community/Pro/Team tiers, replacing ProBadge across the app - Center logo and app name in sidebar header - Add tiered Support section in Settings with docs, GitHub issues, and tier-appropriate email support channels - Remove duplicate links from About section * docs: update changelog and stack management docs for context menu
33 KiB
Changelog
All notable changes to this project will be documented in this file. The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
Added
- sidebar: right-click context menu on stacks with full lifecycle actions (deploy, stop, restart, update, delete, alerts, check for updates)
- sidebar: expanded three-dot dropdown menu to match context menu actions
- settings: new "Support" tab with tier-appropriate help channels (docs, GitHub Issues, email support for Pro)
- ui: TierBadge component with distinctive icons per tier — Globe (Community), Crown (Pro), Users (Team)
Changed
- sidebar: centered logo and app name in sidebar header
- settings: removed duplicate Documentation and GitHub Issues links from About section (now in Support tab)
- replaced ProBadge with TierBadge across UserProfileDropdown and Settings
0.9.0 (2026-03-27)
Added
0.8.0 (2026-03-26)
Added
0.7.0 (2026-03-26)
Added
0.6.0 (2026-03-26)
Added
0.5.0 (2026-03-25)
Added
0.4.0 (2026-03-25)
Added
[Unreleased]
Added
-
auth: Login and Setup pages redesigned with split-panel branding layout (dark branding panel + theme-aware form)
-
auth: Optional admin email field on Setup for future license recovery
-
ui: Mobile-responsive login/setup with compact logo header
-
fleet: Fleet health summary cards - aggregated container, CPU, memory, and alert counts across all nodes (Pro)
-
fleet: Container-level drill-down - expand stacks to see individual containers with state, uptime, and quick navigation (Pro)
-
fleet: Node sorting by name, CPU, memory, container count, or status with persistent preferences (Pro)
-
fleet: Status, type, and critical resource filtering with pill-style toolbar (Pro)
-
fleet: Fleet-wide search across node names and stack names (Pro)
-
fleet: Critical node detection with red badge for nodes exceeding 90% CPU or disk usage
-
fleet: Error toasts on stack and container fetch failures (replaces silent error swallowing)
-
fleet: ProGate now wraps placeholder content instead of empty children for a better upgrade preview
-
webhooks: Custom CI/CD webhooks - create webhooks targeting specific stacks and actions (deploy, restart, stop, start, pull), trigger them from GitHub Actions, GitLab CI, or any HTTP client with HMAC-SHA256 signature authentication (Pro)
-
webhooks: Execution history tracking - last 100 executions per webhook with status, duration, and error details
-
webhooks: Webhook management UI in Settings with create/edit/delete, enable/disable toggle, one-time secret reveal, and copy-to-clipboard for trigger URLs
-
rbac: Role-based access control with admin and viewer roles - viewers get full read-only access to dashboard, stacks, logs, and stats while admins retain full control (Pro)
-
rbac: User management UI in Settings - create, edit, and delete users with username/password/role controls, protected by admin-only access
-
rbac: Automatic migration of existing single-admin credentials to the new multi-user system on first boot
-
rbac: Viewer restrictions across the entire UI - read-only editor, hidden action buttons, disabled host console, and disabled resource management
-
atomic: Atomic deployments with automatic rollback - Sencho backs up compose.yaml and .env before deploying, and auto-restores if health probes detect crashed containers (Pro)
-
atomic: Manual rollback button in the stack action bar - restore the previous deployment with one click when a backup exists (Pro)
-
atomic: Health probes added to stack updates (previously only on deploys) - crashed containers trigger auto-rollback during updates too
-
atomic: Webhook-triggered deploys and updates now use atomic rollback for Pro users
-
fleet: Fleet-wide backups - snapshot all compose files and .env files across every node (local and remote) into a central backup stored in SQLite (Pro)
-
fleet: Snapshot detail view - browse captured files per node and stack with inline preview in a collapsible tree
-
fleet: Per-stack restore from any snapshot - overwrite current compose files with the snapshot version, with optional one-click redeploy (Pro admin)
-
fleet: Graceful handling of offline nodes during snapshot creation - skipped nodes are recorded with reason and displayed as warnings
-
licensing: In-app "View Pricing" button redirects to sencho.io for plan selection - license key activation is the primary flow for self-hosted instances
Changed
- pricing: Personal Pro - $7.99/month, $69.99/year, $249 lifetime (1 admin + 3 viewers)
- pricing: Team Pro - $49.99/month, $499.99/year, $1,499 lifetime (unlimited accounts)
- pricing: Lifetime pricing marked as time-limited early-adopter offer (90-day launch window)
- licensing: License variant (Personal/Team) now stored from Lemon Squeezy metadata and exposed in license API response
Removed
- licensing: Server-side Lemon Squeezy checkout URL generation, webhook endpoint, and billing portal - not applicable to self-hosted model where each user runs their own instance
- rbac: Seat limits enforced server-side based on license variant - Personal Pro: 1 admin + 3 viewers, Team Pro: unlimited
Fixed
- e2e: Use explicit
data-stacks-loadedstring values for reliable attribute selector matching - app-store: Fix crash caused by removed
Githubicon in newer lucide-react versions
0.3.1 (2026-03-25)
Fixed
0.3.0 (2026-03-25)
Added
0.2.5 (2026-03-25)
Fixed
0.2.4 (2026-03-25)
Fixed
0.2.3 (2026-03-25)
Fixed
[Unreleased]
Added
- licensing: Community/Pro tier system with Lemon Squeezy integration. Includes
LicenseServicebackend singleton,/api/licenseendpoints (activate, deactivate, validate),LicenseContextfrontend provider, License settings tab,ProBadgeandProGatereusable components, and 14-day Pro trial on first install. License data stored insystem_statetable with 72-hour periodic validation and 30-day offline grace period. - fleet: Fleet Overview dashboard showing all nodes in a card grid with status, container counts, CPU/RAM/disk usage bars. Pro tier unlocks stack drill-down, auto-refresh (30s), and per-node stack details via
/api/fleet/overviewand/api/fleet/node/:nodeId/stacksendpoints. - ui: User Profile Dropdown replacing scattered Settings/LogOut buttons. Includes theme toggle (System/Light/Dark), billing link (Pro only), documentation, and version display. Appearance tab removed from Settings.
- settings: About section displaying version, license tier, status, instance ID, and links to docs/changelog/issues.
Fixed
- charts: suppress Recharts
width(-1) height(-1)warnings by removing conflictingaspect-videodefault and addingminWidth={0}+minHeight={0}onResponsiveContainer.
0.2.2 (2026-03-25)
Fixed
- editor: ESLint unused params fix (dd5b698)
- editor: ESLint unused params fix (dd5b698)
- editor: remove unused params from getWorker to satisfy ESLint (34172a9)
- editor: remove unused params from getWorker to satisfy ESLint (34172a9)
- editor: remove unused params from getWorker to satisfy ESLint (59290e9)
0.2.1 (2026-03-24)
Fixed
- editor: bundle Monaco locally to fix stuck Loading state (0eaa45b)
- editor: bundle Monaco locally to fix stuck Loading state (0eaa45b)
- editor: bundle Monaco locally to fix stuck Loading state and CSP block (79fde6e)
- editor: Monaco CSP fix + release pipeline fixes (36a9bf3)
- editor: Monaco CSP fix + release pipeline fixes - v0.2.1 (36a9bf3)
0.2.0 (2026-03-24)
Added
- ci: add release-please automated versioning workflow (c2d5d37)
- ci: add release-please automated versioning workflow (c2d5d37)
- ci: add release-please automated versioning workflow (c294def)
- ci: automated versioning with release-please (c991b81)
- ci: automated versioning with release-please (c991b81)
Fixed
- ci: correct release-please changelog section names and tag format (ea57cbe)
- ci: correct release-please changelog section names and tag format (ea57cbe)
- ci: correct release-please changelog section names and tag format (e653bc2)
- ci: release-please config corrections (b6391b9)
- ci: release-please config corrections (b6391b9)
[Unreleased]
Fixed
- fix(editor): Monaco editor stuck on "Loading…" -
@monaco-editor/reactwas fetching Monaco's loader fromcdn.jsdelivr.netat runtime, which the Helmet CSP (scriptSrc: 'self') correctly blocked. Fixed by configuringloader.config({ monaco })inmain.tsxto use the locally bundledmonaco-editornpm package and wiring a Vite?workerblob URL for the editor worker - no CDN requests, no CSP changes needed. - fix(ci):
release-please.ymlusedGITHUB_TOKENto create release tags - GitHub's security model preventsGITHUB_TOKEN-triggered events from cascading to other workflow runs, sodocker-publish.ymlnever fired after a release. Switched toDOCS_REPO_TOKEN(PAT) so tag creation correctly triggers the Docker Hub publish workflow. - fix(ci): Screenshot refresh PR now auto-merges via
gh pr merge --auto --squashafterpeter-evans/create-pull-requestcreates it - no more manual merge required on everydeveloppush.
Fixed
- fix(ci):
docker-publish.ymlwas triggered byrelease: types: [published](GitHub Release event) instead ofpush: tags: v*- pushing a git tag never fired the workflow. Changed trigger topush: tags: v*and updatedenableconditions fromgithub.event_name == 'release'tostartsWith(github.ref, 'refs/tags/v')so anyv*tag push automatically builds and publisheslatest+ semver tags to Docker Hub without requiring a manual GitHub Release.
Added
- feat(ci): Automated versioning via
release-please- on every push tomain, therelease-pleaseworkflow opens or updates a Release PR with a generated CHANGELOG entry andpackage.jsonversion bump. Merging the Release PR creates thevX.Y.Ztag which triggersdocker-publish.yml. Version bumps follow Conventional Commits:fix:→ patch,feat:→ minor,feat!:/BREAKING CHANGE:→ major.
0.1.0 - 2026-03-24
Security
- Fixed: Missing
authMiddlewareonGET /api/notifications,POST /api/notifications/read,DELETE /api/notifications/:id,DELETE /api/notifications,POST /api/notifications/test, andPOST /api/system/console-token- any unauthenticated client could reach these endpoints. - Fixed: Remote node
api_urlaccepted without validation - an attacker could set it tohttp://localhost:6379to SSRF into internal services. Now validates: must be a well-formedhttp://orhttps://URL and the hostname may not belocalhost,127.x.x.x,[::1], or0.0.0.0. - Fixed:
env_filepaths incompose.yamlwere accepted without boundary checking - absolute paths like/etc/passwdcould be read or written. All resolved env file paths are now validated to stay within the stack directory. - Fixed: Stack name validated in write routes but not GET routes - path-traversal names now return 400 on all routes.
- Fixed:
stackParamquery parameter on/api/system/host-consolenow validated againstpath.resolve+startsWith(baseDir)to prevent directory traversal when setting the PTY working directory. - Fixed:
HostTerminalServiceno longer forwards fullprocess.envto spawned PTY shells -JWT_SECRET,AUTH_PASSWORD,AUTH_PASSWORD_HASH, andDATABASE_URLare stripped before the shell is spawned. - Fixed: Host Console and container exec WebSocket endpoints now reject
node_proxyscoped JWT tokens with HTTP 403. - Fixed:
GET /api/settingsno longer leaksauth_username,auth_password_hash, orauth_jwt_secretto the frontend. - Fixed:
POST /api/settingsenforces a strict allowlist of writable keys - auth credential keys and unknown keys are rejected with a 400 error. - Added: Rate limiting on
/api/auth/loginand/api/auth/setup- 5 attempts per 15-minute window per IP, usingexpress-rate-limit. - Added:
helmetmiddleware for security response headers (X-Frame-Options, X-Content-Type-Options, Referrer-Policy, etc.). - Changed: CORS is now restricted to
FRONTEND_URLenv var in production; development continues to allow any origin.
Added
Infrastructure & CI
linux/arm64platform support in the Docker Hub publish workflow (Raspberry Pi 4/5, Oracle ARM VMs) - native modules (bcrypt,better-sqlite3,node-pty) cross-compiled viatonistiigi/xxto eliminate theSIGILLcrash caused by Node.js v20 using ARMv8.1 LSE atomic instructions unsupported by GitHub Actions QEMU.docker/setup-qemu-action@v3step todocker-publish.yml- without it multi-platform builds hung indefinitely.- Automated Docker Hub CI/CD pipeline publishing
devandlatesttags. - Automated documentation pipeline with Mintlify sync and screenshot refresh CI job.
HEALTHCHECKdirective inDockerfile- Docker polls/api/healthevery 30 s and restarts an unhealthy container.GET /api/healthpublic endpoint returning{ status: "ok", uptime }.docker-entrypoint.sh- runs as root, fixes$DATA_DIRvolume ownership, then drops to the non-rootsenchouser viasu-execbefore starting Node. EliminatesSQLITE_READONLYcrashes on host-mounted volumes.- Non-root
senchosystem user inDockerfile; process no longer runs as root. - Graceful shutdown - backend listens for
SIGTERM/SIGINT, drains HTTP connections, stopsMonitorServiceandImageUpdateService, and closes the SQLite connection before exiting. - Vitest backend test suite - 38 tests covering validation utilities, health endpoint, authentication flows, auth middleware enforcement, console-token security, and SSRF validation. Run with
cd backend && npm test. - Playwright E2E test scaffolding (
e2e/) - auth, stack management, and node management specs with shared login helper. Run withnpm run test:e2e. - CI workflow runs Vitest unit tests and ESLint on every PR.
isValidStackName,isValidRemoteUrl,isPathWithinBaseextracted tobackend/src/utils/validation.tsfor reuse and testability.
Multi-Node & Distributed API
- Distributed API proxying using
http-proxy-middlewarefor HTTP and WebSockets - replaces the SSH/SFTP architecture entirely (~500 lines removed). - Long-lived JWT generation for Sencho-to-Sencho API authentication (
POST /api/auth/generate-node-token). nodeContextMiddlewarein Express to dynamically extractx-node-idheaders and?nodeId=query parameters for WebSocket upgrades.NodeRegistryservice managing multiple Docker daemon connections.- Node management API endpoints: list, get, create, update, delete, and test connection.
- Two-tier scoped navigation UX - context pill in the top header always shows the active node name (pulsing blue for remote, green for local).
- Remote-aware headers in
HostConsole,ResourcesView,GlobalObservabilityView, andAppStoreView. SettingsModalscopes its sidebar to the active node type - global-only tabs hidden when a remote node is active.- Cross-node notification aggregation - notification bell surfaces alerts from all connected remote nodes with dedicated real-time WebSocket connections per remote node.
- Remote node host console and container exec WebSocket proxy - gateway exchanges
node_proxytoken for a short-livedconsole_sessionJWT (60 s TTL) before forwarding. localOnlyoption onapiFetch- omitsx-node-idso requests always route to the local node.
Application Features
- App Store - LinuxServer.io API integration as default template registry with rich metadata (architectures, docs links, GitHub links), category filter, one-click deployment, atomic rollback on failure, custom Portainer v2 registry URL support, editable ports/volumes/environment variables, post-deploy health probe.
- Resources Hub - Images, Volumes, and Networks tabs with Managed/External/Unused classification, Docker Disk Footprint stacked-bar widget, scoped prune operations (Sencho-only vs All Docker), managed/external filter toggles, and classification badges.
- Global Observability - centralized dashboard tracking 24-hour historical metrics and aggregating global tail logs across all containers. Dozzle-style Action Bar with multi-select stack filtering, search, STDOUT/STDERR toggles, and Developer Mode SSE real-time streaming.
- Background image update checker - polls OCI-compliant registries every 6 hours using manifest digest comparison; results cached in
stack_update_statustable; pulsing blue dot badge on stacks with available updates. - Real-time WebSocket notifications - replaces 5-second polling;
NotificationService.setBroadcaster()pushes each new alert to all authenticated subscribers the moment it fires. - Live Container Logs viewer using SSE for real-time terminal output.
- Animated design system -
motionpackage andanimate-uilibrary; new brand cyan token; spring-based dialog/tooltip/tab animations;prefers-reduced-motionrespected globally; Geist font via Google Fonts CDN. - Theme-aware sidebar logo - dark and light variants auto-switch based on active theme.
- Auto theme option (light/dark/auto) with
window.matchMedialistener. PATCH /api/settingsbulk-update endpoint - validates all values via Zod schema, persists atomically in a single SQLite transaction.system_stateSQLite table - separates runtime operational state from user-defined config inglobal_settings.- Configurable
metrics_retention_hours(default: 24 h) andlog_retention_days(default: 30 d) -MonitorServicereads these dynamically each cycle. - Managed/unmanaged container count split in
GET /api/stats- Home Dashboard "Active Containers" card shows "N managed · N external". - Two-Stage Teardown for stack deletion -
docker compose downsweeps ghost networks before deployment files are deleted. - Custom Environment Variable injection tool in deployment UI.
ErrorBoundarycomponent now wraps root<App />inmain.tsx.- Git Flow branching strategy and branch protection.
Fixed
Authentication & Proxy
- Login loop caused by remote node auth failure -
apiFetchnow only firessencho-unauthorizedwhen thex-sencho-proxy: 1header is absent (i.e., a genuine local session failure, not a remote node auth error). authMiddlewareand WS upgrade handler now evaluatebearerToken || cookieToken(Bearer first) - cookie no longer shadows a valid Bearer token on node-to-node proxy calls.- Remote node proxy stripping the
/apipath prefix - addedpathRewrite: (path) => '/api' + pathto restore the full path when forwarding to remote instances. - Remote node HTTP proxy body forwarding - replaced
proxyReq.write(JSON.stringify(req.body))(raced againsthttp-proxy'sprocess.nextTick(proxyReq.end)) with a conditional JSON body parser that skipsexpress.json()for remote-targeted requests; the rawIncomingMessagestream is left unconsumed sohttp-proxy'sreq.pipe(proxyReq)forwards it intact. - Remote node proxy forwarding the browser's
sencho_tokencookie to the remote instance - stripped inproxyReqso only the Bearer token is used. - Remote WebSocket upgrades forwarding the browser
cookieheader - stripped beforewsProxyServer.ws()so the remote'sauthMiddlewareuses the Bearer token exclusively. nodeContextMiddlewareblocking/api/nodeswhenx-node-idreferences a deleted node - exempted alongside/api/auth/so the frontend can re-sync a stale node ID.- Backend memory leak from
createProxyMiddlewarecalled inside the request handler on every API call - refactored to a single globally-instantiated proxy using therouteroption. remoteNodeProxyerror handler unsafely castproxyRestoResponseon WebSocket/TCP-level errors - type-narrowed before sending 502.
WebSocket & Streaming
- Container stats WebSocket flooding React with up to 20+
setStatecalls per second - replaced with a ref-buffer + 1.5 s flush interval pattern. streamStatsDocker stats stream leaking after WebSocket client disconnect -ws.on('close')handler callsstats.destroy(); allws.send()calls guarded withreadyState === OPEN.streamStatsandexecContainercalled unawaited - unhandled promise rejections now chain.catch(), log the error, and close the WebSocket cleanly.- Per-connection
WebSocket.Serverinstances for stack logs and host console never closed after upgrade -wss.close()called immediately afterhandleUpgrade. - WebSocket notification reconnect upgraded to exponential backoff (1 s → 30 s max) instead of flat 5-second retry;
ws.onerrorlogs the event; cleanup guards against closing an already-closing socket. - Terminal logs and container stats WebSockets failing with "HTTP Authentication failed" on remote nodes - gateway's
cookieheader stripped before forwarding to remote;nodeIdquery param stripped from forwarded URL. - LogViewer returning 404 on remote nodes -
nodeIdquery param stripped fromproxyReq.pathinonProxyReq.
UI & Frontend
- Blank page on HTTP deployments (root cause - Helmet 8 default CSP
upgrade-insecure-requestsand HSTS) -upgradeInsecureRequests: nullandstrictTransportSecurity: falseset explicitly. - COOP header console warning on HTTP deployments -
crossOriginOpenerPolicy: false. - Inline script CSP violation from Vite module-preload polyfill - disabled via
build.modulePreload.polyfill: false. - CSP
workerSrcmissing (Monaco editor workers) - addedworker-src 'self' blob:. - CSP
connectSrcimplicit - added explicitconnect-src 'self' ws: wss:. - Docker socket
EACCESroot:root edge case - entrypoint handles GID 0 in addition to the standard root:docker case. - Managed container count wrong when stacks launched from COMPOSE_DIR root - classification now uses
com.docker.compose.project.working_dir. - Browser Out of Memory crash in
GlobalObservabilityView- capped DOM rendering to last 300 entries, reduced SSE log cap to 2,000 entries, replacedkey={idx}with monotonic_idcounter. HomeDashboardcreate-stack error handling - reads JSON error body before throwing; uses defensive toast pattern.AlertDialogContentusingasChildwithmotion.divwrapper crashing on delete-stack confirmation - replaced with CSS keyframe animations.- animate-ui
auto-height.tsximportingWithAsChildwithouttypekeyword - crashed browser module loader. - animate-ui
switch.tsxdouble-spreading Radix props ontomotion.buttonDOM element. - "Always Local" badge tooltip crashing (
getStrictContext) - replaced animate-ui tooltip with pure Radix primitives. - Cancel/Add Node buttons in NodeManager dialogs stuck together.
- Resources/App Store/Logs menu buttons not toggling off on second click.
- Monaco container height accumulation on tab switching - reset to 0×0 and force synchronous reflow before re-measuring.
AppStoreViewandGlobalObservabilityViewusing rawfetch()instead ofapiFetch()- all calls now injectx-node-id.HostConsoleWebSocket URL missing?nodeId=query parameter.- "Open App" button opening
http://localhost:{port}for remote node containers - resolves hostname from remote node'sapi_url. - Dashboard cards showing stale local-node data after switching to a remote node - polling effects now depend on
activeNode?.idand clear state immediately on node change. refreshStackscrashing withSyntaxErrororTypeErrorwhen the remote proxy returns a non-JSON response - checksres.okbefore callingres.json().- Four empty
catch {}blocks inEditorLayout- now surface errors viatoast.error(). StackAlertSheetnot fetching notification agent status from the active node on open.SettingsModalNotifications tab hidden when a remote node is active - now visible and configurable on remote nodes.POST /api/alertsnow validates the request body with a Zod schema - rejects unknown metric/operator values, negative thresholds, and missing fields with a structured 400.WebSocket.Serverreplaced with named importWebSocketServerfromwsto fix ESM/CJS interop.NodeProvidermounted outside the auth gate - moved inside the authenticated branch sorefreshNodesno longer fires before authentication.- Infinite re-fetch loop in
NodeContext-refreshNodesuseCallback no longer depends onactiveNodestate; replaced withuseRef. - Infinite page reload loop -
apiFetchreplacedwindow.location.href = '/'with asencho-unauthorizedcustom event. - API Token copy button failing silently on HTTP/non-localhost - added
execCommand('copy')fallback. - E2E nodes tests permanently timing out because the Add Node submit button requires
api_tokento be non-empty. - ESLint CI step - replaced all
anyannotations with proper types, fixed unused catch variables. [DEP0060] DeprecationWarning: util._extendfromhttp-proxy@1.18.1- suppressed at call site.- Global Logs false-positive error misclassifications - replaced naive regex with a robust 3-tier classification engine.
- Memory leak in
GlobalObservabilityViewSSE mode - log array capped at 2,000 entries. - Historical metrics memory leak - polling throttled to 60 s; SQLite payload downsampled by 12×.
- Active node UI dropdown desyncing from API requests on initial page load - state hydrated from localStorage.
MonitorServicecrash (Cannot read properties of undefined (reading 'cpu_usage')) during Docker container transition states.- Deleted node ghost API calls - 404 errors intercepted globally, forcing UI to resync to default node.
- Horizontal UI overflow in Node Manager settings on smaller resolutions.
- Docker API parsing bug where HTML string responses from misconfigured ports were counted as containers.
Changed
- Architecture: Replaced SSH/SFTP remote node model with Distributed API proxy (HTTP/WebSocket) - remote nodes now only require an API URL and Bearer token. Node Manager UI vastly simplified.
- Docs: Migrated Mintlify config from deprecated
mint.jsontodocs.jsonv2 format; bootstrapped full user-facing documentation (configuration, stack management, editor, multi-node, alerts, dashboard, resources, app store, observability, settings reference, troubleshooting, backup & restore). - Design system: Animated UI overhaul - new brand cyan token, spring-based animations on dialogs/tooltips/switches/tabs, dark mode shadow strengthening, Geist font now actually loaded.
- Notification delivery replaced polling with WebSocket push - no more
setIntervalinEditorLayout. DatabaseService.addNotificationHistoryreturns the full inserted record for real-time broadcasting.SettingsModaloverhauled - per-operation loading states, skeleton loader, unsaved-changes indicator, all saves usePATCH /api/settings.MonitorServiceevaluates limits and detects container crashes across all registered nodes concurrently.MonitorServicereads retention settings dynamically each cycle.- Developer settings scoped to the local node - reads/writes always target local via
localOnlyregardless of active node. - Dark mode scrollbar styling - no more white native scrollbars.
- Rebranded "Templates" → "App Store", "Ghost Containers" → "Unmanaged Containers", "Observability" → "Logs".
- Global logs display chronologically (newest at bottom) with smooth auto-scrolling; UTC → local browser timezone.
- Historical CPU/RAM charts relocated to the Home Dashboard; data normalized (CPU relative to host cores, RAM to GB).
EditorLayoutmain workspace container keyed toactiveView- every view switch triggers a fade-up entrance animation.
Removed
- SSH/SFTP remote node adapters (
IFileAdapter,LocalFileAdapter,SSHFileAdapter,SSHFileAdapter,ComposeService.executeRemote,ComposeService.streamLogsSSH path) - ~500 lines.