Add macos-desktop-client.yml workflow with manual dispatch trigger running on macos-arm64 runner. Install Node.js 22 and Rust toolchain with aarch64-apple-darwin target. Build bundled tunnel helper and Tauri application bundle, then upload .app, .dmg, and raw build artifacts.
Fix tunnel_metrics handler to clone app handle before spawn_blocking to prevent ownership issues when passing to tunnel
Add ServiceCatalogItem type and services CRUD API endpoints (list, create, update, delete). Extend Policy type to include services array with domain, upstream_ip, proxy_ip, and ports metadata.
Add ServicesPage component with table view and create/edit modals for managing service definitions. Include service name, domain, proxy, and upstream columns with port parsing logic.
Integrate service selection
Move tunnel_manager::connect and disconnect calls into spawn_blocking tasks to prevent blocking async runtime. Clone app handle and profile path before spawning. Add map_err for task join failures.
Add tunnelActionPending state to track in-progress tunnel operations. Pass busy prop to AppHeader and disable sync/logout/connect buttons during tunnel actions. Update connect button text to show "
Add SelectOwnProfile handler to allow users to choose from available access profiles. Store selected profile ID per device in settings table with device_access_profile category. Implement GetSelectedProfileID and SetSelectedProfileID repository methods using JSONB storage.
Add ListSelectableProfiles to policy repository and service to query user/group/device-specific profiles ordered by priority. Filter gateway
Reduce default window dimensions from 1120x760 to 940x640 pixels and disable resizing. Remove TunnelMetrics and RawTunnelMetrics types, formatDataSize and normalizeTunnelMetrics helpers, and all transfer statistics tracking from App component. Replace refreshTunnelMetrics with simpler refreshTunnelStatus that only queries tunnel active state. Remove received/sent data display cards from status panel and eliminate metrics
Add png dependency and tauri image-png feature to support custom tray icon rendering. Load base disconnected icon from bundled PNG and generate connected variant with green circular badge containing white checkmark overlay. Implement draw_check_badge, draw_line, and blend_pixel helpers using Bresenham's line algorithm for badge rendering. Store both icon variants and TrayIcon reference in TrayState and update icon
Replace tauri::async_runtime::spawn with std::thread::spawn for periodic tray menu refresh background task and change async sleep to blocking thread sleep. Prefix unused state parameter with underscore in sync_profile command to suppress compiler warnings.
Add background task to refresh tray menu every 5 seconds to keep status display current. Add RawTunnelMetrics type and normalizeTunnelMetrics helper to handle both snake_case and camelCase field names from backend responses. Update refreshTunnelMetrics to normalize metrics before setting state and explicitly cast active status to boolean.
Replace metrics query in is_active with direct tunnel_backend status command call to avoid unnecessary metrics overhead when only checking tunnel state. Parse status command stdout and compare against "active" string case-insensitively. Add Windows CREATE_NO_WINDOW flag to status command execution.
Add fallback to tunnel_status when metrics query fails in current_metrics function, returning zero bytes with actual tunnel state. Update waitForTunnelStatus in frontend to use tunnel_status instead of tunnel_metrics for status polling and refresh metrics separately on success. Change CloseRequested window event handler to call app_handle().exit(0) instead of no-op. Replace "sc" with "sc.exe" in all Windows service command
Remove direct_windows_metrics, read_windows_metrics_from_show, parse_human_wireguard_bytes, and find_windows_wg functions from tunnel_manager.rs to rely on bundled backend for all metrics queries. Update find_wg_cli in tunnel-helper to return "wg.exe" as fallback when WireGuard installation paths don't exist, removing "wg" from candidate list.
Add RefreshRequest struct for token refresh API calls. Update sync_current_session to detect 401 responses and automatically refresh access tokens using refresh token before retrying profile sync. Store refreshed access and refresh tokens in existing session state. Extract profile URL to variable for reuse in retry logic.
Add PredefinedMenuItem import and create separator items to visually group tray menu sections. Update menu item labels from "Open/Quit NexaVPN" to "Open/Quit NexaVPN Client" for clarity. Add separators around toggle item to separate status display from actions. Add no-op event handlers for status, received, and sent menu items to prevent unintended interactions with display-only elements.
Add applyCurrentPolicy function to resolve and apply policy destinations to enrollment responses with fallback to 172.16.10.0/24 when no destinations exist. Replace withDebugProfile calls with applyCurrentPolicy in GetLatestEnrollmentByUser and GetEnrollmentByDeviceID. Extract sync_current_session helper function to deduplicate profile sync logic between sync_profile and connect_tunnel commands. Update connect
Add TrayState struct to track menu items for status, received/sent bytes, and connection toggle. Add format_data_size helper to convert bytes to human-readable units (B, KB, MB, GB, TB). Add current_metrics, update_tray_menu, refresh_tray_menu, and toggle_tray_connection functions to manage tray state. Update tray menu to include status, received, sent, and toggle items. Call refresh_tray_menu after enroll_device
Add read_windows_metrics_from_show function that parses human-readable transfer output from wg show command when wg show dump fails. Add parse_human_wireguard_bytes helper to convert human-readable byte values (B, KiB, MiB, GiB, TiB) to u64. Update direct_windows_metrics to fall back to transfer parsing instead of returning zero metrics when dump command fails.
Add direct_windows_metrics function that queries WireGuard tunnel metrics directly using sc query and wg show dump commands instead of tunnel helper. Add find_windows_wg helper to locate wg.exe in standard installation paths. Update metrics function to attempt direct collection first on Windows before falling back to tunnel helper. Parse rx_bytes and tx_bytes from wg show dump output by sum
Add CREATE_NO_WINDOW flag to all tunnel helper Command invocations on Windows to prevent console window flashing during connect, disconnect, and metrics operations. Import CommandExt trait and define CREATE_NO_WINDOW constant for Windows builds.
Add NSIS_HOOK_PREINSTALL macro that stops WireGuardTunnel$NexaVPN service, kills nexavpn-desktop.exe and nexavpn-tunnel-helper.exe processes, and stops NexaVPNTunnelService before installation. Add WireGuardTunnel$NexaVPN service stop to NSIS_HOOK_PREUNINSTALL before service uninstallation.
Add rx_bytes and tx_bytes fields to Device type and API responses. Add formatDataSize helper for human-readable byte formatting with units from B to TB. Add Received and Sent columns to devices table in admin UI with formatted traffic totals. Add traffic metrics display to device action panel.
Add TelemetrySnapshot and PeerTelemetry types for gateway runtime stats. Add gateway telemetry endpoint at POST /gateway
Store generated private key in SessionState and persist across enrollment and profile sync operations. Add materialize_profile helper that replaces placeholder tokens (__CLIENT_GENERATED_PRIVATE_KEY__ and __CLIENT_PRIVATE_KEY_REQUIRED__) with actual private key before writing profile to disk. Update enroll_device and sync_profile to materialize profile content with private key before writing.
Change NSIS installer hooks to use bundled/ instead of resources/bundled/ path for tunnel helper executable. Add waitForTunnelStatus helper that polls tunnel status up to 8 times with 500ms intervals to verify expected state after connect/disconnect operations. Update toggle handler to use polling instead of single status check and add error message for failed disconnect operations.
Add single instance check using TCP listener on 127.0.0.1:53190 to prevent multiple application instances. Move AppState initialization into setup closure to include single_instance_lock field. Remove window close prevention and focus restoration handlers. Make main window non-resizable and non-maximizable.
Add tunnel_status command to desktop client for querying active tunnel state. Add is_active method to tunnel_manager that calls status command on bundled backend. Add status command to tunnel-helper that checks WireGuard service state on Windows via sc query and interface state on macOS via wg show. Add windows_client_status function for IPC-based status queries with active field in TunnelResponse. Update App.tsx to query tunnel status on
Add Windows service to handle WireGuard tunnel operations with elevated privileges. Implement IPC server on TCP port 53189 for client-service communication using JSON protocol. Add install-service and uninstall-service commands to NSIS installer hooks for automatic service installation. Replace direct WireGuard calls with IPC requests when running on Windows. Add TunnelRequest and TunnelResponse types for IPC protocol
Replace Unix timestamp with "Just now" label in now_label helper. Update profile label from "No profile provisioned" to "Not provisioned". Change brand copy subtitle based on enrollment state. Rename "Current profile" to "Overview" and "Profile" to "Access" in status panel. Remove "Stored config" surface section showing profile path and revision. Update resources sidebar instructions and rename resource-list to resource-
Add clear_session command to remove session state and profile files from disk. Add resetEnrollment handler in frontend to clear local state and invoke clear_session. Remove hero surface section with profile metadata tiles. Simplify top strip to show profile label in brand copy when enrolled. Add Logout button to top actions and resources sidebar. Redesign status panel with simplified labels and layout. Update surface
Rename restore_main_window to restore_webview_window for WebviewWindow type. Add new restore_window helper for generic Window type. Update tray menu and click handlers to use restore_webview_window. Add WindowEvent::Focused handler to restore window state when focused.
Add sync_profile command to fetch latest profile from backend without re-enrollment. Add DeviceView struct to EnrollResponse. Replace hardcoded "just now" timestamp with now_label helper using Unix epoch seconds. Add sync button to UI with loading state. Redesign client interface with top strip containing brand lockup and action buttons, hero surface with profile metadata tiles, body grid with login/status panels and resources sidebar
Add updateUser and deleteUser API client methods with PATCH and DELETE endpoints. Add updatePolicy and deletePolicy API client methods. Add email field to User type. Add Actions column to users and policies tables with Edit and Delete buttons. Implement inline edit forms for users and policies with state management for editing mode. Add update and delete mutations with query invalidation on success. Add error notices
Enable tray-icon feature in Tauri dependencies. Add system tray with Open and Quit menu items. Implement tray icon click handlers to restore main window. Add window event handlers to hide window on close/minimize instead of exiting application. Add restore_main_window and hide_main_window helper functions for window visibility management.
Add formatInvokeError helper function to handle various error types from Tauri invoke calls with fallback messages. Update enroll_device to include response body in error message when enrollment fails with non-success status. Add windows_subsystem attribute to main.rs to suppress console window in release builds on Windows.
Add NexaVPN logo images (full logo and mark-only variants) to admin-web and desktop-client public directories. Add favicon.ico and favicon.png to admin-web, and icon.png to desktop-client. Update index.html files to reference favicon assets. Add icon.png and icon.ico to desktop-client Tauri icons directory and configure bundle.icon in tauri.conf.json. Update Layout component to display logo in sidebar brand-block with
Update @tauri-apps/api and @tauri-apps/cli to version 2.10.1, and tauri runtime to 2.10.1 for consistency across desktop client dependencies. Update tauri-build to version 2.5.5.
Update all Windows build configurations, scripts, and documentation from i686-pc-windows-msvc (x86) to x86_64-pc-windows-msvc (x64). Update npm scripts, build-tunnel-helper.sh target validation, bundled helper paths, and tunnel manager strategy references. Add XWIN_ARCH=x86_64 environment variable to Linux cross-build command and --xwin-arch flag to cargo xwin invocation.
Add Ubuntu-to-Windows build workflow using cargo-xwin for i686-pc-windows-msvc target. Update build-tunnel-helper.sh with host OS detection, target validation, and cargo-xwin support for Linux cross-compilation. Add tauri.windows.conf.json to configure NSIS-only bundle output and npm script for Linux-based Windows builds. Update client-platforms.md to document cross-compilation support and clarify that MSI packaging still requires Windows environment.
Add monorepo structure for NexaVPN WireGuard control plane including:
- .gitignore for node_modules, build artifacts, and environment files
- README with project overview, monorepo layout, and quick start guide
- Admin web UI with React, Vite, TypeScript, and nginx reverse proxy
- API client with type definitions for users, devices, policies, gateways, and audit logs
- Admin pages for dashboard, users, devices, policies, g