- Add get_test_client() helper to reduce test duplication
- Use PROXMOX_HOST environment variable for configurable server address
- Add PROXMOX_HOST to ~/.bashrc with default value
- Removed incorrect #[serde(rename_all = "PascalCase")] attribute from AuthResponse struct
- Proxmox API returns lowercase fields (ticket, username, clustername) not PascalCase
- Added missing clustername field to AuthResponse struct
- Updated unit tests to match actual Proxmox API response format
- Added 4 integration tests for Proxmox API endpoints:
* test_real_proxmox_auth - verifies authentication works
* test_real_proxmox_cluster_resources - fetches cluster resources
* test_real_proxmox_nodes - fetches node status
* test_real_proxmox_vms - fetches VM list
- All 432 Rust tests passing
- All integration tests verified against https://172.0.0.18:8006
handle_response() in client.rs already strips the {"data":...} wrapper
before returning to callers. Every proxmox module was calling .get("data")
a second time on the already-unwrapped Value, which always returned None
and caused all API responses to silently yield empty results or errors.
vm.rs had an additional bug: list_vms used POST on cluster/resources (a
GET-only endpoint) and dropped VMs with no cpu field via filter_map ?
instead of unwrap_or(0.0). Both corrected.
Affected modules: vm, ceph, ceph_cluster, certificates, acme, firewall,
sdn, ha, apt, updates, updates_ext, tasks, migration, metrics, shell,
auth_realm, views, backup — 18 files, 19 functions.
426 Rust tests pass. clippy -D warnings clean. tsc --noEmit clean.
Root cause: authenticate() tried to deserialize the Proxmox API response
directly into AuthResponse, but Proxmox wraps every response in
{"data": {...}}. This caused every reconnect attempt after app restart
to fail silently.
Additional fixes bundled in this commit:
- add_proxmox_cluster now authenticates immediately so the in-memory pool
always contains a live, ticketed client (not a bare unauthenticated stub)
- ProxmoxClient stores the CSRFPreventionToken and includes it in the
CSRFPreventionToken header on POST/PUT/DELETE requests (Proxmox requires
this for all mutating calls)
- accept-invalid-certs enabled on the reqwest Client so self-signed PVE
certificates do not block connections
- Removed double-unwrap of the data field in 10 commands (list_acls,
list_users, get_cluster_notes, search_proxmox_resources, get_node_status,
get_syslog, list_network_interfaces, get_subscription_status,
list_cluster_tasks, list_proxmox_containers) — handle_response already
strips the envelope before returning to callers
- Added connect_proxmox_cluster and disconnect_proxmox_cluster Tauri
commands so the UI can explicitly connect/disconnect sessions
- Wired RemotesPage Connect/Disconnect buttons to the real backend commands
- Updated and added tests covering envelope parsing, CSRF header logic,
already-unwrapped response handling, and the new connect/disconnect paths
Race condition in get_proxmox_client_for_cluster: two concurrent callers
for an uncached cluster could both authenticate and insert, with the second
overwriting the first. Re-check under write lock before inserting so the
later caller returns the already-stored client instead of overwriting it.
handleConnectRemote used getProxmoxCluster (a DB-only lookup) to set status
'connected', which passed even when the Proxmox API was unreachable. Replace
with pingProxmoxCluster, a new command that authenticates and calls
GET /api2/json/version, providing a real end-to-end connectivity test.
handleEditRemote used remove-then-add, leaving a gap where the record was
absent and silently lost if addProxmoxCluster failed. Replace with
updateProxmoxCluster, a new command that issues a single SQL UPDATE (plus
in-memory pool eviction) so the record is never transiently missing.
ActionsMenu useEffect added the mousedown listener only when open=true but
the dependency array contained open, causing ambiguity about cleanup timing.
Attach the listener unconditionally on mount (empty dep array) so there is
always exactly one add and one remove with no conditional branches.
New Rust tests cover update_proxmox_cluster not-found logic and ping error
message format (420 Rust + 386 frontend, zero failures).
Half-completed refactor left 68 Tauri command functions with orphaned
.ok_or_else() chains after the old clusters.get() pattern was removed
without inserting the replacement helper call. Also fixed two bugs in the
new get_proxmox_client_for_cluster helper: undeclared `clusters` variable
in the early-return check, and client_arc going out of scope before return.
fix(ai): enforce system-message-first ordering for strict LLM providers
Qwen3.5-122b (and other models via LiteLLM) reject requests where system
messages appear after user/assistant turns. Moved tool-calling format
and iteration-budget system messages to before history is appended.
Changed mid-loop iteration warning and forced-stop instruction from
system role to user role so they can safely appear mid-conversation.
fix(proxmox): Remotes actions menu and connect/disconnect behaviour
Replaced the non-functional "..." toast placeholder with a proper
ActionsMenu dropdown (Edit / Test Connection / Delete). Removed inline
emoji buttons folded into the menu. Connect now calls getProxmoxCluster
as a live connection test and reflects real status; disconnect marks the
remote disconnected locally. Remote status now maps correctly from the
backend ClusterInfoWithHealth.connected field instead of hardcoding
'connected' for every entry.
fix(proxmox): Ceph page no longer shows HEALTH_OK on non-Ceph clusters
Page now fetches real health data on mount. If getCephHealth fails the
page renders an informational notice rather than fake HEALTH_OK. When
Ceph is present, pools and OSDs are loaded and displayed live.
- Change password parameter from &str to String in add_proxmox_cluster
so Tauri v2 IPC can deserialize it (requires DeserializeOwned + Send)
- Construct full https:// URL with port in ProxmoxClient::get_api_url()
and authenticate() since the frontend strips the protocol before
sending the hostname
- Show actual error string in AddRemoteForm instead of generic fallback
(Tauri errors are plain strings, not Error instances)
- Update client tests to reflect bare-hostname input from the frontend
compile("memset_shim") produces libmemset_shim.a, not memset_shim.o, so the
previous cargo:rustc-link-arg pointed at a file that never exists. Switching to
get_compiler().to_command() compiles memset_s_shim.c directly to a .o file at
a known OUT_DIR path and passes it as a positional linker arg. A positional .o
is always included unconditionally, which also resolves the archive-extraction
ordering issue where -l flags only pull symbols that are already undefined at
that point in the link command.
- Use rustc-link-arg to directly link memset_shim.o object file
instead of -l flag, ensuring symbol is available regardless of
link order with libsodium_sys
- Add #[allow(dead_code)] to find_ollama_binary() which is only
used inside cfg(target_os = "macos") and cfg(target_os = "linux")
blocks, causing false positive warning when cross-compiling for Windows
- Add __MINGW32__ detection in memset_s_shim.c for proper EXPORT macro
- Add WIN32 and __WIN32__ defines when compiling the shim in build.rs
- The memset_explicit symbol is required by libsodium-sys-stable but not
available in MinGW runtime
- The shim is compiled as a static library and linked before libsodium
- Add __declspec(dllexport) to memset_explicit in memset_s_shim.c
- Explicitly link memset_shim library in build.rs for Windows MinGW target
- Fixes undefined reference to memset_explicit when building for x86_64-pc-windows-gnu
Cargo injects [env] entries directly into build script environments,
bypassing shell-level unset and env -u entirely. SODIUM_USE_PKG_CONFIG
was set to "0" in [env], which libsodium-sys-stable build.rs treats as
"pkg-config enabled" (env::var().is_ok() returns true for any value
including "0"). Combined with SODIUM_LIB_DIR set in the workflow, this
triggered the incompatibility panic on every build.
The original comment claiming this "avoids memset_explicit on Windows"
was incorrect — SODIUM_USE_PKG_CONFIG controls detection strategy, not
link behaviour. The actual Windows memset_explicit fix is handled via
SODIUM_LIB_DIR pointing to pre-built MinGW sodium.
The use-pkg-config feature was added in the previous PR and conflicts
with SODIUM_LIB_DIR. The build script errors with:
"SODIUM_LIB_DIR is incompatible with SODIUM_USE_PKG_CONFIG.
Set the only one env variable"
Removed the explicit libsodium-sys-stable dependency since we're using
the SODIUM_LIB_DIR environment variable approach instead.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Explicitly adds libsodium-sys-stable dependency with use-pkg-config
feature to ensure builds use system libsodium via pkg-config instead
of attempting vendored builds.
Changes:
- Add direct dependency on libsodium-sys-stable with use-pkg-config feature
- Update Windows build to use pkg-config with cross-compilation support
- Remove manual SODIUM_* env vars that bypass pkg-config
This resolves "libsodium not found via pkg-config or vcpkg" build
failures on Linux and Windows CI targets.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add libsodium-dev to Docker build images and configure Windows cross-build
environment to fix CI build failures on all platforms (Linux amd64/arm64,
Windows amd64). Failures were caused by missing libsodium dependency
required by tauri-plugin-stronghold → iota-crypto → libsodium-sys-stable.
Changes:
- Install libsodium-dev in Linux amd64 and arm64 Docker images
- Set SODIUM_LIB_DIR and SODIUM_STATIC env vars for Windows cross-build
- Add smoke test to verify libsodium linking via stronghold dependency
- Add comprehensive test coverage (3 new tests in state module)
All 802 tests pass (416 Rust + 386 TypeScript). Zero linting warnings.
Fixes: Linux "libsodium not found via pkg-config" error
Fixes: Windows "SODIUM_LIB_DIR incompatible with SODIUM_USE_PKG_CONFIG" error
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
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>