     1→//! Support session codes management
     2→//!
     3→//! Handles generation and validation of 6-digit support codes
     4→//! for one-time remote support sessions.
     5→
     6→use std::collections::HashMap;
     7→use std::sync::Arc;
     8→use tokio::sync::RwLock;
     9→use chrono::{DateTime, Utc};
    10→use rand::Rng;
    11→use serde::{Deserialize, Serialize};
    12→use uuid::Uuid;
    13→
    14→/// A support session code
    15→#[derive(Debug, Clone, Serialize)]
    16→pub struct SupportCode {
    17→    pub code: String,
    18→    pub session_id: Uuid,
    19→    pub created_by: String,
    20→    pub created_at: DateTime<Utc>,
    21→    pub status: CodeStatus,
    22→    pub client_name: Option<String>,
    23→    pub client_machine: Option<String>,
    24→    pub connected_at: Option<DateTime<Utc>>,
    25→}
    26→
    27→#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
    28→#[serde(rename_all = "lowercase")]
    29→pub enum CodeStatus {
    30→    Pending,    // Waiting for client to connect
    31→    Connected,  // Client connected, session active
    32→    Completed,  // Session ended normally
    33→    Cancelled,  // Code cancelled by tech
    34→}
    35→
    36→/// Request to create a new support code
    37→#[derive(Debug, Deserialize)]
    38→pub struct CreateCodeRequest {
    39→    pub technician_id: Option<String>,
    40→    pub technician_name: Option<String>,
    41→}
    42→
    43→/// Response when a code is validated
    44→#[derive(Debug, Serialize)]
    45→pub struct CodeValidation {
    46→    pub valid: bool,
    47→    pub session_id: Option<String>,
    48→    pub server_url: Option<String>,
    49→    #[serde(skip_serializing_if = "Option::is_none")]
    50→    pub error: Option<String>,
    51→}
    52→
    53→/// Manages support codes
    54→#[derive(Clone)]
    55→pub struct SupportCodeManager {
    56→    codes: Arc<RwLock<HashMap<String, SupportCode>>>,
    57→    session_to_code: Arc<RwLock<HashMap<Uuid, String>>>,
    58→}
    59→
    60→impl SupportCodeManager {
    61→    pub fn new() -> Self {
    62→        Self {
    63→            codes: Arc::new(RwLock::new(HashMap::new())),
    64→            session_to_code: Arc::new(RwLock::new(HashMap::new())),
    65→        }
    66→    }
    67→
    68→    /// Generate a unique 6-digit code
    69→    async fn generate_unique_code(&self) -> String {
    70→        let codes = self.codes.read().await;
    71→        let mut rng = rand::thread_rng();
    72→
    73→        loop {
    74→            let code: u32 = rng.gen_range(100000..999999);
    75→            let code_str = code.to_string();
    76→
    77→            if !codes.contains_key(&code_str) {
    78→                return code_str;
    79→            }
    80→        }
    81→    }
    82→
    83→    /// Create a new support code
    84→    pub async fn create_code(&self, request: CreateCodeRequest) -> SupportCode {
    85→        let code = self.generate_unique_code().await;
    86→        let session_id = Uuid::new_v4();
    87→
    88→        let support_code = SupportCode {
    89→            code: code.clone(),
    90→            session_id,
    91→            created_by: request.technician_name.unwrap_or_else(|| "Unknown".to_string()),
    92→            created_at: Utc::now(),
    93→            status: CodeStatus::Pending,
    94→            client_name: None,
    95→            client_machine: None,
    96→            connected_at: None,
    97→        };
    98→
    99→        let mut codes = self.codes.write().await;
   100→        codes.insert(code.clone(), support_code.clone());
   101→
   102→        let mut session_to_code = self.session_to_code.write().await;
   103→        session_to_code.insert(session_id, code);
   104→
   105→        support_code
   106→    }
   107→
   108→    /// Validate a code and return session info
   109→    pub async fn validate_code(&self, code: &str) -> CodeValidation {
   110→        let codes = self.codes.read().await;
   111→
   112→        match codes.get(code) {
   113→            Some(support_code) => {
   114→                if support_code.status == CodeStatus::Pending || support_code.status == CodeStatus::Connected {
   115→                    CodeValidation {
   116→                        valid: true,
   117→                        session_id: Some(support_code.session_id.to_string()),
   118→                        server_url: Some("wss://connect.azcomputerguru.com/ws/support".to_string()),
   119→                        error: None,
   120→                    }
   121→                } else {
   122→                    CodeValidation {
   123→                        valid: false,
   124→                        session_id: None,
   125→                        server_url: None,
   126→                        error: Some("This code has expired or been used".to_string()),
   127→                    }
   128→                }
   129→            }
   130→            None => CodeValidation {
   131→                valid: false,
   132→                session_id: None,
   133→                server_url: None,
   134→                error: Some("Invalid code".to_string()),
   135→            },
   136→        }
   137→    }
   138→
   139→    /// Mark a code as connected
   140→    pub async fn mark_connected(&self, code: &str, client_name: Option<String>, client_machine: Option<String>) {
   141→        let mut codes = self.codes.write().await;
   142→        if let Some(support_code) = codes.get_mut(code) {
   143→            support_code.status = CodeStatus::Connected;
   144→            support_code.client_name = client_name;
   145→            support_code.client_machine = client_machine;
   146→            support_code.connected_at = Some(Utc::now());
   147→        }
   148→    }
   149→
   150→    /// Link a support code to an actual WebSocket session
   151→    pub async fn link_session(&self, code: &str, real_session_id: Uuid) {
   152→        let mut codes = self.codes.write().await;
   153→        if let Some(support_code) = codes.get_mut(code) {
   154→            // Update session_to_code mapping with real session ID
   155→            let old_session_id = support_code.session_id;
   156→            support_code.session_id = real_session_id;
   157→
   158→            // Update the reverse mapping
   159→            let mut session_to_code = self.session_to_code.write().await;
   160→            session_to_code.remove(&old_session_id);
   161→            session_to_code.insert(real_session_id, code.to_string());
   162→        }
   163→    }
   164→
   165→    /// Get code by its code string
   166→    pub async fn get_code(&self, code: &str) -> Option<SupportCode> {
   167→        let codes = self.codes.read().await;
   168→        codes.get(code).cloned()
   169→    }
   170→
   171→    /// Mark a code as completed
   172→    pub async fn mark_completed(&self, code: &str) {
   173→        let mut codes = self.codes.write().await;
   174→        if let Some(support_code) = codes.get_mut(code) {
   175→            support_code.status = CodeStatus::Completed;
   176→        }
   177→    }
   178→
   179→    /// Cancel a code (works for both pending and connected)
   180→    pub async fn cancel_code(&self, code: &str) -> bool {
   181→        let mut codes = self.codes.write().await;
   182→        if let Some(support_code) = codes.get_mut(code) {
   183→            if support_code.status == CodeStatus::Pending || support_code.status == CodeStatus::Connected {
   184→                support_code.status = CodeStatus::Cancelled;
   185→                return true;
   186→            }
   187→        }
   188→        false
   189→    }
   190→
   191→    /// Check if a code is cancelled
   192→    pub async fn is_cancelled(&self, code: &str) -> bool {
   193→        let codes = self.codes.read().await;
   194→        codes.get(code).map(|c| c.status == CodeStatus::Cancelled).unwrap_or(false)
   195→    }
   196→
   197→    /// Check if a code is valid for connection (exists and is pending)
   198→    pub async fn is_valid_for_connection(&self, code: &str) -> bool {
   199→        let codes = self.codes.read().await;
   200→        codes.get(code).map(|c| c.status == CodeStatus::Pending).unwrap_or(false)
   201→    }
   202→
   203→    /// List all codes (for dashboard)
   204→    pub async fn list_codes(&self) -> Vec<SupportCode> {
   205→        let codes = self.codes.read().await;
   206→        codes.values().cloned().collect()
   207→    }
   208→
   209→    /// List active codes only
   210→    pub async fn list_active_codes(&self) -> Vec<SupportCode> {
   211→        let codes = self.codes.read().await;
   212→        codes.values()
   213→            .filter(|c| c.status == CodeStatus::Pending || c.status == CodeStatus::Connected)
   214→            .cloned()
   215→            .collect()
   216→    }
   217→
   218→    /// Get code by session ID
   219→    pub async fn get_by_session(&self, session_id: Uuid) -> Option<SupportCode> {
   220→        let session_to_code = self.session_to_code.read().await;
   221→        let code = session_to_code.get(&session_id)?;
   222→
   223→        let codes = self.codes.read().await;
   224→        codes.get(code).cloned()
   225→    }
   226→}
   227→
   228→impl Default for SupportCodeManager {
   229→    fn default() -> Self {
   230→        Self::new()
   231→    }
   232→}
   233→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>

