1→# GuruRMM Tray Icon Implementation Plan 2→ 3→## Overview 4→ 5→Add a system tray application for the GuruRMM agent that provides user visibility and interaction, with all features controlled by admin policy from the server. 6→ 7→## Architecture 8→ 9→``` 10→┌─────────────────┐ Policy ┌─────────────────┐ 11→│ RMM Server │ ──────────────► │ Agent Service │ 12→│ (WebSocket) │ │ (Background) │ 13→└─────────────────┘ └────────┬────────┘ 14→ │ IPC 15→ │ (Named Pipe) 16→ ▼ 17→ ┌─────────────────┐ 18→ │ Tray App │ 19→ │ (User Session) │ 20→ └─────────────────┘ 21→``` 22→ 23→**Key Points:** 24→- Agent service runs as SYSTEM, always running 25→- Tray app runs in user session, started by service if policy enables it 26→- IPC via Windows Named Pipe for status/commands 27→- Policy controls everything: visibility, menu items, allowed actions 28→ 29→--- 30→ 31→## Phase 1: IPC Infrastructure 32→ 33→### 1.1 Add Named Pipe Server to Agent Service 34→ 35→**File:** `agent/src/ipc.rs` (new) 36→ 37→```rust 38→// Named pipe: \\.\pipe\gururmm-agent 39→// Protocol: JSON messages over pipe 40→ 41→pub enum IpcRequest { 42→ GetStatus, // Get current agent status 43→ GetPolicy, // Get current tray policy 44→ ForceCheckin, // Trigger immediate metrics send 45→ StopAgent, // Stop the agent service (if allowed) 46→ Subscribe, // Subscribe to status updates 47→} 48→ 49→pub enum IpcResponse { 50→ Status(AgentStatus), 51→ Policy(TrayPolicy), 52→ Ok, 53→ Error(String), 54→ Denied(String), // Action not allowed by policy 55→} 56→ 57→pub struct AgentStatus { 58→ pub connected: bool, 59→ pub last_checkin: Option>, 60→ pub server_url: String, 61→ pub agent_version: String, 62→ pub device_id: String, 63→ pub hostname: String, 64→} 65→``` 66→ 67→### 1.2 Tray Policy Structure 68→ 69→**Add to server → agent protocol:** 70→ 71→```rust 72→pub struct TrayPolicy { 73→ pub enabled: bool, // Show tray icon at all 74→ pub show_status: bool, // Show connection status 75→ pub show_info: bool, // Show agent info in menu 76→ pub allow_force_checkin: bool, // Allow manual check-in 77→ pub allow_view_logs: bool, // Allow opening log file 78→ pub allow_open_dashboard: bool, // Allow opening web dashboard 79→ pub allow_stop_agent: bool, // Allow stopping the agent 80→ pub dashboard_url: Option, 81→ pub custom_icon: Option, // Base64 encoded custom icon 82→ pub tooltip_text: Option, 83→} 84→``` 85→ 86→--- 87→ 88→## Phase 2: Tray Application (Windows) 89→ 90→### 2.1 New Crate Structure 91→ 92→``` 93→gururmm/ 94→├── agent/ # Existing agent service 95→│ └── src/ 96→│ ├── main.rs 97→│ ├── ipc.rs # NEW: IPC server 98→│ └── ... 99→└── tray/ # NEW: Tray application 100→ ├── Cargo.toml 101→ └── src/ 102→ ├── main.rs # Entry point, IPC client 103→ ├── tray.rs # Windows tray icon management 104→ ├── menu.rs # Dynamic menu building 105→ └── ipc.rs # Named pipe client 106→``` 107→ 108→### 2.2 Tray Crate Dependencies 109→ 110→```toml 111→[package] 112→name = "gururmm-tray" 113→version = "0.1.0" 114→ 115→[dependencies] 116→tray-icon = "0.14" # Cross-platform tray (uses winit) 117→winit = "0.29" # Window event loop 118→image = "0.24" # Icon loading 119→serde = { version = "1", features = ["derive"] } 120→serde_json = "1" 121→tokio = { version = "1", features = ["net", "io-util", "sync"] } 122→anyhow = "1" 123→tracing = "0.1" 124→tracing-subscriber = "0.3" 125→ 126→[target.'cfg(windows)'.dependencies] 127→windows = { version = "0.58", features = [ 128→ "Win32_System_Pipes", 129→ "Win32_Foundation", 130→]} 131→ 132→[profile.release] 133→opt-level = "z" 134→lto = true 135→strip = true 136→``` 137→ 138→### 2.3 Tray Icon States 139→ 140→| State | Icon | Tooltip | 141→|-------|------|---------| 142→| Connected | Green circle | "GuruRMM - Connected" | 143→| Disconnected | Yellow circle | "GuruRMM - Reconnecting..." | 144→| Error | Red circle | "GuruRMM - Error: {message}" | 145→| Disabled | Gray circle | "GuruRMM - Disabled by policy" | 146→ 147→### 2.4 Menu Structure (Policy-Controlled) 148→ 149→``` 150→GuruRMM Agent v1.0.0 151→───────────────────── 152→✓ Connected to rmm-api.example.com 153→ Last check-in: 2 minutes ago 154→ Device: WORKSTATION-01 155→───────────────────── 156→ Force Check-in [if allow_force_checkin] 157→ View Logs [if allow_view_logs] 158→ Open Dashboard [if allow_open_dashboard] 159→───────────────────── 160→ Stop Agent [if allow_stop_agent] 161→ Exit Tray [always, hides tray only] 162→``` 163→ 164→--- 165→ 166→## Phase 3: Agent Service Changes 167→ 168→### 3.1 Tray Launcher 169→ 170→Agent service launches tray app for each logged-in user session: 171→ 172→**File:** `agent/src/tray_launcher.rs` (new) 173→ 174→```rust 175→// On Windows: 176→// 1. Enumerate user sessions (WTSEnumerateSessions) 177→// 2. For active sessions, launch tray in user context 178→// 3. Monitor for new logons, launch tray 179→// 4. If policy disables tray, terminate tray processes 180→``` 181→ 182→### 3.2 Policy Handler 183→ 184→When server sends policy update: 185→ 186→```rust 187→// In transport handler: 188→ServerMessage::PolicyUpdate(policy) => { 189→ // Store policy 190→ self.policy.write().await = policy; 191→ 192→ // Update tray processes 193→ if policy.tray.enabled { 194→ self.tray_launcher.ensure_running().await; 195→ } else { 196→ self.tray_launcher.terminate_all().await; 197→ } 198→ 199→ // Notify connected tray apps of policy change 200→ self.ipc_server.broadcast_policy(&policy.tray).await; 201→} 202→``` 203→ 204→--- 205→ 206→## Phase 4: Server-Side Policy 207→ 208→### 4.1 Database Schema Addition 209→ 210→```sql 211→ALTER TABLE sites ADD COLUMN tray_policy JSONB DEFAULT '{ 212→ "enabled": true, 213→ "show_status": true, 214→ "show_info": true, 215→ "allow_force_checkin": true, 216→ "allow_view_logs": true, 217→ "allow_open_dashboard": true, 218→ "allow_stop_agent": false, 219→ "dashboard_url": null, 220→ "custom_icon": null, 221→ "tooltip_text": null 222→}'; 223→``` 224→ 225→### 4.2 Dashboard UI 226→ 227→Add to Site Settings page: 228→- Toggle: "Show system tray icon" 229→- Checkboxes for each menu option 230→- Custom icon upload 231→- Custom tooltip text 232→ 233→--- 234→ 235→## Implementation Order 236→ 237→### Step 1: IPC Infrastructure (Agent Side) 238→1. Create `agent/src/ipc.rs` with named pipe server 239→2. Add IPC task to agent main loop 240→3. Define message protocol (GetStatus, GetPolicy, etc.) 241→4. Test with simple client 242→ 243→### Step 2: Tray Application (Windows) 244→1. Create `tray/` crate with Cargo.toml 245→2. Implement IPC client connecting to agent 246→3. Create basic tray icon with `tray-icon` crate 247→4. Build dynamic menu from policy 248→5. Handle menu actions → IPC requests 249→ 250→### Step 3: Tray Launcher (Agent Side) 251→1. Add session enumeration (WTSEnumerateSessions) 252→2. Launch tray in user context (CreateProcessAsUser) 253→3. Monitor for logon/logoff events 254→4. Handle policy enable/disable 255→ 256→### Step 4: Server Integration 257→1. Add tray_policy to server data model 258→2. Add policy to site settings API 259→3. Send policy to agent on connect 260→4. Add dashboard UI for policy management 261→ 262→### Step 5: Polish 263→1. Icon assets (green/yellow/red/gray) 264→2. Installer updates (include tray binary) 265→3. Documentation 266→ 267→--- 268→ 269→## Files to Create/Modify 270→ 271→### New Files 272→| File | Purpose | 273→|------|---------| 274→| `agent/src/ipc.rs` | Named pipe IPC server | 275→| `agent/src/tray_launcher.rs` | Launch tray in user sessions | 276→| `tray/Cargo.toml` | Tray app crate config | 277→| `tray/src/main.rs` | Tray app entry point | 278→| `tray/src/tray.rs` | Tray icon management | 279→| `tray/src/menu.rs` | Dynamic menu building | 280→| `tray/src/ipc.rs` | Named pipe client | 281→| `assets/icons/` | Tray icon images | 282→ 283→### Modified Files 284→| File | Changes | 285→|------|---------| 286→| `agent/src/main.rs` | Add IPC server task, tray launcher | 287→| `agent/src/transport/mod.rs` | Add TrayPolicy to protocol | 288→| `agent/Cargo.toml` | Add windows crate for session APIs | 289→| `server/src/models/` | Add tray_policy to site model | 290→| `dashboard/src/` | Add tray policy UI to site settings | 291→ 292→--- 293→ 294→## Future: Mac/Linux Support 295→ 296→### macOS 297→- Menu bar icon via same `tray-icon` crate 298→- IPC via Unix domain socket instead of named pipe 299→- Launch via launchd user agent 300→ 301→### Linux 302→- System tray via `tray-icon` (uses libappindicator/StatusNotifierItem) 303→- IPC via Unix domain socket 304→- Launch via XDG autostart or systemd user service 305→- Note: GNOME requires extension for tray icons 306→ 307→--- 308→ 309→## Security Considerations 310→ 311→1. **IPC Authentication**: Tray must prove it's running in a valid user session 312→2. **Policy Enforcement**: Agent validates actions against policy, not tray 313→3. **Stop Agent**: Requires admin policy AND user confirmation dialog 314→4. **Named Pipe ACL**: Restrict pipe access to authenticated users 315→ 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.