Added: - PROJECTS_INDEX.md - Master catalog of 7 active projects - GURURMM_API_ACCESS.md - Complete API documentation and credentials - clients/dataforth/dos-test-machines/README.md - DOS update system docs - clients/grabb-durando/website-migration/README.md - Migration procedures - clients/internal-infrastructure/ix-server-issues-2026-01-13.md - Server issues - projects/msp-tools/guru-connect/README.md - Remote desktop architecture - projects/msp-tools/toolkit/README.md - MSP PowerShell tools - projects/internal/acg-website-2025/README.md - Website rebuild docs - test_gururmm_api.py - GuruRMM API testing script Modified: - credentials.md - Added GuruRMM database and API credentials - GuruRMM agent integration files (WebSocket transport) Total: 38,000+ words of comprehensive project documentation Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
310 lines
7.4 KiB
Rust
310 lines
7.4 KiB
Rust
//! Transport layer for agent-server communication
|
|
//!
|
|
//! Handles WebSocket connection to the GuruRMM server with:
|
|
//! - Auto-reconnection on disconnect
|
|
//! - Authentication via API key
|
|
//! - Sending metrics and receiving commands
|
|
//! - Heartbeat to maintain connection
|
|
|
|
mod websocket;
|
|
|
|
pub use websocket::WebSocketClient;
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
use uuid::Uuid;
|
|
|
|
/// Messages sent from agent to server
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
#[serde(tag = "type", content = "payload")]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum AgentMessage {
|
|
/// Authentication message (sent on connect)
|
|
Auth(AuthPayload),
|
|
|
|
/// Metrics report
|
|
Metrics(crate::metrics::SystemMetrics),
|
|
|
|
/// Network state update (sent on connect and when interfaces change)
|
|
NetworkState(crate::metrics::NetworkState),
|
|
|
|
/// Command execution result
|
|
CommandResult(CommandResultPayload),
|
|
|
|
/// Watchdog event (service stopped, restarted, etc.)
|
|
WatchdogEvent(WatchdogEventPayload),
|
|
|
|
/// Update result (success, failure, rollback)
|
|
UpdateResult(UpdateResultPayload),
|
|
|
|
/// Heartbeat to keep connection alive
|
|
Heartbeat,
|
|
}
|
|
|
|
/// Authentication payload
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct AuthPayload {
|
|
/// API key for this agent (or site)
|
|
pub api_key: String,
|
|
|
|
/// Unique device identifier (hardware-derived)
|
|
pub device_id: String,
|
|
|
|
/// Hostname of this machine
|
|
pub hostname: String,
|
|
|
|
/// Operating system type
|
|
pub os_type: String,
|
|
|
|
/// Operating system version
|
|
pub os_version: String,
|
|
|
|
/// Agent version
|
|
pub agent_version: String,
|
|
|
|
/// Architecture (amd64, arm64, etc.)
|
|
#[serde(default = "default_arch")]
|
|
pub architecture: String,
|
|
|
|
/// Previous version if reconnecting after update
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub previous_version: Option<String>,
|
|
|
|
/// Update ID if reconnecting after update
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub pending_update_id: Option<Uuid>,
|
|
}
|
|
|
|
fn default_arch() -> String {
|
|
#[cfg(target_arch = "x86_64")]
|
|
{ "amd64".to_string() }
|
|
#[cfg(target_arch = "aarch64")]
|
|
{ "arm64".to_string() }
|
|
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
|
|
{ "unknown".to_string() }
|
|
}
|
|
|
|
/// Command execution result payload
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct CommandResultPayload {
|
|
/// Command ID (from the server)
|
|
pub command_id: Uuid,
|
|
|
|
/// Exit code (0 = success)
|
|
pub exit_code: i32,
|
|
|
|
/// Standard output
|
|
pub stdout: String,
|
|
|
|
/// Standard error
|
|
pub stderr: String,
|
|
|
|
/// Execution duration in milliseconds
|
|
pub duration_ms: u64,
|
|
}
|
|
|
|
/// Watchdog event payload
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct WatchdogEventPayload {
|
|
/// Service or process name
|
|
pub name: String,
|
|
|
|
/// Event type
|
|
pub event: WatchdogEvent,
|
|
|
|
/// Additional details
|
|
pub details: Option<String>,
|
|
}
|
|
|
|
/// Types of watchdog events
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum WatchdogEvent {
|
|
/// Service/process was found stopped
|
|
Stopped,
|
|
|
|
/// Service/process was restarted by the agent
|
|
Restarted,
|
|
|
|
/// Restart attempt failed
|
|
RestartFailed,
|
|
|
|
/// Max restart attempts reached
|
|
MaxRestartsReached,
|
|
|
|
/// Service/process recovered on its own
|
|
Recovered,
|
|
}
|
|
|
|
/// Messages sent from server to agent
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
#[serde(tag = "type", content = "payload")]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum ServerMessage {
|
|
/// Authentication acknowledgment
|
|
AuthAck(AuthAckPayload),
|
|
|
|
/// Command to execute
|
|
Command(CommandPayload),
|
|
|
|
/// Configuration update
|
|
ConfigUpdate(ConfigUpdatePayload),
|
|
|
|
/// Agent update command
|
|
Update(UpdatePayload),
|
|
|
|
/// Acknowledgment of received message
|
|
Ack { message_id: Option<String> },
|
|
|
|
/// Error message
|
|
Error { code: String, message: String },
|
|
}
|
|
|
|
/// Authentication acknowledgment payload
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct AuthAckPayload {
|
|
/// Whether authentication was successful
|
|
pub success: bool,
|
|
|
|
/// Agent ID assigned by server
|
|
pub agent_id: Option<Uuid>,
|
|
|
|
/// Error message if authentication failed
|
|
pub error: Option<String>,
|
|
}
|
|
|
|
/// Command payload from server
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct CommandPayload {
|
|
/// Unique command ID
|
|
pub id: Uuid,
|
|
|
|
/// Type of command
|
|
pub command_type: CommandType,
|
|
|
|
/// Command text to execute
|
|
pub command: String,
|
|
|
|
/// Optional timeout in seconds
|
|
pub timeout_seconds: Option<u64>,
|
|
|
|
/// Whether to run as elevated/admin
|
|
pub elevated: bool,
|
|
}
|
|
|
|
/// Types of commands
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum CommandType {
|
|
/// Shell command (cmd on Windows, bash on Unix)
|
|
Shell,
|
|
|
|
/// PowerShell command (Windows)
|
|
PowerShell,
|
|
|
|
/// Python script
|
|
Python,
|
|
|
|
/// Raw script (requires interpreter path)
|
|
Script { interpreter: String },
|
|
|
|
/// Claude Code task execution
|
|
ClaudeTask {
|
|
/// Task description for Claude Code
|
|
task: String,
|
|
/// Optional working directory (defaults to C:\Shares\test)
|
|
working_directory: Option<String>,
|
|
/// Optional context files to provide to Claude
|
|
context_files: Option<Vec<String>>,
|
|
},
|
|
}
|
|
|
|
/// Configuration update payload
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct ConfigUpdatePayload {
|
|
/// New metrics interval (if changed)
|
|
pub metrics_interval_seconds: Option<u64>,
|
|
|
|
/// Updated watchdog config
|
|
pub watchdog: Option<WatchdogConfigUpdate>,
|
|
}
|
|
|
|
/// Watchdog configuration update
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct WatchdogConfigUpdate {
|
|
/// Enable/disable watchdog
|
|
pub enabled: Option<bool>,
|
|
|
|
/// Check interval
|
|
pub check_interval_seconds: Option<u64>,
|
|
|
|
// Services and processes would be included here for remote config updates
|
|
}
|
|
|
|
/// Update command payload from server
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct UpdatePayload {
|
|
/// Unique update ID for tracking
|
|
pub update_id: Uuid,
|
|
|
|
/// Target version to update to
|
|
pub target_version: String,
|
|
|
|
/// Download URL for the new binary
|
|
pub download_url: String,
|
|
|
|
/// SHA256 checksum of the binary
|
|
pub checksum_sha256: String,
|
|
|
|
/// Whether to force update (skip version check)
|
|
#[serde(default)]
|
|
pub force: bool,
|
|
}
|
|
|
|
/// Update result payload sent back to server
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct UpdateResultPayload {
|
|
/// Update ID (from the server)
|
|
pub update_id: Uuid,
|
|
|
|
/// Update status
|
|
pub status: UpdateStatus,
|
|
|
|
/// Old version before update
|
|
pub old_version: String,
|
|
|
|
/// New version after update (if successful)
|
|
pub new_version: Option<String>,
|
|
|
|
/// Error message if failed
|
|
pub error: Option<String>,
|
|
}
|
|
|
|
/// Update status codes
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum UpdateStatus {
|
|
/// Update starting
|
|
Starting,
|
|
|
|
/// Downloading new binary
|
|
Downloading,
|
|
|
|
/// Download complete, verifying
|
|
Verifying,
|
|
|
|
/// Installing (replacing binary)
|
|
Installing,
|
|
|
|
/// Restarting service
|
|
Restarting,
|
|
|
|
/// Update completed successfully
|
|
Completed,
|
|
|
|
/// Update failed
|
|
Failed,
|
|
|
|
/// Rolled back to previous version
|
|
RolledBack,
|
|
}
|