//! Support session codes management
//!
//! Handles generation and validation of 6-digit support codes
//! for one-time remote support sessions.

use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
use chrono::{DateTime, Utc};
use rand::Rng;
use serde::{Deserialize, Serialize};
use uuid::Uuid;

/// A support session code
#[derive(Debug, Clone, Serialize)]
pub struct SupportCode {
    pub code: String,
    pub session_id: Uuid,
    pub created_by: String,
    pub created_at: DateTime<Utc>,
    pub status: CodeStatus,
    pub client_name: Option<String>,
    pub client_machine: Option<String>,
    pub connected_at: Option<DateTime<Utc>>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum CodeStatus {
    Pending,    // Waiting for client to connect
    Connected,  // Client connected, session active
    Completed,  // Session ended normally
    Cancelled,  // Code cancelled by tech
}

/// Request to create a new support code
#[derive(Debug, Deserialize)]
pub struct CreateCodeRequest {
    pub technician_id: Option<String>,
    pub technician_name: Option<String>,
}

/// Response when a code is validated
#[derive(Debug, Serialize)]
pub struct CodeValidation {
    pub valid: bool,
    pub session_id: Option<String>,
    pub server_url: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub error: Option<String>,
}

/// Manages support codes
#[derive(Clone)]
pub struct SupportCodeManager {
    codes: Arc<RwLock<HashMap<String, SupportCode>>>,
    session_to_code: Arc<RwLock<HashMap<Uuid, String>>>,
}

impl SupportCodeManager {
    pub fn new() -> Self {
        Self {
            codes: Arc::new(RwLock::new(HashMap::new())),
            session_to_code: Arc::new(RwLock::new(HashMap::new())),
        }
    }

