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>
7.3 KiB
7.3 KiB
GuruRMM Tunnel Protocol - Quick Reference
Message Types
Server → Agent
| Message | Payload | Purpose |
|---|---|---|
TunnelOpen |
{ session_id: String, tech_id: Uuid } |
Open tunnel session |
TunnelClose |
{ session_id: String } |
Close tunnel session |
TunnelData |
{ channel_id: String, data: TunnelDataPayload } |
Send command/data |
Agent → Server
| Message | Payload | Purpose |
|---|---|---|
TunnelReady |
{ session_id: String } |
Confirm tunnel ready |
TunnelData |
{ channel_id: String, data: TunnelDataPayload } |
Return output/data |
TunnelError |
{ channel_id: String, error: String } |
Report error |
TunnelDataPayload (Both Directions)
| Variant | Fields | Direction | Purpose |
|---|---|---|---|
Terminal |
{ command: String } |
Server → Agent | Execute terminal command |
TerminalOutput |
{ stdout: String, stderr: String, exit_code: Option<i32> } |
Agent → Server | Return command output |
Message Flow Examples
1. Open Tunnel
Client → Server API: POST /api/v1/tunnel/open {"agent_id":"..."}
Server → Agent WS: {"type":"tunnel_open","payload":{"session_id":"...","tech_id":"..."}}
Agent → Server WS: {"type":"tunnel_ready","payload":{"session_id":"..."}}
Server: Updates last_activity, logs success
2. Execute Command (Phase 2)
Client → Server API: POST /api/v1/tunnel/:session_id/command {"command":"ls -la"}
Server → Agent WS: {"type":"tunnel_data","payload":{"channel_id":"...","data":{"type":"terminal","payload":{"command":"ls -la"}}}}
Agent: Executes command
Agent → Server WS: {"type":"tunnel_data","payload":{"channel_id":"...","data":{"type":"terminal_output","payload":{"stdout":"...\n","stderr":"","exit_code":0}}}}
Server → Client WS: Forwards output to connected clients
3. Error Handling
Agent encounters error
Agent → Server WS: {"type":"tunnel_error","payload":{"channel_id":"...","error":"Failed to execute: permission denied"}}
Server: Logs error, forwards to clients (Phase 2)
4. Close Tunnel
Client → Server API: POST /api/v1/tunnel/close {"session_id":"..."}
Server → Agent WS: {"type":"tunnel_close","payload":{"session_id":"..."}}
Server: Updates database (status='closed', closed_at=NOW())
5. Agent Disconnect
Agent WebSocket closes
Server: Detects disconnect
Server: Calls close_agent_tunnel_sessions(agent_id)
Server: Sets all active sessions to 'closed'
Server: Logs count of sessions closed
JSON Examples
TunnelOpen (Server → Agent)
{
"type": "tunnel_open",
"payload": {
"session_id": "550e8400-e29b-41d4-a716-446655440000",
"tech_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7"
}
}
TunnelReady (Agent → Server)
{
"type": "tunnel_ready",
"payload": {
"session_id": "550e8400-e29b-41d4-a716-446655440000"
}
}
TunnelData - Terminal Command (Server → Agent)
{
"type": "tunnel_data",
"payload": {
"channel_id": "terminal-1",
"data": {
"type": "terminal",
"payload": {
"command": "ls -la /home"
}
}
}
}
TunnelData - Terminal Output (Agent → Server)
{
"type": "tunnel_data",
"payload": {
"channel_id": "terminal-1",
"data": {
"type": "terminal_output",
"payload": {
"stdout": "total 8\ndrwxr-xr-x 2 user user 4096 Jan 01 12:00 .\ndrwxr-xr-x 20 root root 4096 Jan 01 12:00 ..\n",
"stderr": "",
"exit_code": 0
}
}
}
}
TunnelError (Agent → Server)
{
"type": "tunnel_error",
"payload": {
"channel_id": "terminal-1",
"error": "Failed to execute command: No such file or directory"
}
}
TunnelClose (Server → Agent)
{
"type": "tunnel_close",
"payload": {
"session_id": "550e8400-e29b-41d4-a716-446655440000"
}
}
HTTP API Endpoints
Open Tunnel
POST /api/v1/tunnel/open
Authorization: Bearer <jwt_token>
Content-Type: application/json
{
"agent_id": "550e8400-e29b-41d4-a716-446655440000"
}
Response:
{
"session_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"status": "active"
}
Status Codes:
- 200 OK - Tunnel opened successfully
- 400 Bad Request - Invalid agent_id format
- 404 Not Found - Agent not connected
- 409 Conflict - Active session already exists
Close Tunnel
POST /api/v1/tunnel/close
Authorization: Bearer <jwt_token>
Content-Type: application/json
{
"session_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7"
}
Response:
{
"status": "closed"
}
Status Codes:
- 200 OK - Tunnel closed successfully
- 400 Bad Request - Invalid session_id format
- 403 Forbidden - Session not owned by user
- 404 Not Found - Session not found
Get Tunnel Status
GET /api/v1/tunnel/status/{session_id}
Authorization: Bearer <jwt_token>
Response:
{
"session_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"agent_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "active",
"opened_at": "2026-04-14T10:30:00Z",
"last_activity": "2026-04-14T10:31:45Z"
}
Status Codes:
- 200 OK - Status retrieved successfully
- 400 Bad Request - Invalid session_id format
- 403 Forbidden - Session not owned by user
- 404 Not Found - Session not found
Database Schema
CREATE TABLE tech_sessions (
id SERIAL PRIMARY KEY,
session_id VARCHAR(36) UNIQUE NOT NULL,
tech_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
agent_id UUID NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
opened_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
last_activity TIMESTAMPTZ NOT NULL DEFAULT NOW(),
closed_at TIMESTAMPTZ,
status VARCHAR(20) NOT NULL DEFAULT 'active',
CONSTRAINT unique_active_session UNIQUE (tech_id, agent_id, status)
WHERE status = 'active'
);
Indexes:
idx_tech_sessions_techontech_ididx_tech_sessions_agentonagent_ididx_tech_sessions_statusonstatus
Error Codes
PostgreSQL Errors
23505- Unique constraint violation (handled as 409 Conflict)
HTTP Status Codes
400- Bad Request (invalid UUID format, malformed JSON)401- Unauthorized (missing/invalid JWT token)403- Forbidden (session not owned by user)404- Not Found (agent offline, session doesn't exist)409- Conflict (active session already exists)500- Internal Server Error (database failure, unexpected error)
Implementation Checklist
Phase 1 (Complete)
- Database schema (
tech_sessionstable) - Server message types (
TunnelOpen,TunnelClose,TunnelData) - Agent message types (
TunnelReady,TunnelData,TunnelError) - HTTP API endpoints (open, close, status)
- WebSocket message handlers (all 3 agent messages)
- Session ownership validation
- Unique constraint handling (409 Conflict)
- Agent disconnect cleanup
- Foreign key constraints
- Error logging and monitoring
Phase 2 (Pending)
- Client WebSocket endpoint for output streaming
- Command execution endpoint (send Terminal commands)
- Output buffering/forwarding to clients
- Client connection tracking
- Real-time output streaming
- Command audit logging
Last Updated: 2026-04-14