Complete bidirectional tunnel communication between server and agents, enabling persistent secure channels for future command execution and file operations. Agents transition from heartbeat mode to tunnel mode on-demand while maintaining WebSocket connection. Server Implementation: - Database layer (db/tunnel.rs): Session CRUD, ownership validation, cleanup on disconnect (prevents orphaned sessions) - API endpoints (api/tunnel.rs): POST /open, POST /close, GET /status with JWT auth, UUID validation, proper HTTP status codes - Protocol extension (ws/mod.rs): TunnelOpen/Close/Data messages, agent response handlers (TunnelReady/Data/Error) - Migration (006_tunnel_sessions.sql): tech_sessions table with partial unique constraint, foreign keys with CASCADE, audit table Agent Implementation: - State machine (tunnel/mod.rs): AgentMode (Heartbeat ↔ Tunnel), channel multiplexing, concurrent session prevention - WebSocket handlers (transport/websocket.rs): Open/close tunnel, mode switching without dropping connection, cleanup on disconnect - Protocol extension (transport/mod.rs): TunnelReady/Data/Error messages matching server definitions - Unit tests: Lifecycle and channel management coverage Key Features: - Security: JWT auth, session ownership verification, SQL injection prevention, constraint-based duplicate session blocking - Cleanup: Automatic session closure on agent disconnect (both sides), channel cleanup, graceful state transitions - Error handling: Proper HTTP status codes (400/403/404/409/500), comprehensive Result types, detailed logging - Extensibility: Channel types ready (Terminal/File/Registry/Service), TunnelDataPayload enum for Phase 2+ expansion Phase 1 Scope (Implemented): - Tunnel session lifecycle management - Mode switching (heartbeat ↔ tunnel) - Protocol message routing - Database session tracking Phase 2 Next Steps: - Terminal command execution (tokio::process::Command) - Client WebSocket connections for output streaming - Command audit logging - File transfer operations Verification: - Server compiles successfully (0 errors) - Agent unit tests pass (tunnel lifecycle, channel management) - Code review approved (protocol alignment verified) - Database constraints enforce referential integrity - Cleanup tested (session closure on disconnect) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
8.8 KiB
GuruRMM Tunnel - Phase 1 Agent Implementation
Date: 2026-04-14 Status: COMPLETED Component: Agent (Rust)
Summary
Successfully implemented Phase 1 of the GuruRMM real-time tunnel feature on the agent side. The agent now supports mode switching between Heartbeat and Tunnel modes, handles tunnel lifecycle messages, and is ready for Phase 2 terminal command execution.
Implementation Details
1. Protocol Extensions
File: agent/src/transport/mod.rs
Added new message types to AgentMessage enum:
TunnelReady { session_id: String }- Confirmation that tunnel is readyTunnelData { channel_id: String, data: TunnelDataPayload }- Bidirectional tunnel dataTunnelError { channel_id: String, error: String }- Error reporting
Added new message types to ServerMessage enum:
TunnelOpen { session_id: String, tech_id: Uuid }- Server request to open tunnelTunnelClose { session_id: String }- Server request to close tunnelTunnelData { channel_id: String, data: TunnelDataPayload }- Bidirectional tunnel data
Added TunnelDataPayload enum (Phase 1: Terminal only):
Terminal { command: String }- Terminal command requestTerminalOutput { stdout: String, stderr: String, exit_code: Option<i32> }- Terminal response
2. Tunnel Manager Module
File: agent/src/tunnel/mod.rs (NEW)
Created comprehensive tunnel state management:
AgentMode enum:
pub enum AgentMode {
Heartbeat, // Default: 30s heartbeats, metrics, network monitoring
Tunnel {
session_id: String,
tech_id: Uuid,
channels: HashMap<String, ChannelType>,
},
}
TunnelManager struct:
open_tunnel()- Transition from Heartbeat to Tunnel modeclose_tunnel()- Transition back to Heartbeat modeadd_channel()- Register new channel (terminal, file, etc.)remove_channel()- Cleanup channelforce_close()- Emergency cleanup on disconnect
Channel types (extensible for future phases):
Terminal- Command execution (Phase 1)File- File operations (Phase 2+)Registry- Registry operations (Phase 2+)Service- Service management (Phase 2+)
3. WebSocket Integration
File: agent/src/transport/websocket.rs
Updated WebSocket client to support tunnel operations:
New handler functions:
handle_tunnel_open()- Process TunnelOpen request, send TunnelReadyhandle_tunnel_close()- Process TunnelClose request, cleanup statehandle_tunnel_data()- Route tunnel data by channel (Phase 1: placeholder)
Message loop updates:
- Created
TunnelManagerinstance in connection lifecycle - Updated
handle_server_message()signature to accept tunnel manager - Added tunnel message logging (TunnelReady, TunnelData, TunnelError)
- Force-close tunnel on WebSocket disconnect
Mode persistence:
- Tunnel state maintained across message loop iterations
- Heartbeat continues in both modes (connection keepalive)
- Clean shutdown closes active sessions
4. Module Registration
File: agent/src/main.rs
Added tunnel module to module tree:
mod tunnel;
Testing
Unit Tests
Created comprehensive test suite in agent/src/tunnel/mod.rs:
Test: test_tunnel_lifecycle
- Starts in Heartbeat mode
- Opens tunnel successfully
- Rejects concurrent tunnel sessions
- Closes tunnel and returns to Heartbeat mode
Test: test_channel_management
- Rejects channel operations without active tunnel
- Adds multiple channels
- Retrieves channel types
- Removes channels
- Force-closes tunnel
Test Results:
running 2 tests
test tunnel::tests::test_tunnel_lifecycle ... ok
test tunnel::tests::test_channel_management ... ok
test result: ok. 2 passed; 0 failed; 0 ignored
Compilation
Status: SUCCESSFUL
Warnings (expected, non-critical):
tech_idfield unused (will be used in Phase 2 for authorization)- Some enum variants unused (File, Registry, Service - Phase 2+)
- Some methods unused (mode accessors - used in Phase 2)
Protocol Flow
Tunnel Open
Server → Agent: TunnelOpen { session_id, tech_id }
Agent: tunnel_manager.open_tunnel()
Agent → Server: TunnelReady { session_id }
Tunnel Close
Server → Agent: TunnelClose { session_id }
Agent: tunnel_manager.close_tunnel()
Terminal Command (Phase 1 - Placeholder)
Server → Agent: TunnelData {
channel_id: "...",
data: Terminal { command: "..." }
}
Agent: Log command (execution in Phase 2)
Agent → Server: TunnelData {
channel_id: "...",
data: TerminalOutput {
stdout: "",
stderr: "Not implemented",
exit_code: Some(-1)
}
}
Connection Loss
WebSocket disconnect detected
Agent: tunnel_manager.force_close()
Agent: Cleanup tasks, return to heartbeat mode
Key Features
Mode Switching
- Clean transition between Heartbeat and Tunnel modes
- Single active tunnel per agent (prevents session conflicts)
- Tunnel state persists across message loop iterations
Channel Multiplexing
- HashMap-based channel routing by
channel_id - Extensible channel types (Terminal, File, Registry, Service)
- Channel lifecycle management (add, remove, cleanup)
Error Handling
- Validates session IDs on close requests
- Rejects concurrent tunnel sessions
- Sends TunnelError messages for failures
- Force-close on unexpected disconnect
Logging
- Info-level: Tunnel open/close, mode transitions
- Debug-level: Channel operations, TunnelData routing
- Warn-level: Errors, rejected operations
Files Modified
agent/src/transport/mod.rs- Protocol message definitionsagent/src/transport/websocket.rs- WebSocket tunnel integrationagent/src/main.rs- Module registrationagent/src/tunnel/mod.rs- NEW: Tunnel manager implementation
Next Steps (Phase 2)
Terminal Command Execution
Implementation required in handle_tunnel_data():
- Parse
TunnelDataPayload::Terminal { command } - Spawn process using
tokio::process::Command - Capture stdout/stderr streams
- Handle exit codes and timeouts
- Send
TunnelDataPayload::TerminalOutputresponse
Considerations:
- Shell selection (PowerShell, cmd, bash based on OS)
- Working directory restrictions (security)
- Timeout enforcement (prevent hung processes)
- Error handling (process spawn failures, permission errors)
Integration Testing
Manual testing with server:
- Deploy updated agent to test machine
- Server sends TunnelOpen via WebSocket
- Verify TunnelReady response
- Send Terminal command
- Verify TerminalOutput response (Phase 2)
- Server sends TunnelClose
- Verify graceful cleanup
Compliance with Architecture Plan
Alignment with plans/real-time-tunnel-architecture.md:
- [OK] AgentMode state machine (Heartbeat ↔ Tunnel)
- [OK] Channel routing by channel_id
- [OK] TunnelOpen/TunnelClose lifecycle
- [OK] TunnelReady confirmation message
- [OK] TunnelDataPayload enum (Phase 1: Terminal only)
- [OK] Heartbeat maintained in tunnel mode
- [OK] Force-close on disconnect
- [OK] Unit tests for state machine
- [PENDING] Terminal command execution (Phase 2)
- [PENDING] File operations (Phase 3)
- [PENDING] MCP server integration (Phase 4)
Known Limitations
-
Terminal execution not implemented - Phase 1 only handles protocol and state management. Actual command execution is Phase 2.
-
No working directory restrictions - Security layer for path validation will be added in Phase 2.
-
Single tunnel per agent - By design, prevents session conflicts. Multi-tech sessions deferred to future enhancement.
-
No channel-level timeouts - Will be added in Phase 2 with actual command execution.
Security Notes
Implemented:
- Session validation (session_id matching on close)
- Single tunnel enforcement (rejects concurrent sessions)
- Clean state transitions (no lingering channels)
Pending (Phase 2+):
- Command sanitization (injection prevention)
- Working directory allowlist
- Rate limiting (server-side)
- Audit logging (server-side)
Performance Impact
Memory:
TunnelManager: ~200 bytes (enum + HashMap overhead)- Active per connection, deallocated on disconnect
- Negligible impact on heartbeat mode
CPU:
- Mode checks: O(1) enum match
- Channel routing: O(1) HashMap lookup
- No continuous tasks in tunnel mode
- Heartbeat continues at 30s interval (unchanged)
Network:
- TunnelReady: Single message on tunnel open (~100 bytes)
- Heartbeat continues in tunnel mode (no change)
- TunnelData: Variable (depends on command output in Phase 2)
Conclusion
Phase 1 agent implementation is complete and tested. The agent can now:
- Switch between Heartbeat and Tunnel modes
- Handle TunnelOpen/TunnelClose lifecycle
- Route tunnel messages by channel_id
- Maintain connection integrity in both modes
Ready for Phase 2: Terminal command execution implementation.
Status: READY FOR SERVER INTEGRATION TESTING