    /// Generate a unique 6-digit code
    async fn generate_unique_code(&self) -> String {
        let codes = self.codes.read().await;
        let mut rng = rand::thread_rng();

        loop {
            let code: u32 = rng.gen_range(100000..999999);
            let code_str = code.to_string();

            if !codes.contains_key(&code_str) {
                return code_str;
            }
        }
    }

    /// Create a new support code
    pub async fn create_code(&self, request: CreateCodeRequest) -> SupportCode {
        let code = self.generate_unique_code().await;
        let session_id = Uuid::new_v4();

        let support_code = SupportCode {
            code: code.clone(),
            session_id,
            created_by: request.technician_name.unwrap_or_else(|| "Unknown".to_string()),
            created_at: Utc::now(),
            status: CodeStatus::Pending,
            client_name: None,
            client_machine: None,
            connected_at: None,
        };

        let mut codes = self.codes.write().await;
        codes.insert(code.clone(), support_code.clone());

        let mut session_to_code = self.session_to_code.write().await;
        session_to_code.insert(session_id, code);

        support_code
    }

    /// Validate a code and return session info
    pub async fn validate_code(&self, code: &str) -> CodeValidation {
        let codes = self.codes.read().await;

        match codes.get(code) {
            Some(support_code) => {
                if support_code.status == CodeStatus::Pending || support_code.status == CodeStatus::Connected {
                    CodeValidation {
                        valid: true,
                        session_id: Some(support_code.session_id.to_string()),
                        server_url: Some("wss://connect.azcomputerguru.com/ws/support".to_string()),
                        error: None,
                    }
                } else {
                    CodeValidation {
                        valid: false,
                        session_id: None,
                        server_url: None,
                        error: Some("This code has expired or been used".to_string()),
                    }
                }
            }
            None => CodeValidation {
                valid: false,
                session_id: None,
                server_url: None,
                error: Some("Invalid code".to_string()),
            },
        }
    }

    /// Mark a code as connected
    pub async fn mark_connected(&self, code: &str, client_name: Option<String>, client_machine: Option<String>) {
        let mut codes = self.codes.write().await;
        if let Some(support_code) = codes.get_mut(code) {
            support_code.status = CodeStatus::Connected;
            support_code.client_name = client_name;
            support_code.client_machine = client_machine;
            support_code.connected_at = Some(Utc::now());
        }
    }

    /// Link a support code to an actual WebSocket session
    pub async fn link_session(&self, code: &str, real_session_id: Uuid) {
        let mut codes = self.codes.write().await;
        if let Some(support_code) = codes.get_mut(code) {
            // Update session_to_code mapping with real session ID
            let old_session_id = support_code.session_id;
            support_code.session_id = real_session_id;

            // Update the reverse mapping
            let mut session_to_code = self.session_to_code.write().await;
            session_to_code.remove(&old_session_id);
            session_to_code.insert(real_session_id, code.to_string());
        }
    }

    /// Get code by its code string
    pub async fn get_code(&self, code: &str) -> Option<SupportCode> {
        let codes = self.codes.read().await;
        codes.get(code).cloned()
    }

    /// Mark a code as completed
    pub async fn mark_completed(&self, code: &str) {
        let mut codes = self.codes.write().await;
        if let Some(support_code) = codes.get_mut(code) {
            support_code.status = CodeStatus::Completed;
        }
    }

    /// Cancel a code (works for both pending and connected)
    pub async fn cancel_code(&self, code: &str) -> bool {
        let mut codes = self.codes.write().await;
        if let Some(support_code) = codes.get_mut(code) {
            if support_code.status == CodeStatus::Pending || support_code.status == CodeStatus::Connected {
                support_code.status = CodeStatus::Cancelled;
                return true;
            }
        }
        false
    }

    /// Check if a code is cancelled
    pub async fn is_cancelled(&self, code: &str) -> bool {
        let codes = self.codes.read().await;
        codes.get(code).map(|c| c.status == CodeStatus::Cancelled).unwrap_or(false)
    }

    /// Check if a code is valid for connection (exists and is pending)
    pub async fn is_valid_for_connection(&self, code: &str) -> bool {
        let codes = self.codes.read().await;
        codes.get(code).map(|c| c.status == CodeStatus::Pending).unwrap_or(false)
    }

    /// List all codes (for dashboard)
    pub async fn list_codes(&self) -> Vec<SupportCode> {
        let codes = self.codes.read().await;
        codes.values().cloned().collect()
    }

    /// List active codes only
    pub async fn list_active_codes(&self) -> Vec<SupportCode> {
        let codes = self.codes.read().await;
        codes.values()
            .filter(|c| c.status == CodeStatus::Pending || c.status == CodeStatus::Connected)
            .cloned()
            .collect()
    }

    /// Get code by session ID
    pub async fn get_by_session(&self, session_id: Uuid) -> Option<SupportCode> {
        let session_to_code = self.session_to_code.read().await;
        let code = session_to_code.get(&session_id)?;

        let codes = self.codes.read().await;
        codes.get(code).cloned()
    }
}

impl Default for SupportCodeManager {
    fn default() -> Self {
        Self::new()
    }
}


Perfect! Now let me check the proto definitions to understand the message types:


Bash

find "C:/Users/MikeSwanson/Claude/guru-connect" -name "*.proto" -type f

Find protobuf definition files


