libsodium-sys requires memset_explicit which is not available in older
MinGW toolchains. Added a C shim that provides a fallback implementation
using volatile pointers to prevent compiler optimization.
Changes:
- Added memset_s_shim.c with fallback memset_explicit implementation
- Updated build.rs to compile shim for Windows GNU targets
- Added cc crate as build dependency
- Set CFLAGS in CI to target Windows 8+ (_WIN32_WINNT=0x0602)
- Set SODIUM_STATIC=yes to force static libsodium build
Fixes linking error: undefined reference to memset_explicit
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add check_app_updates, install_app_updates, get_update_channel, set_update_channel to Tauri handler
- Add unit tests for update channel functionality
This fixes the 'Command check_app_updates not found' and 'Failed to update channel' errors reported in the latest build.
- Add release-beta.yml: triggers on push to beta, creates
v{CARGO}-beta.N pre-release tags with prerelease: true, builds all
four platforms; tag counter resets when Cargo.toml version bumps
- Add beta to test.yml push triggers so CI runs on direct pushes to
beta (pull_request already covers PRs targeting beta)
- Implement update_channel in AppSettings (state.rs) with serde
default "stable"; wire get/set_update_channel commands to AppState
instead of returning hardcoded stubs
- Implement channel-aware check_app_updates: queries /releases?limit=20
and picks first non-draft release matching the active channel
(stable = !prerelease, beta = prerelease), skipping drafts
- Document two-channel strategy in docs/wiki/CICD-Pipeline.md
Manual steps still required in Gitea UI:
1. Create beta branch from master
2. Apply same branch protection rules as master to beta
3. Set repo default PR target branch to beta
- Replace hardcoded dummy data in VMs, Containers, Storage, Backup, and
Firewall pages with live API calls; show empty-state UI when no
clusters are configured
- Add list_proxmox_containers backend command (LXC via cluster/resources)
and register it in the Tauri handler and frontend proxmoxClient.ts
- Fix add_proxmox_cluster to store credentials without requiring a live
Proxmox connection; persist username in DB (migration 034); update
list/get queries to read username column from new schema
- Replace alert() in RemotesPage with toast.error() + rethrow so errors
surface correctly in Tauri WebView
- Replace tauri-plugin-updater with direct Gitea HTTP API call for
update checks; use tauri-plugin-opener for browser launch; Updater UI
now shows current/latest version and release notes
- Add gogs.tftsr.com to CSP connect-src
- Fix all 74 pre-existing ESLint no-explicit-any warnings in
proxmoxClient.ts; remove stale eslint-disable directive in ACLPage.tsx
- All checks pass: cargo fmt, clippy -D warnings, 411 Rust tests,
tsc --noEmit, eslint --max-warnings 0, 386 frontend tests
- Add migration 033 to automatically clean up old dummy/proxmox test data
- Fix cluster deletion functionality
- Fix cluster creation and save functionality
- Bump version to 1.2.2 in all config files
- Update CHANGELOG and feature parity documentation
- Run cargo fmt and clippy, all checks pass
- Replace NetworkPage placeholder with live network interface list (type, address, gateway, active/autostart badges)
- Replace TasksPage placeholder with real cluster task log including running/completed/failed summary cards
- Create ViewsPage with create/delete UI for custom dashboard views
- Fix createClusterView TS client to pass viewId + name params matching Rust command signature
- Fix ClusterView TS interface to use view_id matching Rust DashboardView serialization
- Add ClusterInfoWithHealth struct to list_proxmox_clusters command with connected field reflecting in-memory pool state
- Add connected? field to ClusterInfo domain type
- Wire /proxmox/views route and Views nav entry in App.tsx
Adds 20 new TypeScript client functions to proxmoxClient.ts with typed
interfaces, and 20 corresponding Tauri commands in commands/proxmox.rs
wired up across Phases 6-15. All commands are registered in lib.rs.
Rust and TypeScript type checks pass clean.
- Implement check_updates with full update information
- Implement list_updates and get_update_status
- Implement refresh_updates and install_updates
- Implement get_update_history
- All operations use proper error handling with Option safety
- Add 2 unit tests for update info and status serialization
- Implement list_ha_groups with full group configuration parsing
- Implement create_ha_group, update_ha_group, delete_ha_group
- Implement list_ha_resources with full resource configuration
- Implement enable_ha_resource and disable_ha_resource
- Implement manage_ha_resource for custom actions
- Implement get_ha_group_status and get_ha_resource_status
- All operations use proper error handling with Option safety
- Add 2 unit tests for HA group and resource serialization
- Implement list_firewall_rules with full rule configuration parsing
- Implement add_rule, delete_rule, update_rule
- Implement enable_firewall and disable_firewall
- Implement get_firewall_status with full status details
- Implement get_firewall_zone and list_firewall_zones
- All operations use proper error handling with Option safety
- Add 2 unit tests for firewall rule and status serialization
- Implement list_evpn_zones with full zone configuration parsing
- Implement create_evpn_zone, update_evpn_zone, delete_evpn_zone
- Implement list_vnets with full virtual network configuration
- Implement create_vnet, update_vnet, delete_vnet
- Implement get_vnet_status and list_dhcp_leases
- All operations use proper error handling with Option safety
- Add 2 unit tests for EVPN zone and virtual network serialization
- Implement list_pools with full pool configuration parsing
- Implement create_pool and delete_pool
- Implement set_pool_quota for capacity management
- Implement list_osds with up/in status and weight
- Implement set_osd_weight, osd_out, osd_in for OSD management
- Implement list_mds, get_mds_status, mds_failover
- Implement list_rbd, create_rbd, delete_rbd, clone_rbd, resize_rbd
- Implement create_snapshot for RBD snapshots
- Implement list_monitors, get_monitor_status, quorum_health
- Implement get_ceph_health with status, summary, and details
- All operations use proper error handling with Option safety
- Add 4 unit tests for pool, OSD, monitor, and health serialization
- Implement list_backup_jobs with full job configuration parsing
- Implement create_backup_job and update_backup_job
- Implement delete_backup_job and trigger_backup_job (manual execution)
- Implement list_datastores and get_datastore_status
- Implement list_backup_snapshots and restore_backup
- All operations use proper error handling with Option safety
- Add 2 unit tests for backup job and datastore info serialization
- Implement start_vm, stop_vm, reboot_vm, shutdown_vm, resume_vm
- Implement suspend_vm for pausing VMs
- Implement list_vms with full resource filtering
- Implement get_vm with detailed configuration parsing
- Implement get_vm_status and get_vm_config
- Implement create_vm, delete_vm, clone_vm
- Implement migrate_vm for live migration
- Implement snapshot operations: create, delete, rollback, list
- All operations use proper error handling with Option safety
- Add 2 unit tests for VM info and state serialization
- Add proxmox module with client, cluster, and resource management
- Implement VM management stubs with full API documentation
- Add database migrations for proxmox_clusters and proxmox_resources tables
- Implement IPC commands for cluster CRUD operations
- Add Proxmox state management to AppState
- Create 22 unit tests for Proxmox modules (all passing)
- Update lib.rs, state.rs, and integrations.rs for Proxmox integration
Monaco: install monaco-editor and configure @monaco-editor/react loader to use the locally
bundled module in main.tsx instead of the CDN, resolving the perpetual spinner in YamlEditor
under Tauri's offline WebView. Added worker format and optimizeDeps entries to vite.config.ts.
Pod columns: extend PodInfo struct with ip, node, and restarts fields; extract podIP, nodeName,
and restartCount from kubectl JSON output in parse_pods_json so the IP, Node columns render
real data instead of blanks.
Also fix ref-during-render lint error in useKeyboardShortcuts.
Add `path_str()` to `KubeconfigGuard` so the path can be passed to
`SessionParams` without consuming the guard. Both `start_pty_exec_session`
and `start_pty_attach_session` now hold the guard live until
`start_exec/attach_session` returns `Ok`, then disarm it. Previously
`disarm()` was called before the session-start call, meaning a kubeconfig
temp file would leak if PTY spawn or session registration failed after the
guard was consumed.
BLOCKER fixes:
- Implement create_azuredevops_workitem instead of returning a stub error,
reusing the existing create_work_item integration helper and writing an
audit-log entry on success.
- Log kill failures in PtySession::Drop so leaked child processes surface
in tracing rather than being silently swallowed.
- Add explicit PTY cleanup on every exit path of run_session_io (process
exit, read error, write error, resize error, terminate command).
- Treat PTY resize failures as fatal: emit terminal-error to the frontend
and break the session loop instead of just warning.
WARNING fixes:
- Remove the dead extract_json_path_value helper from commands/kube.rs.
- Wrap temp kubeconfig files in commands/metrics.rs in an RAII guard
(TempKubeconfig) so they're removed on early-return / panic paths.
- Wrap temp kubeconfig files in commands/shell.rs PTY-session starters
in a disarmable RAII guard (KubeconfigGuard); if kubectl resolution
fails we no longer leak the file.
- Drop the `clear;` prefix from the kubectl-exec shell fallback so
containers without `clear`/`tput` don't print a confusing error.
SUGGESTION fixes:
- Document why node CPU/memory percentages are 0.0 in metrics::client
and link the gap to future work fetching node capacity.
- Add a module-level doc comment to AppState describing the
synchronization expectations (std vs tokio Mutex) for each public
field, and warn against holding std::sync MutexGuards across .await.
Verified: cargo fmt --check, cargo clippy -- -D warnings, and
cargo test (377 passed, 6 ignored) all pass.
- Add metrics module with CPU/memory parsing
- Create get_pod_metrics and get_node_metrics commands
- Parse kubectl top pods/nodes JSON output
- Format CPU (nanocores) and memory (KB) to human-readable
- Add unit tests for parsing functions
- Register metrics commands in Tauri
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replace LogsModal with LogStreamPanel in PodList for streaming logs
Add smart positioning to ResourceActionMenu to flip when near bottom
Fix dark mode text visibility by applying class to html element
Fix YAML editor loading race condition
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Fix LogStreamPanel event listener cleanup with synchronous unlisten
- Fix eventBus async-unsafe unsubscribe with proper error handling
- Fix KubernetesPage infinite loading by resetting state on section change
- Add ErrorBoundary component with reset capability
- Add Badge component with multiple variants
- Add ResourceDetailsDrawer for slide-out details panel
- Add useFavorites hook with localStorage persistence
- Add useKeyboardShortcuts hook for declarative shortcuts
- Add comprehensive test coverage for all new components/hooks
- Add keyboard shortcuts documentation to README
- Wrap KubernetesPage with ErrorBoundary for crash recovery
- Install react-window for virtual scrolling support
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Pod actions (logs, shell, attach, edit, delete) were receiving namespace='all'
from the UI filter prop and passing it to kubectl as -n all. Fixes by adding
namespace field to PodInfo (Rust + TypeScript) and using pod.namespace in all
action command calls in PodList.
Each kubectl command now uses a globally unique temp kubeconfig path via
an AtomicU64 counter, preventing TempFileCleanup from deleting a file that
a concurrent call is still using.
Add write_secure_temp_file() helper that creates files with mode 0600
on Unix (owner read/write only) instead of the default 0644
(world-readable). All 41 temp kubeconfig write sites updated.
Kubeconfig files contain cluster credentials; world-readable temp files
would expose them to any local user on the system.
- Add detect_auth_method() to identify kubeconfig credential type
(exec plugin, bearer token, inline cert, file-path cert, basic auth)
and surface warnings when the auth requires an external binary or file
- Split test into Stage 1 (kubectl get --raw=/healthz, no auth) and
Stage 2 (kubectl cluster-info, authenticated), so connectivity and
auth failures are reported distinctly rather than collapsing both
into opaque memcache.go noise
- Output now includes auth method and per-stage result for faster
diagnosis of 'server requires credentials' vs unreachable host
Credential error persists: switch all 40 kubectl invocations from using
KUBECONFIG env var to the explicit --kubeconfig CLI flag. The flag has higher
precedence in kubectl's lookup order and is unambiguous regardless of any
inherited KUBECONFIG env var in the parent process environment.
Also adds test_kubectl_connection Tauri command (runs kubectl cluster-info
with the stored kubeconfig) and a Test button in Settings → Kubeconfig so
the exact kubectl output — context name, exit code, full stdout/stderr — is
visible without needing to inspect tracing logs. This output will reveal
whether the issue is expired certs, a missing exec-auth plugin, wrong context,
or something else entirely.
## kubectl credentials still failing after --context fix
Root cause: both extract_context() (kube.rs) and upload_kubeconfig() (shell.rs)
ignored the kubeconfig's current-context field and always picked contexts[0] from
the contexts array. If a kubeconfig has multiple contexts and current-context
points to entry N>0, we silently used the wrong context — one that may have empty
or expired credentials — causing the 401 "the server has asked for the client to
provide credentials" error on every kubectl call.
Fixes:
- extract_context(): read current-context field first; fall back to contexts[0]
only when current-context is absent or empty.
- extract_current_context_name(): new helper in kubeconfig.rs using the same
line-scanner approach as parse_kubeconfig_contexts (no extra dependencies).
- upload_kubeconfig(): use current-context to select the matching context entry
when storing context name in kubeconfig_files; falls back to first entry.
NOTE: existing kubeconfig rows in the database have the old (wrong) context
stored. Re-uploading kubeconfig files after deploying this build will fix them.
## Cluster dropdown still showing UUID
Root cause: SelectValue rendered ctx.value (the raw UUID passed to SelectItem's
value prop) instead of the display label (SelectItem's children). The custom
Select component had no mechanism to mirror a selected item's children into the
trigger area.
Fix: Select now builds a value→label Map by walking the children tree at render
time (collectLabels). The map is memoised on children. SelectValue reads the
display label from the map; if found, shows the label; otherwise falls back to
the raw value so existing behaviour is preserved for callers that don't need it.
1. kubectl credentials error (41 places in kube.rs)
Every kubectl invocation used .env("KUBERNETES_CONTEXT", context) which
is not a real kubectl environment variable — kubectl silently ignores it
and falls back to whatever current-context is set in the kubeconfig YAML.
If that context has expired or wrong credentials the auth failure occurs.
Replaced all 41 instances with .arg("--context").arg(context) so kubectl
always uses the correct context from the stored kubeconfig.
2. Cluster name still showed UUID (two causes)
a) Hotbar read from kubernetesStore.clusters (ClusterInfo[]) which is never
populated by the kubeconfig-based flow — always empty, so selectedCluster
was always undefined. Removed the Zustand cluster lookup from Hotbar and
added a clusterName prop passed from KubernetesPage.tsx (selectedConfig?.name).
b) ClusterOverview fell back to showing raw clusterId UUID when clusterName
was undefined. Changed subtitle to render conditionally so UUID never shows.
3. Bell dialog had no way to close
Custom DialogContent had no X button and no backdrop-click handler.
Added X close button (top-right) and backdrop-click-to-close.
4. Hotbar icons invisible in dark mode
variant="ghost" only styles hover state with no baseline text color.
Added className="text-foreground" to all icon-only ghost buttons.
Bug 1 — Dead multi-word tier3 entries / missing single-token commands
parse_single_command() extracts only the first token as `command`, so
multi-word entries like "kill -9", "init 0", "service stop" in the tier3
array never matched. Adding the single-token forms "kill", "pkill",
"killall", "init" to TIER3_COMMANDS ensures these commands are always
denied. Removed all dead multi-word entries.
Bug 2 — systemctl Tier 1 special case was dead code
systemctl was not in tier1_general, so the block that was supposed to
auto-execute `systemctl status` never ran. Moved systemctl handling into
its own block (TIER1_SYSTEMCTL_SUBCOMMANDS / TIER2_SYSTEMCTL_SUBCOMMANDS)
evaluated before the general tier checks. status, is-active, is-enabled,
list-units, list-unit-files → Tier 1; all others → Tier 2.
Bug 3 — ldapmodify / ldapdelete / ldapadd misclassified as Tier 1
Both appeared in the old tier1_general and tier2_general arrays; the tier1
check ran first, so LDAP write operations auto-executed. Removed them from
tier1. ldapsearch (read-only) remains Tier 1.
Dynamic Safety Architecture UI
Extracted all tier classification arrays to module-level pub const slices
(TIER3_COMMANDS, TIER1_KUBECTL_SUBCOMMANDS, etc.) so both the classifier
logic and a new get_classifier_rules() Tauri command share a single source
of truth. ShellExecution.tsx now calls getClassifierRulesCmd() on mount and
renders the actual command lists in collapsible per-tier cards — any change
to the const arrays is automatically reflected in the UI with no manual
documentation update needed.
Also fixes the cargo fmt CI failure introduced in the previous commit
(ClusterClient::new call reformatted to a single line).
Resolves four bugs in the Kubernetes management interface:
1. **Cluster not found error** - commands/kube.rs::list_nodes (and all other
kube resource commands) look up clusters from state.clusters (in-memory map)
which was never populated from the kubeconfig_files table. Add a new
connect_cluster_from_kubeconfig Tauri command that reads the encrypted
kubeconfig from the DB, decrypts it, and inserts a ClusterClient into
state.clusters. Wire it into KubernetesPage on initial load and cluster
change so the in-memory map is always populated before any kube command runs.
2. **Dropdown selection has no effect** - same root cause as #1; activating a
kubeconfig only updated the DB flag but never loaded the client into memory.
handleClusterChange now calls connectClusterFromKubeconfigCmd after activation.
3. **GUID shown instead of cluster name** - ClusterOverview displayed the raw
internal UUID as the page subtitle. Now accepts a clusterName prop (populated
from kubeconfig.context) and renders that instead. ClusterDetails similarly
changed to show kubeconfig.context in the header, not the UUID.
4. **Bell icon not clickable** - Hotbar bell button had no onClick handler. Add
optional onNotifications / notificationCount props; badge count is now dynamic
rather than hardcoded. KubernetesPage wires up a notifications dialog showing
active cluster context and a link to the Events section.
All changes follow TDD: failing tests written first, then implementation.
Complete overhaul of the Kubernetes management page from a basic config
panel into a full Lens-style IDE shell with 26 resource types, real-time
data, and a comprehensive test suite.
Layout & navigation:
- Rewrite KubernetesPage as a Lens v5-style shell: collapsible sidebar
(Workloads / Services & Networking / Config & Storage / Access Control /
Cluster), top hotbar with cluster+namespace selectors, Ctrl+K command
palette
- All 26 resource types now accessible via sidebar navigation (previously 5)
New resource types (Rust + TypeScript + React):
- StorageClasses, NetworkPolicies, ResourceQuotas, LimitRanges
- 4 new Tauri commands registered in generate_handler![]
Component implementations (replacing stubs with real IPC):
- Terminal: full xterm.js with multi-tab sessions and exec_pod IPC
- YamlEditor: Monaco editor with YAML syntax highlighting
- MetricsChart: recharts LineChart/BarChart
- ClusterOverview: live node/pod/deployment/namespace counts
- ClusterDetails: real kubeconfig + node data
- PodDetail, DeploymentDetail, ServiceDetail, ConfigMapDetail, SecretDetail:
all connected to real IPC data, zero hardcoded values
- CreateResourceModal, EditResourceModal: wired to createResourceCmd /
editResourceCmd
- RbacViewer: live data from 4 RBAC IPC commands
- RbacEditor: create roles/cluster-roles via YAML editor
- CommandPalette: 12 real navigation commands, keyboard nav
Dependencies added: xterm@5, xterm-addon-fit, xterm-addon-web-links,
@monaco-editor/react@4, recharts@2
Tooling:
- Replace eslint-plugin-react (incompatible with ESLint 10) with
@eslint-react/eslint-plugin; fix eslint.config.js for flat config
- Fix pre-existing hoisting lint errors in Security.tsx, PortForwardForm.tsx
- Fix eventBus.ts: replace all `any` generics with `unknown`
Tests: 251 passing across 35 test files (was 94/19)
- 16 new test files covering all new and fixed components (TDD)
- npx tsc --noEmit: 0 errors
- cargo clippy -- -D warnings: 0 warnings
- cargo fmt --check: passes
- eslint src/ --max-warnings 0: 0 issues
- Remove duplicate state.inner() calls in subscribe_to_k8s_events and
subscribe_to_all_k8s_events (copy-paste error)
- Share all AppState Arc fields in OAuth callback task — clusters,
port_forwards, refresh_registry, and watchers were previously
constructed as fresh isolated instances instead of being cloned from
the live AppState
- Replace infinite sleep loop in Watcher::start with an immediate
warn-and-return, preventing Tokio thread leaks from stub watchers