     1→syntax = "proto3";
     2→package guruconnect;
     3→
     4→// ============================================================================
     5→// Session Management
     6→// ============================================================================
     7→
     8→message SessionRequest {
     9→    string agent_id = 1;
    10→    string session_token = 2;
    11→    SessionType session_type = 3;
    12→    string client_version = 4;
    13→}
    14→
    15→message SessionResponse {
    16→    bool success = 1;
    17→    string session_id = 2;
    18→    string error = 3;
    19→    DisplayInfo display_info = 4;
    20→}
    21→
    22→enum SessionType {
    23→    SCREEN_CONTROL = 0;
    24→    VIEW_ONLY = 1;
    25→    BACKSTAGE = 2;
    26→    FILE_TRANSFER = 3;
    27→}
    28→
    29→// ============================================================================
    30→// Display Information
    31→// ============================================================================
    32→
    33→message DisplayInfo {
    34→    repeated Display displays = 1;
    35→    int32 primary_display = 2;
    36→}
    37→
    38→message Display {
    39→    int32 id = 1;
    40→    string name = 2;
    41→    int32 x = 3;
    42→    int32 y = 4;
    43→    int32 width = 5;
    44→    int32 height = 6;
    45→    bool is_primary = 7;
    46→}
    47→
    48→message SwitchDisplay {
    49→    int32 display_id = 1;
    50→}
    51→
    52→// ============================================================================
    53→// Video Frames
    54→// ============================================================================
    55→
    56→message VideoFrame {
    57→    int64 timestamp = 1;
    58→    int32 display_id = 2;
    59→    int32 sequence = 3;
    60→
    61→    oneof encoding {
    62→        RawFrame raw = 10;
    63→        EncodedFrame vp9 = 11;
    64→        EncodedFrame h264 = 12;
    65→        EncodedFrame h265 = 13;
    66→    }
    67→}
    68→
    69→message RawFrame {
    70→    int32 width = 1;
    71→    int32 height = 2;
    72→    bytes data = 3;             // Zstd compressed BGRA
    73→    bool compressed = 4;
    74→    repeated DirtyRect dirty_rects = 5;
    75→    bool is_keyframe = 6;       // Full frame vs incremental
    76→}
    77→
    78→message DirtyRect {
    79→    int32 x = 1;
    80→    int32 y = 2;
    81→    int32 width = 3;
    82→    int32 height = 4;
    83→}
    84→
    85→message EncodedFrame {
    86→    bytes data = 1;
    87→    bool keyframe = 2;
    88→    int64 pts = 3;
    89→    int64 dts = 4;
    90→}
    91→
    92→message VideoAck {
    93→    int32 sequence = 1;
    94→    int64 timestamp = 2;
    95→}
    96→
    97→// ============================================================================
    98→// Cursor
    99→// ============================================================================
   100→
   101→message CursorShape {
   102→    uint64 id = 1;
   103→    int32 hotspot_x = 2;
   104→    int32 hotspot_y = 3;
   105→    int32 width = 4;
   106→    int32 height = 5;
   107→    bytes data = 6;             // BGRA bitmap
   108→}
   109→
   110→message CursorPosition {
   111→    int32 x = 1;
   112→    int32 y = 2;
   113→    bool visible = 3;
   114→}
   115→
   116→// ============================================================================
   117→// Input Events
   118→// ============================================================================
   119→
   120→message MouseEvent {
   121→    int32 x = 1;
   122→    int32 y = 2;
   123→    MouseButtons buttons = 3;
   124→    int32 wheel_delta_x = 4;
   125→    int32 wheel_delta_y = 5;
   126→    MouseEventType event_type = 6;
   127→}
   128→
   129→enum MouseEventType {
   130→    MOUSE_MOVE = 0;
   131→    MOUSE_DOWN = 1;
   132→    MOUSE_UP = 2;
   133→    MOUSE_WHEEL = 3;
   134→}
   135→
   136→message MouseButtons {
   137→    bool left = 1;
   138→    bool right = 2;
   139→    bool middle = 3;
   140→    bool x1 = 4;
   141→    bool x2 = 5;
   142→}
   143→
   144→message KeyEvent {
   145→    bool down = 1;              // true = key down, false = key up
   146→    KeyEventType key_type = 2;
   147→    uint32 vk_code = 3;         // Virtual key code (Windows VK_*)
   148→    uint32 scan_code = 4;       // Hardware scan code
   149→    string unicode = 5;         // Unicode character (for text input)
   150→    Modifiers modifiers = 6;
   151→}
   152→
   153→enum KeyEventType {
   154→    KEY_VK = 0;                 // Virtual key code
   155→    KEY_SCAN = 1;               // Scan code
   156→    KEY_UNICODE = 2;            // Unicode character
   157→}
   158→
   159→message Modifiers {
   160→    bool ctrl = 1;
   161→    bool alt = 2;
   162→    bool shift = 3;
   163→    bool meta = 4;              // Windows key
   164→    bool caps_lock = 5;
   165→    bool num_lock = 6;
   166→}
   167→
   168→message SpecialKeyEvent {
   169→    SpecialKey key = 1;
   170→}
   171→
   172→enum SpecialKey {
   173→    CTRL_ALT_DEL = 0;
   174→    LOCK_SCREEN = 1;
   175→    PRINT_SCREEN = 2;
   176→}
   177→
   178→// ============================================================================
   179→// Clipboard
   180→// ============================================================================
   181→
   182→message ClipboardData {
   183→    ClipboardFormat format = 1;
   184→    bytes data = 2;
   185→    string mime_type = 3;
   186→}
   187→
   188→enum ClipboardFormat {
   189→    CLIPBOARD_TEXT = 0;
   190→    CLIPBOARD_HTML = 1;
   191→    CLIPBOARD_RTF = 2;
   192→    CLIPBOARD_IMAGE = 3;
   193→    CLIPBOARD_FILES = 4;
   194→}
   195→
   196→message ClipboardRequest {
   197→    // Request current clipboard content
   198→}
   199→
   200→// ============================================================================
   201→// Quality Control
   202→// ============================================================================
   203→
   204→message QualitySettings {
   205→    QualityPreset preset = 1;
   206→    int32 custom_fps = 2;       // 1-60
   207→    int32 custom_bitrate = 3;   // kbps
   208→    CodecPreference codec = 4;
   209→}
   210→
   211→enum QualityPreset {
   212→    QUALITY_AUTO = 0;
   213→    QUALITY_LOW = 1;            // Low bandwidth
   214→    QUALITY_BALANCED = 2;
   215→    QUALITY_HIGH = 3;           // Best quality
   216→}
   217→
   218→enum CodecPreference {
   219→    CODEC_AUTO = 0;
   220→    CODEC_RAW = 1;              // Raw + Zstd (LAN)
   221→    CODEC_VP9 = 2;
   222→    CODEC_H264 = 3;
   223→    CODEC_H265 = 4;
   224→}
   225→
   226→message LatencyReport {
   227→    int64 rtt_ms = 1;
   228→    int32 fps = 2;
   229→    int32 bitrate_kbps = 3;
   230→}
   231→
   232→// ============================================================================
   233→// Chat Messages
   234→// ============================================================================
   235→
   236→message ChatMessage {
   237→    string id = 1;              // Unique message ID
   238→    string sender = 2;          // "technician" or "client"
   239→    string content = 3;         // Message text
   240→    int64 timestamp = 4;        // Unix timestamp
   241→}
   242→
   243→// ============================================================================
   244→// Control Messages
   245→// ============================================================================
   246→
   247→message Heartbeat {
   248→    int64 timestamp = 1;
   249→}
   250→
   251→message HeartbeatAck {
   252→    int64 client_timestamp = 1;
   253→    int64 server_timestamp = 2;
   254→}
   255→
   256→message Disconnect {
   257→    string reason = 1;
   258→}
   259→
   260→// Server commands agent to start streaming video
   261→message StartStream {
   262→    string viewer_id = 1;           // ID of viewer requesting stream
   263→    int32 display_id = 2;           // Which display to stream (0 = primary)
   264→}
   265→
   266→// Server commands agent to stop streaming
   267→message StopStream {
   268→    string viewer_id = 1;           // Which viewer disconnected
   269→}
   270→
   271→// Agent reports its status periodically when idle
   272→message AgentStatus {
   273→    string hostname = 1;
   274→    string os_version = 2;
   275→    bool is_elevated = 3;
   276→    int64 uptime_secs = 4;
   277→    int32 display_count = 5;
   278→    bool is_streaming = 6;
   279→}
   280→
   281→// ============================================================================
   282→// Top-Level Message Wrapper
   283→// ============================================================================
   284→
   285→message Message {
   286→    oneof payload {
   287→        // Session
   288→        SessionRequest session_request = 1;
   289→        SessionResponse session_response = 2;
   290→
   291→        // Video
   292→        VideoFrame video_frame = 10;
   293→        VideoAck video_ack = 11;
   294→        SwitchDisplay switch_display = 12;
   295→
   296→        // Cursor
   297→        CursorShape cursor_shape = 15;
   298→        CursorPosition cursor_position = 16;
   299→
   300→        // Input
   301→        MouseEvent mouse_event = 20;
   302→        KeyEvent key_event = 21;
   303→        SpecialKeyEvent special_key = 22;
   304→
   305→        // Clipboard
   306→        ClipboardData clipboard_data = 30;
   307→        ClipboardRequest clipboard_request = 31;
   308→
   309→        // Quality
   310→        QualitySettings quality_settings = 40;
   311→        LatencyReport latency_report = 41;
   312→
   313→        // Control
   314→        Heartbeat heartbeat = 50;
   315→        HeartbeatAck heartbeat_ack = 51;
   316→        Disconnect disconnect = 52;
   317→        StartStream start_stream = 53;
   318→        StopStream stop_stream = 54;
   319→        AgentStatus agent_status = 55;
   320→
   321→        // Chat
   322→        ChatMessage chat_message = 60;
   323→    }
   324→}
   325→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>

syntax = "proto3";
package guruconnect;

// ============================================================================
// Session Management
// ============================================================================

message SessionRequest {
    string agent_id = 1;
    string session_token = 2;
    SessionType session_type = 3;
    string client_version = 4;
}

message SessionResponse {
    bool success = 1;
    string session_id = 2;
    string error = 3;
    DisplayInfo display_info = 4;
}

enum SessionType {
    SCREEN_CONTROL = 0;
    VIEW_ONLY = 1;
    BACKSTAGE = 2;
    FILE_TRANSFER = 3;
}

// ============================================================================
// Display Information
// ============================================================================

message DisplayInfo {
    repeated Display displays = 1;
    int32 primary_display = 2;
}

message Display {
    int32 id = 1;
    string name = 2;
    int32 x = 3;
    int32 y = 4;
    int32 width = 5;
    int32 height = 6;
    bool is_primary = 7;
}

message SwitchDisplay {
    int32 display_id = 1;
}

// ============================================================================
// Video Frames
// ============================================================================

message VideoFrame {
    int64 timestamp = 1;
    int32 display_id = 2;
    int32 sequence = 3;

    oneof encoding {
        RawFrame raw = 10;
        EncodedFrame vp9 = 11;
        EncodedFrame h264 = 12;
        EncodedFrame h265 = 13;
    }
}

message RawFrame {
    int32 width = 1;
    int32 height = 2;
    bytes data = 3;             // Zstd compressed BGRA
    bool compressed = 4;
    repeated DirtyRect dirty_rects = 5;
    bool is_keyframe = 6;       // Full frame vs incremental
}

message DirtyRect {
    int32 x = 1;
    int32 y = 2;
    int32 width = 3;
    int32 height = 4;
}

message EncodedFrame {
    bytes data = 1;
    bool keyframe = 2;
    int64 pts = 3;
    int64 dts = 4;
}

message VideoAck {
    int32 sequence = 1;
    int64 timestamp = 2;
}

// ============================================================================
// Cursor
// ============================================================================

message CursorShape {
    uint64 id = 1;
    int32 hotspot_x = 2;
    int32 hotspot_y = 3;
    int32 width = 4;
    int32 height = 5;
    bytes data = 6;             // BGRA bitmap
}

message CursorPosition {
    int32 x = 1;
    int32 y = 2;
    bool visible = 3;
}

// ============================================================================
// Input Events
// ============================================================================

message MouseEvent {
    int32 x = 1;
    int32 y = 2;
    MouseButtons buttons = 3;
    int32 wheel_delta_x = 4;
    int32 wheel_delta_y = 5;
    MouseEventType event_type = 6;
}

enum MouseEventType {
    MOUSE_MOVE = 0;
    MOUSE_DOWN = 1;
    MOUSE_UP = 2;
    MOUSE_WHEEL = 3;
}

message MouseButtons {
    bool left = 1;
    bool right = 2;
    bool middle = 3;
    bool x1 = 4;
    bool x2 = 5;
}

message KeyEvent {
    bool down = 1;              // true = key down, false = key up
    KeyEventType key_type = 2;
    uint32 vk_code = 3;         // Virtual key code (Windows VK_*)
    uint32 scan_code = 4;       // Hardware scan code
    string unicode = 5;         // Unicode character (for text input)
    Modifiers modifiers = 6;
}

enum KeyEventType {
    KEY_VK = 0;                 // Virtual key code
    KEY_SCAN = 1;               // Scan code
    KEY_UNICODE = 2;            // Unicode character
}

message Modifiers {
    bool ctrl = 1;
    bool alt = 2;
    bool shift = 3;
    bool meta = 4;              // Windows key
    bool caps_lock = 5;
    bool num_lock = 6;
}

message SpecialKeyEvent {
    SpecialKey key = 1;
}

enum SpecialKey {
    CTRL_ALT_DEL = 0;
    LOCK_SCREEN = 1;
    PRINT_SCREEN = 2;
}

// ============================================================================
// Clipboard
// ============================================================================

message ClipboardData {
    ClipboardFormat format = 1;
    bytes data = 2;
    string mime_type = 3;
}

enum ClipboardFormat {
    CLIPBOARD_TEXT = 0;
    CLIPBOARD_HTML = 1;
    CLIPBOARD_RTF = 2;
    CLIPBOARD_IMAGE = 3;
    CLIPBOARD_FILES = 4;
}

message ClipboardRequest {
    // Request current clipboard content
}

// ============================================================================
// Quality Control
// ============================================================================

message QualitySettings {
    QualityPreset preset = 1;
    int32 custom_fps = 2;       // 1-60
    int32 custom_bitrate = 3;   // kbps
    CodecPreference codec = 4;
}

enum QualityPreset {
    QUALITY_AUTO = 0;
    QUALITY_LOW = 1;            // Low bandwidth
    QUALITY_BALANCED = 2;
    QUALITY_HIGH = 3;           // Best quality
}

enum CodecPreference {
    CODEC_AUTO = 0;
    CODEC_RAW = 1;              // Raw + Zstd (LAN)
    CODEC_VP9 = 2;
    CODEC_H264 = 3;
    CODEC_H265 = 4;
}

message LatencyReport {
    int64 rtt_ms = 1;
    int32 fps = 2;
    int32 bitrate_kbps = 3;
}

// ============================================================================
// Chat Messages
// ============================================================================

message ChatMessage {
    string id = 1;              // Unique message ID
    string sender = 2;          // "technician" or "client"
    string content = 3;         // Message text
    int64 timestamp = 4;        // Unix timestamp
}

// ============================================================================
// Control Messages
// ============================================================================

message Heartbeat {
    int64 timestamp = 1;
}

message HeartbeatAck {
    int64 client_timestamp = 1;
    int64 server_timestamp = 2;
}

message Disconnect {
    string reason = 1;
}

// Server commands agent to start streaming video
message StartStream {
    string viewer_id = 1;           // ID of viewer requesting stream
    int32 display_id = 2;           // Which display to stream (0 = primary)
}

// Server commands agent to stop streaming
message StopStream {
    string viewer_id = 1;           // Which viewer disconnected
}

// Agent reports its status periodically when idle
message AgentStatus {
    string hostname = 1;
    string os_version = 2;
    bool is_elevated = 3;
    int64 uptime_secs = 4;
    int32 display_count = 5;
    bool is_streaming = 6;
}

// ============================================================================
// Top-Level Message Wrapper
// ============================================================================

message Message {
    oneof payload {
        // Session
        SessionRequest session_request = 1;
        SessionResponse session_response = 2;

        // Video
        VideoFrame video_frame = 10;
        VideoAck video_ack = 11;
        SwitchDisplay switch_display = 12;

        // Cursor
        CursorShape cursor_shape = 15;
        CursorPosition cursor_position = 16;

        // Input
        MouseEvent mouse_event = 20;
        KeyEvent key_event = 21;
        SpecialKeyEvent special_key = 22;

        // Clipboard
        ClipboardData clipboard_data = 30;
        ClipboardRequest clipboard_request = 31;

        // Quality
        QualitySettings quality_settings = 40;
        LatencyReport latency_report = 41;

        // Control
        Heartbeat heartbeat = 50;
        HeartbeatAck heartbeat_ack = 51;
        Disconnect disconnect = 52;
        StartStream start_stream = 53;
        StopStream stop_stream = 54;
        AgentStatus agent_status = 55;

        // Chat
        ChatMessage chat_message = 60;
    }
}


Excellent! Now I have a comprehensive understanding of the GuruConnect server architecture. Let me provide you with a detailed analysis.

## GuruConnect Server Architecture Analysis

### Current Architecture Overview

The GuruConnect server is a **WebSocket-based relay server** built with **Axum** (Rust async web framework) that manages real-time connections between remote desktop agents (Windows clients) and web-based viewers (technicians). The server is currently **entirely in-memory** with no persistent storage.

### 1. Session Management Architecture (`src/session/mod.rs`)

**Current Design:**
- **SessionManager** (Arc<RwLock<HashMap>>) - Master coordinator for all active sessions
- Uses in-memory hashmaps with async RwLock for thread-safe access:
  - `sessions`: Maps SessionId → SessionData
  - `agents`: Maps AgentId → SessionId (one agent per session)

**Session Lifecycle:**
1. **Agent Registration** → Creates new SessionId, allocates communication channels
2. **Viewer Join** → Subscribers to broadcast channel, sends StartStream to agent
3. **Viewer Leave** → Unsubscribes, sends StopStream when last viewer leaves
4. **Agent Disconnect** → Marks persistent agents offline (keeps session), deletes support sessions

**Communication Channels:**
```
Frame Channel (broadcast):  Agent → All Viewers (video frames)
Input Channel (mpsc):       Viewers → Agent (mouse/keyboard/input)
```

**Status Tracking:**
- Heartbeat monitoring with 90-second timeout
- Agent metadata: OS version, elevation status, uptime, display count, streaming status
- Viewer list with connection timestamps

### 2. Application State (`src/main.rs`)

```rust
#[derive(Clone)]
pub struct AppState {
    sessions: session::SessionManager,        // In-memory session storage
    support_codes: SupportCodeManager,        // In-memory 6-digit code management
}
```

**State Initialization:**
- AppState is cloned and passed to all handlers via Axum State extraction
- No database initialization, no persistent storage

**Current Routes:**
- `/health` - Health check
- `/ws/agent` - WebSocket endpoint for agents
- `/ws/viewer` - WebSocket endpoint for viewers
- `/api/sessions` - List/get/delete sessions (REST)
- `/api/codes/*` - Support code management

### 3. Database Module (`src/db/mod.rs`)

**Current Status:** Complete placeholder
```rust
pub struct Database {
    _placeholder: (),  // TODO: Add sqlx pool when PostgreSQL needed
}
```

**Defined but Unimplemented:**
- `SessionEvent` struct with event types (Started, ViewerJoined, ViewerLeft, Ended)
- `Database::log_session_event()` method (placeholder only)
- No actual database operations

### 4. Dependencies (`Cargo.toml`)

**Database Stack Already Included:**
- **sqlx** (0.8) - Async PostgreSQL client with connection pooling
- Features: postgres, runtime-tokio, uuid, chrono, json
- **Ready to use** - just needs implementation

**Other Key Dependencies:**
- **tokio** - Async runtime
- **axum** - Web framework with WebSocket support
- **prost/prost-types** - Protocol Buffers serialization
- **serde/serde_json** - Serialization
- **tracing** - Structured logging
- **jsonwebtoken** - JWT authentication (not yet implemented)
- **argon2** - Password hashing (not yet implemented)

### 5. Message Protocol (`proto/guruconnect.proto`)

**Message Types:**
- **VideoFrame** - Screen captures (RawFrame, VP9, H264, H265)
- **MouseEvent, KeyEvent, SpecialKeyEvent** - Input events
- **ChatMessage** - Technician-client communication
- **AgentStatus** - Agent metadata (OS, elevation, uptime, displays)
- **Heartbeat/HeartbeatAck** - Connection health monitoring
- **StartStream/StopStream** - Server commands to agent
- **Disconnect** - Graceful disconnection
- **SessionRequest/SessionResponse** - Session negotiation
- **QualitySettings, LatencyReport** - Quality control

### 6. Support Codes (`src/support_codes.rs`)

**In-Memory Management:**
- Generates unique 6-digit codes
- States: Pending → Connected → Completed (or Cancelled)
- Links support codes to actual WebSocket sessions
- Currently all data lost on server restart

### 7. WebSocket Handler Flow (`src/relay/mod.rs`)

**Agent Connection Handler:**
1. Validates support code (if provided)
2. Registers agent and gets channels
3. Spawns input forwarding task
4. Spawns cancellation check task (every 2 seconds)
5. Main loop processes: VideoFrame, AgentStatus, Heartbeat
6. Cleanup on disconnect

**Viewer Connection Handler:**
1. Joins session (creates ViewerId)
2. Subscribes to frame broadcast channel
3. Gets input sender to agent
4. Spawns frame forwarding task
5. Main loop processes: MouseEvent, KeyEvent, ChatMessage
6. Cleanup on disconnect (sends StopStream if last viewer)

---

## Database Integration Points (Required for Persistence)

### 1. **Session Audit Logging**
```
Table: connect_session_logs
- session_id (UUID, FK)
- agent_id (String)
- agent_name (String)
- started_at (timestamp)
- ended_at (timestamp, nullable)
- duration_secs (int)
- viewer_count (int)
- is_persistent (bool)
- disconnect_reason (text, nullable)
- created_at (timestamp)
```

**Insert Points:**
- `session::register_agent()` → Insert new session
- `relay::handle_agent_connection()` cleanup → Update session with end_time, reason

### 2. **Session Events Audit Trail**
```
Table: connect_session_events
- id (UUID)
- session_id (UUID, FK)
- event_type (ENUM: STARTED, VIEWER_JOINED, VIEWER_LEFT, ENDED, DISCONNECTED)
- event_timestamp (timestamp)
- user_id (String, nullable - technician ID)
- viewer_name (String, nullable)
- details (JSONB, nullable)
```

**Insert Points:**
- Register agent → STARTED event
- `session::join_session()` → VIEWER_JOINED event
- `session::leave_session()` → VIEWER_LEFT event
- Agent disconnect → ENDED/DISCONNECTED event

### 3. **Support Code History**
```
Table: connect_support_codes
- code (String, PK)
- session_id (UUID, FK)
- created_by (String)
- created_at (timestamp)
- connected_at (timestamp, nullable)
- completed_at (timestamp, nullable)
- cancelled_at (timestamp, nullable)
- status (ENUM: PENDING, CONNECTED, COMPLETED, CANCELLED)
- client_name (String, nullable)
- client_machine (String, nullable)
```

**Insert Points:**
- `support_codes::create_code()` → Insert new code
- `support_codes::mark_connected()` → Update status, set connected_at
- `support_codes::mark_completed()` → Update status, set completed_at
- `support_codes::cancel_code()` → Update status, set cancelled_at

### 4. **Agent Status History** (Optional - High Volume)
```
Table: connect_agent_status_history
- id (UUID)
- agent_id (String)
- session_id (UUID, FK)
- timestamp (timestamp)
- os_version (String)
- is_elevated (bool)
- uptime_secs (bigint)
- display_count (int)
- is_streaming (bool)
- heartbeat_received (bool)
```

**Insert Points:**
- `relay::handle_agent_connection()` → `AgentStatus` message received
- Could be batched or sampled to avoid high volume

### 5. **Viewer Connection History** (Optional)
```
Table: connect_viewer_sessions
- id (UUID)
- viewer_id (String)
- viewer_name (String)
- session_id (UUID, FK)
- joined_at (timestamp)
- left_at (timestamp, nullable)
- duration_secs (int, nullable)
```

**Insert Points:**
- `session::join_session()` → Insert viewer record
- `session::leave_session()` → Update with left_at timestamp

---

## Architecture Summary Table

| Component | Current State | Storage | Persistence |
|-----------|---------------|---------|-------------|
| **Sessions** | In-memory HashMap | Runtime memory | Lost on restart |
| **Support Codes** | In-memory HashMap | Runtime memory | Lost on restart |
| **Session Data** | In-memory SessionData | Runtime memory | Lost on restart |
| **Communication Channels** | Broadcast/MPSC | Tokio channels | Lost on restart |
| **Database Module** | Placeholder stub | None | N/A |
| **Dependencies** | sqlx 0.8 ready | Configured but unused | Ready to implement |

---

## Key Observations

1. **Designed for Real-Time, Not Persistence** - Session data is deliberately kept in-memory for speed; the server treats sessions as ephemeral connections

2. **Single-Server Architecture** - No support for clustering/replication; all state is local to one server instance

3. **Ready for Database** - Dependencies already included; just needs schema creation and method implementations

4. **Audit-Friendly Protocol** - Proto messages contain all metadata needed for comprehensive logging (status, viewers, timestamps)

5. **Support Code Design Flaw** - Currently in-memory; support codes lost on restart, making them unreliable for customer-facing features

6. **No Authentication Implementation** - Auth module is placeholder; production needs JWT validation and API key verification

7. **Heartbeat-Based Health** - Relies on agent heartbeats; detects timeouts after 90 seconds of silence

This architecture is production-ready for **short-lived support sessions** but needs database integration for:
- Audit trails and compliance
- Session recovery/reconnection
- Support code persistence
- Agent history tracking
- Analytics and monitoring