# JetKVM Multi-Session Support ## Table of Contents 1. [Overview](#overview) 2. [Session Modes](#session-modes) 3. [Session Settings](#session-settings) 4. [Session Lifecycle](#session-lifecycle) 5. [Grace Period & Reconnection](#grace-period--reconnection) 6. [Session Approval System](#session-approval-system) 7. [Primary Role Transfer](#primary-role-transfer) 8. [Emergency Promotion](#emergency-promotion) 9. [Permissions System](#permissions-system) 10. [Session Identification](#session-identification) 11. [Testing Guide](#testing-guide) --- ## Overview JetKVM supports multiple concurrent connections to a single device, allowing multiple users to collaborate or observe remote system management. The multi-session system uses a role-based access control model with automatic session management and conflict resolution. ### Key Features - **Multiple concurrent connections** - Up to 10 simultaneous sessions per device - **Role-based permissions** - Four distinct session modes with different privilege levels - **Automatic session promotion** - Seamless handoff between primary sessions - **Grace period reconnection** - Recover from accidental disconnects without losing control - **Session approval workflow** - Optional gating for new connections - **Transfer protection** - Prevent unwanted session takeovers - **Automatic nickname generation** - Identify sessions by browser type ### Design Philosophy The multi-session system is designed with the following principles: 1. **Always have a primary** - The system ensures at least one session has control when sessions exist 2. **Graceful degradation** - Accidental disconnects are protected by grace periods 3. **Manual control prioritized** - User-initiated actions take precedence over automatic promotion 4. **Security by default** - Emergency promotions use trust scoring to select the most appropriate session --- ## Session Modes Every session operates in one of four modes, each with different permissions and responsibilities. ### Primary Mode ![Primary Session Screenshot - placeholder for image] **Permissions**: Full control of the remote system The primary session has complete control over the KVM device and can: - View video feed - Send keyboard and mouse input - Control power (ATX/DC) - Mount/unmount virtual media - Access all system settings - Manage other sessions (approve, deny, transfer, kick) - Access serial console and terminal - Configure USB devices and extensions **Visual Indicators**: - Primary badge displayed in session list - Full control UI enabled - All menu options accessible **Automatic Behaviors**: - Only one primary session exists at any time - Primary session receives exclusive input control - Times out after 5 minutes of inactivity (configurable) - Can voluntarily release control to observers ### Observer Mode ![Observer Session Screenshot - placeholder for image] **Permissions**: View-only access with request capability Observer sessions can: - View video feed in real-time - See mounted media (but cannot mount/unmount) - Request primary control - View session list Observer sessions **cannot**: - Send keyboard/mouse input - Control power or hardware - Modify system settings - Manage other sessions **Visual Indicators**: - "Request Control" button visible - Input controls disabled/grayed out - Settings menu locked **Automatic Behaviors**: - Promoted to primary when no primary exists (if not blacklisted) - Can be promoted by current primary via manual transfer - Automatically becomes observer if primary is transferred away ### Queued Mode ![Queued Session Screenshot - placeholder for image] **Permissions**: Same as observer, with pending request status Queued sessions have: - Same permissions as observers - Visible indication that control has been requested - Position in request queue **Visual Indicators**: - "Request Pending" status shown - Queue position displayed (if multiple requests) - "Cancel Request" button available **Automatic Behaviors**: - Enters this mode after clicking "Request Control" - Primary session is notified of the request - Returns to observer if request is denied - Becomes primary if request is approved ### Pending Mode ![Pending Session Screenshot - placeholder for image] **Permissions**: No access until approved Pending sessions have: - **No video access** - Screen shows "Waiting for approval" - **No system access** - Cannot view or interact with anything - Optional nickname field (if required by settings) **Visual Indicators**: - "Waiting for approval" message - Nickname input field (if required) - No video feed or controls visible **Automatic Behaviors**: - Only active when "Require Approval" setting is enabled - Session must be approved by current primary - Becomes observer after approval - Automatically removed after 1 minute if not approved (security measure) --- ## Session Settings Session behavior is controlled through global settings that affect all current and future sessions. ### Require Approval **Default**: `false` **Type**: Boolean When enabled, new sessions must be explicitly approved by the current primary session before gaining access. **Use Cases**: - Secure environments where unauthorized viewing should be prevented - Limiting access to trusted users only - Preventing session spam or unauthorized monitoring **Behavior**: - New sessions start in Pending mode with no video access - Primary receives notification with session details (source, identity, nickname) - Primary can approve or deny access - If no primary exists, the system uses emergency promotion to select a trusted session **Security Note**: This setting prevents unauthorized video access but requires a primary session to approve new connections. If the primary disconnects, the system will automatically promote the most trusted pending session to prevent deadlock. ### Require Nickname **Default**: `false` **Type**: Boolean When enabled, sessions must provide a valid nickname before being approved. **Use Cases**: - Identify multiple concurrent users - Maintain audit trail of session activity - Prevent anonymous connections **Behavior**: - Sessions without nicknames cannot be approved - Approval request is delayed until nickname is provided - Nickname must be 2-30 characters, alphanumeric with dashes/underscores - If disabled, nicknames are auto-generated based on browser type **Auto-Generated Nicknames**: When nickname requirement is disabled, sessions receive automatic nicknames in the format: - `u-chrome-a1b2` - Chrome browser - `u-firefox-c3d4` - Firefox browser - `u-safari-e5f6` - Safari browser - `u-edge-g7h8` - Edge browser - `u-user-i9j0` - Unknown browser ### Reconnect Grace Period **Default**: `10` seconds **Type**: Integer (1-300 seconds) **Configurable**: Yes Grace period duration for primary session reconnection after disconnect. **Purpose**: Prevents accidental loss of control due to: - Network hiccups - Browser tab refresh - Page navigation - Temporary connection issues **Behavior**: - Primary session disconnects → grace period starts - Primary slot is reserved for the disconnected session - Other sessions cannot become primary during grace period - Original session can reclaim primary status if reconnecting within grace period - If grace period expires without reconnection, next eligible session is promoted **Technical Details**: - Grace period is cleared on intentional logout - Manual transfers bypass grace period (immediate promotion) - Grace period does not apply to observer sessions (they reconnect as observers) ### Primary Timeout **Default**: `300` seconds (5 minutes) **Type**: Integer **Configurable**: Yes **Special Values**: `0` = disabled (no timeout) Inactivity timeout for primary session. **Purpose**: - Prevent abandoned sessions from holding control indefinitely - Free up primary slot when user walks away - Ensure active users have access to control **Activity Tracking**: Primary session is considered "active" when: - Mouse movement detected - Keyboard input sent - Any RPC method called - WebRTC keep-alive ping received **Behavior**: - Timer resets on each activity - After timeout expires, primary is demoted to observer - Next eligible session is automatically promoted to primary - Original session can request control again after demotion ### Private Keystrokes **Default**: `false` **Type**: Boolean When enabled, keystroke events are only broadcast to the primary session. **Purpose**: - Prevent observer sessions from seeing typed passwords - Protect sensitive data entry - Maintain privacy during credential input **Behavior**: - When `false`: All sessions see keystroke notifications (for awareness) - When `true`: Only primary session receives keystroke events - Mouse movements are always visible to all sessions - Does not affect actual input execution (only event visibility) **Use Cases**: - Password entry during login - Entering API keys or tokens - Private configuration data - Any sensitive text input ### Maximum Rejection Attempts **Default**: `3` **Type**: Integer (1-10) Maximum number of times a session can be rejected before automatic blocking. **Purpose**: - Prevent spam from repeatedly requesting approval - Rate limit approval request DoS attacks - Automatic blacklisting of problematic sessions **Behavior**: - Counter increments on each denial - After reaching limit, session is automatically disconnected - Counter resets after 60 seconds of no requests - Does not affect legitimate new connection attempts --- ## Session Lifecycle ### Connection Establishment ``` [Browser] → [WebRTC Handshake] → [Session Creation] → [Mode Assignment] ``` 1. **Initial Connection** - Client connects via WebRTC - Session ID generated (UUID) - Source and identity extracted from connection metadata - Browser type detected from User-Agent 2. **Mode Assignment Logic** ``` IF session exists in grace period: Reconnect to existing session (preserve mode) ELSE IF no primary exists AND not blacklisted: Assign Primary mode ELSE IF approval required AND primary exists: Assign Pending mode ELSE: Assign Observer mode ``` 3. **Validation & Broadcasting** - Session added to manager - Single primary verified (auto-fix if multiple) - All sessions notified of new connection - Primary notified if approval required ### Normal Disconnection ``` [User Logout] → [Clear Grace Period] → [Remove Session] → [Promote Next] ``` **Intentional Logout Flow**: 1. Session calls logout/disconnect method 2. Grace period is cleared (marked as intentional) 3. Session removed from manager 4. If was primary: immediate promotion of next eligible session 5. All sessions notified of removal **Characteristics**: - No grace period applied - Immediate promotion if primary leaves - Clean session cleanup - No reconnection possibility ### Accidental Disconnection ``` [Connection Lost] → [Grace Period Active] → [Await Reconnect | Timeout] ↓ ↓ [Reconnect Success] [Promote Next] ``` **Accidental Disconnect Flow**: 1. Connection drops (network issue, browser issue, etc.) 2. Grace period started (default 10 seconds) 3. Session info preserved in reconnection map 4. Primary slot reserved (if was primary) 5. **Wait for reconnection**: - If reconnects within grace: restore original mode - If grace expires: session removed, next promoted **Grace Period Details**: - Grace period duration configurable (1-300 seconds) - Maximum 10 concurrent grace periods (DoS protection) - Oldest entries evicted if limit reached - Grace periods cleared on new primary establishment ### Reconnection Scenarios #### Scenario 1: Primary Reconnects Within Grace Period ``` Time 0s: Primary disconnects (network issue) Time 0s: Grace period starts (10 seconds) Time 3s: Primary reconnects → RESTORED as primary ✓ ``` **Result**: Session seamlessly reclaims primary status, no interruption to workflow. #### Scenario 2: Primary Reconnects After Grace Expiry ``` Time 0s: Primary disconnects Time 0s: Grace period starts (10 seconds) Time 12s: Grace expires → Observer B promoted to primary Time 15s: Original primary reconnects → Becomes observer ✗ ``` **Result**: Original primary returns as observer, must request control if needed. #### Scenario 3: Observer Reconnects ``` Time 0s: Observer disconnects Time 0s: Grace period starts Time 5s: Observer reconnects → RESTORED as observer ✓ ``` **Result**: Observer sessions always reconnect as observers (no primary slot reservation). #### Scenario 4: Multiple Rapid Disconnects ``` Time 0s: Primary A disconnects → Grace period active Time 1s: Observer B promoted (emergency) Time 2s: Primary B disconnects → Grace period active Time 3s: Observer C promoted (emergency, rate limit bypassed) ``` **Result**: System ensures at least one primary always exists, bypassing rate limits when necessary. --- ## Grace Period & Reconnection ### Grace Period Mechanics The grace period is a time window during which a disconnected session can reclaim its previous role without interruption. **Key Properties**: - **Per-Session**: Each session gets its own grace period - **Mode-Aware**: Different behavior for primary vs observer sessions - **Expiration-Based**: Time-bound protection window - **Blacklist-Aware**: Respects manual transfer blacklisting ### Primary Grace Period When a primary session disconnects accidentally: 1. **Grace Period Activation** ```go sm.primarySessionID = "" // Clear active slot sm.lastPrimaryID = sessionID // Remember who it was sm.reconnectGrace[sessionID] = now + 10s // Set expiration ``` 2. **Protection During Grace** - Primary slot remains empty (no one can claim it) - `lastPrimaryID` tracks the rightful owner - Other sessions cannot be promoted to primary - Requests are queued instead of immediately granted 3. **Successful Reconnection** ```go IF session.ID == sm.lastPrimaryID AND not blacklisted: session.Mode = Primary sm.primarySessionID = session.ID sm.lastPrimaryID = "" // Clear tracking ``` 4. **Grace Expiration** - After 10 seconds (configurable), grace period expires - System promotes most eligible observer to primary - If approval required, uses trust-based emergency promotion - Original session becomes observer if it reconnects later ### Observer Grace Period When an observer session disconnects: 1. **Grace Period Activation** - Same as primary, but no primary slot reservation - Session info stored in reconnection map - No impact on other sessions 2. **Successful Reconnection** - Restores as observer - No promotion or special treatment - Session resumes viewing 3. **Grace Expiration** - Session removed from grace map - No promotion occurs - Session gone forever unless new connection made ### Blacklist Interaction **Transfer Blacklisting** (60-second duration): When a manual transfer occurs (A → B): 1. Session A is demoted to observer 2. Session A is blacklisted for 60 seconds 3. All other sessions (except B) are blacklisted for 60 seconds 4. Grace periods for blacklisted sessions are cleared **Purpose**: Prevent immediate re-takeover after manual transfer **Grace Period Impact**: - Blacklisted sessions cannot reclaim primary during grace period - Reconnection still works, but session becomes observer instead - Blacklist expires after 60 seconds, then normal grace period rules apply ### Grace Period Edge Cases #### Case 1: Grace Period During Manual Transfer ``` Time 0s: Primary A disconnects → Grace active Time 3s: Observer B manually requests control Time 3s: Primary A's grace cleared, B promoted Time 5s: Primary A reconnects → Becomes observer (blacklisted) ``` **Reason**: Manual user action takes precedence over grace period. #### Case 2: Multiple Grace Periods ``` Primary A disconnects → Grace A active (expires at T+10s) Observer B disconnects → Grace B active (expires at T+10s) Observer C disconnects → Grace C active (expires at T+10s) ``` **Limit**: Maximum 10 concurrent grace periods (DoS protection) **Eviction**: Oldest grace period removed if limit exceeded #### Case 3: Grace Period with Approval Required ``` Primary disconnects → Grace active New session connects → Becomes pending (no primary to approve) Grace expires → Pending session promoted via emergency promotion ``` **Reason**: System must always have a primary when sessions exist. --- ## Session Approval System The approval system gates new connections, requiring explicit permission from the current primary before granting access. ### Approval Workflow ![Approval Workflow Diagram - placeholder for image] ``` [New Session] → [Pending Mode] → [Primary Notified] → [Approve/Deny] → [Observer/Disconnect] ``` #### Step 1: New Session Arrives ```go IF RequireApproval enabled AND primary exists: session.Mode = Pending session.hasPermission(VideoView) = false // No video access ``` **Result**: Session sees "Waiting for approval" screen, no video feed. #### Step 2: Primary Notification Primary session receives JSON-RPC event: ```json { "method": "newSessionPending", "params": { "sessionId": "uuid-here", "source": "cloud" | "local", "identity": "user@example.com", "nickname": "user-chrome-a1b2" } } ``` **UI Display**: - Notification badge appears - Session list shows pending session with "Approve" and "Deny" buttons - Session details visible (source, nickname) #### Step 3: Nickname Validation (if required) ```go IF RequireNickname enabled AND nickname empty: // Wait for nickname before showing approval request return ``` **Behavior**: - Approval request delayed until nickname provided - Pending session shows nickname input field - After nickname entered, approval request sent to primary #### Step 4: Primary Decision **Approve Action**: ```json { "method": "approveNewSession", "params": { "sessionId": "uuid-here" } } ``` **Result**: - Session promoted to Observer mode - Video access granted - Session can now view and request control - All sessions notified of mode change **Deny Action**: ```json { "method": "denyNewSession", "params": { "sessionId": "uuid-here" } } ``` **Result**: - Rejection counter incremented - Session receives "Access Denied" message - Connection closed after 5 seconds - If rejection counter exceeds max (default 3), session is blocked ### Approval Security #### DoS Protection **Rate Limiting**: - Maximum 3 rejections per session (configurable) - After reaching limit, session auto-disconnected - 60-second cooldown before counter resets **Pending Session Timeout**: - Pending sessions removed after 1 minute if not approved - Prevents accumulation of stale pending sessions - Logged as "timed-out pending session" **Maximum Pending Sessions**: - System tracks and limits pending session count - Oldest pending sessions removed if limit reached #### Emergency Approval Bypass **Scenario**: Primary disconnects while sessions are pending ``` Primary disconnects → Grace expires → No primary exists BUT pending sessions exist → DEADLOCK RISK ``` **Solution**: Emergency promotion with approval bypass ```go IF no primary exists AND approval required: // Find most trusted pending/observer session session = findMostTrustedSessionForEmergency() session.Mode = Primary // Bypass approval requirement LogWarning("EMERGENCY: Bypassing approval to prevent deadlock") ``` **Trust Scoring**: Sessions are scored based on: - Session age (longer = more trusted, up to 100 points) - Previous primary status (+50 points) - Current mode (observer +20, queued +10, pending +0) - Nickname presence (if required: +15 if present, -30 if missing) **Highest Score Wins**: Most trusted session promoted to prevent deadlock. ### Approval Testing Scenarios #### Test 1: Basic Approval Flow 1. Enable "Require Approval" 2. Connect Session A → Becomes primary 3. Connect Session B → Becomes pending 4. Session B shows "Waiting for approval", no video 5. Session A receives notification 6. Session A approves → Session B becomes observer with video access **Expected**: Clean approval flow, video access granted after approval. #### Test 2: Approval with Nickname Required 1. Enable "Require Approval" and "Require Nickname" 2. Connect Session A → Becomes primary 3. Connect Session B (no nickname) → Becomes pending 4. Session B shows nickname input field 5. Session A does NOT receive notification (waiting for nickname) 6. Session B enters nickname "TestUser" 7. Session A NOW receives notification 8. Session A approves → Session B becomes observer **Expected**: Approval delayed until nickname provided. #### Test 3: Approval Denial 1. Enable "Require Approval" 2. Connect Session A → Becomes primary 3. Connect Session B → Becomes pending 4. Session A denies → Session B disconnected 5. Session B reconnects → Becomes pending again 6. Session A denies → Rejection counter = 2 7. Session B reconnects → Becomes pending again 8. Session A denies → Session B blocked (max rejections reached) **Expected**: After 3 rejections, session auto-blocked. #### Test 4: Emergency Approval Bypass 1. Enable "Require Approval" 2. Connect Session A → Becomes primary 3. Connect Session B → Becomes pending (no video) 4. Session A disconnects (close browser) 5. Grace period starts (10 seconds) 6. Wait for grace expiration 7. Session B automatically promoted to primary (emergency bypass) **Expected**: Session B gains primary status despite pending mode. --- ## Primary Role Transfer The system supports multiple ways to transfer primary control between sessions. ### Transfer Types #### 1. Direct Transfer (Manual) **Initiated By**: Current primary session **Target**: Specific observer or queued session **Method**: `transferPrimary(fromID, toID)` **Flow**: ``` Primary A → "Transfer to Session B" → Direct transfer ``` **Behavior**: 1. Session A demoted to observer 2. Session A blacklisted for 60 seconds 3. Session B promoted to primary 4. Session B blacklist entry removed 5. All other sessions blacklisted for 60 seconds 6. Grace periods cleared 7. `lastPrimaryID` cleared (prevents reconnection as primary) **Use Case**: Primary voluntarily gives control to specific session. **UI**: "Transfer Control" button next to observer sessions. #### 2. Approval Transfer **Initiated By**: Queued session requesting control **Target**: Requesting session **Method**: `approveRequest(requesterID)` **Flow**: ``` Observer B → "Request Control" → Queued → Primary A approves → Transfer ``` **Behavior**: - Same as direct transfer - Requester removed from queue - Primary receives notification of request - Approval UI shown in session list **Use Case**: Observer asks for control, primary grants it. #### 3. Release Transfer **Initiated By**: Current primary session **Target**: Next eligible observer **Method**: `releasePrimary()` **Flow**: ``` Primary A → "Release Control" → Find next observer → Auto-promote ``` **Behavior**: 1. Primary A demoted to observer 2. System finds next eligible session (not blacklisted) 3. Selected session promoted to primary 4. Blacklist applied to protect new primary 5. Queue order respected (queued sessions first) **Use Case**: Primary gives up control without specifying recipient. **UI**: "Release Control" button in session menu. #### 4. Emergency Promotion **Initiated By**: System (automatic) **Target**: Most eligible/trusted session **Trigger**: Primary timeout, grace expiration, or deadlock **Types of Emergency Promotion**: **a) Emergency Auto-Promotion** (`emergency_auto_promotion`) - Occurs when no primary exists and no grace period active - Triggered by validation checks - Selects any eligible observer (if approval not required) - Uses trust scoring if approval required **b) Emergency Timeout Promotion** (`emergency_timeout_promotion`) - Occurs when primary times out due to inactivity - Primary timeout default: 5 minutes - Trust scoring used if approval required - Excludes timed-out session from promotion **c) Emergency Deadlock Prevention** (`emergency_promotion_deadlock_prevention`) - Occurs when grace period expires without reconnection - Prevents system from having no primary - Trust scoring used if approval required - Rate limited (30 seconds between emergency promotions) **Rate Limiting**: ```go IF no primary exists: Bypass all rate limits // CRITICAL: Must always have primary ELSE: IF last emergency < 30 seconds ago: Block promotion (potential DoS attack) IF consecutive emergencies >= 3: Block promotion (security protection) ``` **Trust Scoring Algorithm**: ``` score = 0 score += min(sessionAge.Minutes(), 100) // Up to 100 points for age IF lastPrimaryID == sessionID: score += 50 // Previous primary bonus IF mode == Observer: score += 20 ELSE IF mode == Queued: score += 10 IF nickname required AND nickname present: score += 15 IF nickname required AND nickname missing: score -= 30 ``` ### Transfer Protection (Blacklisting) **Purpose**: Prevent unwanted immediate re-takeover after manual transfer. **Duration**: 60 seconds **Applied To**: All sessions except newly promoted primary **Mechanism**: ```go type TransferBlacklistEntry struct { SessionID string ExpiresAt time.Time } ``` **Only Applied During Manual Transfers**: - Direct transfer - Approval transfer - Release transfer **NOT Applied During Emergency Promotions**: - Emergency auto-promotion - Emergency timeout promotion - Emergency deadlock prevention - Initial promotion (first session) **Reasoning**: - Manual transfers = user-initiated, need protection from immediate reversal - Emergency promotions = system-initiated for availability, must happen immediately **Effects**: - Blacklisted sessions cannot become primary - Blacklisted sessions cannot be selected for promotion - Grace period reconnection respects blacklist - Expires after 60 seconds automatically ### Transfer Testing Scenarios #### Test 1: Direct Transfer 1. Session A is primary, Session B is observer 2. Session A clicks "Transfer to Session B" 3. Session A becomes observer 4. Session B becomes primary 5. Session A is blacklisted for 60 seconds 6. Session A tries to request control → Blocked by blacklist 7. Wait 60 seconds 8. Session A requests control → Allowed **Expected**: Clean transfer with 60-second protection. #### Test 2: Transfer with Refresh 1. Session A is primary, Session B is observer 2. Session A clicks "Transfer to Session B" 3. Session B becomes primary 4. Session B refreshes browser (WebRTC reconnection) 5. Session B reconnects and REMAINS primary **Expected**: Session B does not lose primary status on refresh. #### Test 3: Primary Logout with Observers 1. Session A is primary, Sessions B and C are observers 2. Session A clicks "Logout" 3. Session A disconnects (intentional) 4. Session B OR C promoted immediately (no grace period) **Expected**: Instant promotion, no delay. #### Test 4: Primary Timeout 1. Session A is primary, Session B is observer 2. Session A becomes inactive (no input for 5 minutes) 3. After 5 minutes, Session A demoted to observer 4. Session B promoted to primary automatically 5. Session A can request control again **Expected**: Automatic handoff on timeout. --- ## Emergency Promotion Emergency promotion is the system's safety mechanism to ensure there is always a primary session when sessions exist. ### When Emergency Promotion Occurs #### Trigger 1: No Primary Exists (Validation) **Scenario**: System detects no primary during periodic validation ```go // Runs every 10 seconds func validateSinglePrimary() { IF primaryCount == 0 AND totalSessions > 0 AND no active grace period: EmergencyPromote() } ``` **Common Causes**: - Bug in session management - Race condition during transfers - Manual state corruption **Resolution**: Automatic promotion of next eligible session. #### Trigger 2: Primary Timeout **Scenario**: Primary session inactive for too long (default 5 minutes) ```go IF now - primarySession.LastActive > 5 minutes: DemotePrimary() EmergencyPromote() ``` **Activity Resets Timer**: - Mouse movement - Keyboard input - Any RPC method call - WebRTC ping/pong **Resolution**: Timed-out session demoted, next session promoted. #### Trigger 3: Grace Period Expiration **Scenario**: Primary disconnects, grace period expires without reconnection ```go IF grace period expired AND lastPrimaryID set: ClearPrimarySlot() EmergencyPromote() ``` **Timeline**: ``` T+0s: Primary disconnects T+0s: Grace period starts (10 seconds) T+10s: Grace expires T+10s: Emergency promotion triggered ``` **Resolution**: Most eligible session promoted to prevent indefinite wait. #### Trigger 4: Multiple Rapid Disconnects **Scenario**: Primary and newly promoted session both disconnect quickly ``` T+0s: Primary A disconnects T+1s: Observer B promoted (emergency) T+2s: Primary B disconnects (in background tab, ICE gathering stuck) T+2s: Observer C promoted (emergency, rate limit bypassed) ``` **Critical Fix**: Rate limits bypassed when no primary exists ```go hasPrimary := sm.primarySessionID != "" IF !hasPrimary: LogError("CRITICAL: No primary exists - bypassing all rate limits") // Promote immediately, ignore rate limits ``` **Reasoning**: System availability takes precedence over rate limiting when deadlock is imminent. ### Emergency Promotion Selection #### Without Approval Requirement **Algorithm**: Simple first-eligible selection ```go // Check queue first IF queueOrder not empty: FOR each queued session: IF not blacklisted: RETURN session // Then check observers FOR each session WHERE mode == Observer: IF not blacklisted: RETURN session // Last resort: pending sessions FOR each session WHERE mode == Pending: IF not blacklisted: RETURN session ``` **Priority Order**: 1. Queued sessions (in queue order) 2. Observer sessions (arbitrary order) 3. Pending sessions (last resort) #### With Approval Requirement **Algorithm**: Trust-based scoring ```go bestSessionID = "" bestScore = -1 // First pass: Observers and Queued FOR each session WHERE mode IN [Observer, Queued]: IF not blacklisted: score = calculateTrustScore(session) IF score > bestScore: bestScore = score bestSessionID = session.ID // Second pass: Pending (only if no observers found) IF bestSessionID == "": FOR each session WHERE mode == Pending: IF not blacklisted: score = calculateTrustScore(session) IF score > bestScore: bestScore = score bestSessionID = session.ID ``` **Trust Scoring Factors**: | Factor | Points | Reasoning | |--------|--------|-----------| | Session age | 0-100 | Older sessions more likely legitimate | | Was previous primary | +50 | Proven trusted user | | Observer mode | +20 | Already has video access | | Queued mode | +10 | Actively waiting | | Pending mode | +0 | Not yet trusted | | Has nickname (when required) | +15 | Engaged user | | Missing nickname (when required) | -30 | Incomplete setup | **Example Scoring**: ``` Session A: age=2min, observer, nickname="Admin" Score = 2 + 20 + 15 = 37 Session B: age=30min, was primary, observer, nickname="Bob" Score = 30 + 50 + 20 + 15 = 115 ← SELECTED Session C: age=1min, pending, no nickname Score = 1 + 0 - 30 = -29 ``` ### Emergency Promotion Rate Limiting **Purpose**: Prevent DoS attacks via rapid session churn **Limits**: - **Time-based**: 30 seconds between emergency promotions - **Consecutive**: Maximum 3 consecutive emergency promotions - **Bypass**: Both limits bypassed when NO primary exists **Implementation**: ```go isEmergencyPromotion := false hasPrimary := sm.primarySessionID != "" IF approvalRequired: isEmergencyPromotion = true IF !hasPrimary: LogError("CRITICAL: No primary - bypassing rate limits") // Promote immediately ELSE: IF now - lastEmergencyPromotion < 30 seconds: LogWarn("Emergency rate limit exceeded") RETURN // Skip this promotion IF consecutiveEmergencyPromotions >= 3: LogError("Too many consecutive emergencies") RETURN // Skip this promotion ``` **Counter Reset**: - Consecutive counter resets on successful manual transfer - Time limit is absolute (30 second window) **Logging**: All emergency promotions logged with: - Reason (timeout, grace expiration, deadlock prevention) - Selected session ID - Trust score (if approval required) - Whether rate limits were bypassed ### Emergency Promotion Testing #### Test 1: No Primary Detection 1. Start with Session A as primary 2. Manually corrupt state: `sm.primarySessionID = ""` 3. Wait 10 seconds (validation runs) 4. Session A should be re-detected and set as primary **Expected**: System auto-corrects invalid state. #### Test 2: Primary Timeout 1. Session A becomes primary 2. Stop all activity (don't move mouse, don't type) 3. Wait 5 minutes 4. Session B should be promoted automatically 5. Session A should become observer **Expected**: Clean timeout and promotion. #### Test 3: Grace Period Expiration 1. Session A is primary, Session B is observer 2. Session A disconnects (network disconnect simulation) 3. Grace period starts (10 seconds) 4. Wait 15 seconds (let grace expire) 5. Session B promoted to primary **Expected**: Session B takes over after grace expires. #### Test 4: Rapid Disconnect Handling 1. Session A is primary, Sessions B and C are observers 2. Session A disconnects 3. Session B promoted (emergency) 4. IMMEDIATELY: Session B disconnects 5. Session C promoted (rate limit bypassed) **Expected**: System maintains primary availability despite rapid disconnects. #### Test 5: Emergency with Approval 1. Enable "Require Approval" 2. Session A is primary 3. Sessions B and C are pending (not approved) 4. Session A disconnects permanently 5. Grace expires 6. Session B OR C promoted (trust scoring selects highest) **Expected**: System bypasses approval requirement to prevent deadlock. --- ## Permissions System The permission system controls what actions each session mode can perform. ### Permission Model **Architecture**: Role-Based Access Control (RBAC) Each session mode has a predefined set of permissions that cannot be modified at runtime. This ensures consistent security boundaries. ### Permission Categories #### Video & Display - `video.view` - View video feed #### Input Control - `keyboard.input` - Send keyboard input - `mouse.input` - Send mouse input - `clipboard.paste` - Paste from clipboard #### Session Management - `session.transfer` - Transfer primary to another session - `session.approve` - Approve pending sessions - `session.kick` - Disconnect other sessions - `session.request_primary` - Request primary control - `session.release_primary` - Release primary control - `session.manage` - Modify session settings #### Power & Hardware - `power.control` - ATX/DC power control - `usb.control` - USB device management #### Mount & Media - `mount.media` - Mount virtual media - `mount.unmedia` - Unmount virtual media - `mount.list` - View mounted media #### Extensions - `extension.manage` - Enable/disable extensions - `extension.atx` - ATX power extension - `extension.dc` - DC power extension - `extension.serial` - Serial console extension - `extension.wol` - Wake-on-LAN extension #### Terminal & Serial - `terminal.access` - SSH terminal access - `serial.access` - Serial console access #### Settings - `settings.read` - Read system settings - `settings.write` - Modify system settings - `settings.access` - Access settings UI #### System Operations - `system.reboot` - Reboot JetKVM - `system.update` - Update firmware - `system.network` - Network configuration ### Permissions by Mode #### Primary Permissions Primary sessions have **ALL** permissions: ``` ✓ video.view ✓ keyboard.input ✓ mouse.input ✓ clipboard.paste ✓ session.transfer ✓ session.approve ✓ session.kick ✓ session.release_primary ✓ session.manage ✓ power.control ✓ usb.control ✓ mount.media ✓ mount.unmedia ✓ mount.list ✓ extension.* (all) ✓ terminal.access ✓ serial.access ✓ settings.* (all) ✓ system.* (all) ✗ session.request_primary (not needed) ``` #### Observer Permissions Observer sessions have **limited** permissions: ``` ✓ video.view ✓ session.request_primary ✓ mount.list (view only) ✗ All other permissions denied ``` #### Queued Permissions Queued sessions have **same as observer**: ``` ✓ video.view ✓ session.request_primary ✗ All other permissions denied ``` #### Pending Permissions Pending sessions have **NO** permissions: ``` ✗ video.view (no video access) ✗ ALL other permissions denied ``` ### Permission Enforcement **Check Timing**: Permissions are checked at multiple layers: 1. **RPC Method Call** - Before executing any JSON-RPC method 2. **UI Rendering** - Frontend hides unavailable controls 3. **WebRTC Channels** - Input channels only active for primary **Enforcement Flow**: ```go // RPC handler func handleRPCMethod(session *Session, method string) { requiredPermission := GetMethodPermission(method) IF session does NOT have requiredPermission: RETURN "Permission denied: {permission}" // Execute method } ``` **Permission Errors**: ```json { "error": { "code": -32000, "message": "Permission denied: keyboard.input" } } ``` ### Method-to-Permission Mapping **Power Control**: ``` setATXPowerAction → power.control setDCPowerState → power.control setDCRestoreState → power.control ``` **USB Management**: ``` setUsbDeviceState → usb.control setUsbDevices → usb.control ``` **Mount Operations**: ``` mountUsb → mount.media unmountUsb → mount.media mountBuiltInImage → mount.media getMassStorageMode → mount.list (read-only) ``` **Settings Operations**: ``` setNetworkSettings → settings.write setVideoFramerate → settings.write setSessionSettings → session.manage getNetworkSettings → settings.read ``` **Session Operations**: ``` approveNewSession → session.approve denyNewSession → session.approve transferSession → session.transfer requestPrimary → session.request_primary releasePrimary → session.release_primary ``` **Input Operations**: ``` keyboardReport → keyboard.input keypressReport → keyboard.input absMouseReport → mouse.input relMouseReport → mouse.input ``` *See full mapping in `/internal/session/permissions.go` lines 148-300* ### Permission Testing #### Test 1: Observer Input Block 1. Session A is primary, Session B is observer 2. Session B attempts keyboard input via dev console: ```javascript ws.send(JSON.stringify({ method: "keyboardReport", params: { keys: ["a"] } })) ``` 3. Should receive: `Permission denied: keyboard.input` **Expected**: Observer cannot send input. #### Test 2: Observer Settings Block 1. Session B (observer) tries to access Settings page 2. Should see "Permission Denied" or redirect 3. Cannot view or modify any settings **Expected**: Settings completely inaccessible to observers. #### Test 3: Primary Full Access 1. Session A is primary 2. Verify can access: - All settings pages - Power controls - Mount/unmount media - Session management - Input controls **Expected**: Primary has unrestricted access. #### Test 4: Pending No Video 1. Enable "Require Approval" 2. Session B connects → Pending mode 3. Session B should see NO video feed 4. Session B cannot call `getVideoState` (permission denied) **Expected**: Pending sessions completely blocked from video. --- ## Session Identification Sessions are identified through multiple attributes for security, auditing, and user recognition. ### Session Identity Components #### Session ID (UUID) **Format**: UUID v4 (e.g., `f47ac10b-58cc-4372-a567-0e02b2c3d479`) **Properties**: - **Unique**: Globally unique identifier - **Persistent**: Maintained across reconnections (within grace period) - **Generated**: Server-side on first connection - **Used For**: All internal session tracking and RPC references **Reconnection**: ```go IF session.ID exists in grace period map: Reconnect to existing session ELSE: Create new session with new UUID ``` #### Source **Values**: `"cloud"` or `"local"` **Determination**: - `cloud` - Connection via cloud relay (using cloud token) - `local` - Direct local network connection **Used For**: - Session display in UI - Trust scoring (local connections may be trusted more) - Audit logging **Visual**: - Cloud sessions show cloud icon - Local sessions show local network icon #### Identity **Format**: String (email, username, or IP address) **Sources**: - Cloud connections: User's email from OAuth - Local connections: IP address or configured username **Properties**: - **Not Unique**: Multiple sessions can have same identity - **Display**: Shown in session list - **Validation**: Used to verify reconnection legitimacy **Example**: ``` Cloud: "alice@example.com" Local: "192.168.1.100" ``` #### Nickname **Format**: 2-30 characters, alphanumeric with dashes/underscores **Pattern**: `^[a-zA-Z0-9_-]+$` **Sources**: - **User-provided**: When "Require Nickname" enabled - **Auto-generated**: When nickname not required **Auto-Generation Format**: ``` u-{browser}-{short-id} Examples: u-chrome-a1b2 u-firefox-c3d4 u-safari-e5f6 u-edge-g7h8 u-user-i9j0 (unknown browser) ``` **Short ID**: Last 4 characters of session UUID (lowercase) **Used For**: - User-friendly identification - Session list display - Approval notifications - Audit logs **Validation**: ```go IF len(nickname) < 2: ERROR "Nickname must be at least 2 characters" IF len(nickname) > 30: ERROR "Nickname must be 30 characters or less" IF !matches pattern: ERROR "Nickname can only contain letters, numbers, dashes, and underscores" ``` #### Browser Type **Detected From**: User-Agent header **Supported Browsers**: - `chrome` - Chrome/Chromium - `firefox` - Firefox - `safari` - Safari - `edge` - Edge - `opera` - Opera - `user` - Unknown/other **Detection Logic**: ```go ua := strings.ToLower(userAgent) IF contains "edg/" OR "edge": RETURN "edge" ELSE IF contains "firefox": RETURN "firefox" ELSE IF contains "chrome": RETURN "chrome" ELSE IF contains "safari" AND NOT "chrome": RETURN "safari" ELSE IF contains "opera" OR "opr/": RETURN "opera" ELSE: RETURN "user" ``` **Used For**: - Auto-generating nicknames - Browser-specific UI adjustments - Debugging connection issues #### Created At **Type**: Timestamp (RFC 3339) **Set**: On initial session creation **Persistent**: Maintained across reconnections **Used For**: - Session age display - Trust scoring (older = more trusted) - Audit logs #### Last Active **Type**: Timestamp (RFC 3339) **Updated On**: - Mouse movement - Keyboard input - Any RPC method call - WebRTC ping/pong **Used For**: - Primary timeout calculation - Inactivity detection - Session sorting (most recent first) ### Session Display in UI **Session List Format**: ``` ┌─────────────────────────────────────────────┐ │ 👤 u-chrome-a1b2 [PRIMARY] │ │ alice@example.com │ │ Local • Active 2m ago │ │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ │ │ │ 👤 u-firefox-c3d4 [OBSERVER] │ │ bob@example.com │ │ Cloud • Active 5s ago │ │ [Request Control] │ │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ │ │ │ 👤 TestUser [QUEUED] │ │ charlie@example.com │ │ Local • Active 1m ago │ │ Request Pending (#1 in queue) │ │ [Approve] [Deny] │ │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │ │ │ │ 👤 (pending) [PENDING] │ │ david@example.com │ │ Cloud • Connected 30s ago │ │ Waiting for approval │ │ [Approve] [Deny] │ └─────────────────────────────────────────────┘ ``` **Display Elements**: - **Nickname**: Large, prominent - **Mode Badge**: Color-coded (Primary=green, Observer=blue, Queued=yellow, Pending=gray) - **Identity**: Smaller, secondary text - **Source Icon**: Cloud or local network icon - **Activity**: Relative time ("2m ago", "just now") - **Actions**: Context-specific buttons ### Identity Security **Session Hijacking Prevention**: When a session reconnects: ```go IF existing.Identity != session.Identity: RETURN "Session ID already in use by different user" IF existing.Source != session.Source: RETURN "Session ID already in use by different user" ``` **Reasoning**: Prevents malicious actors from stealing session IDs. **Nickname Spoofing Prevention**: - Nicknames validated server-side - Cannot impersonate other sessions - Auto-generated nicknames use session-specific data --- ## Testing Guide This section provides comprehensive testing scenarios for all multi-session features. ### Test Environment Setup **Prerequisites**: 1. JetKVM device (hardware or development environment) 2. Multiple browsers (Chrome, Firefox, Safari) for multi-session testing 3. Network access (local and/or cloud) 4. Admin access to session settings **Recommended Setup**: ``` Browser 1 (Chrome): Primary session Browser 2 (Firefox): Observer session Browser 3 (Safari): Test session ``` **Configuration Reset**: Before each test suite, reset to defaults: ```json { "requireApproval": false, "requireNickname": false, "reconnectGrace": 10, "primaryTimeout": 300, "privateKeystrokes": false, "maxRejectionAttempts": 3 } ``` ### Core Functionality Tests #### TEST-001: Basic Multi-Session **Objective**: Verify multiple sessions can connect simultaneously **Steps**: 1. Connect with Browser 1 → Verify becomes primary 2. Connect with Browser 2 → Verify becomes observer 3. Connect with Browser 3 → Verify becomes observer 4. Check session list shows all 3 sessions 5. Verify Browser 1 can control, Browsers 2&3 can only view **Expected**: - ✓ 3 sessions visible in session list - ✓ 1 primary, 2 observers - ✓ Only primary can send input - ✓ All sessions see video feed **Pass Criteria**: All checkmarks verified --- #### TEST-002: Primary Transfer **Objective**: Verify primary role can be transferred between sessions **Steps**: 1. Browser 1 is primary, Browser 2 is observer 2. Browser 1 clicks "Transfer Control" next to Browser 2 3. Verify Browser 2 becomes primary 4. Verify Browser 1 becomes observer 5. Verify Browser 2 can now send input 6. Verify Browser 1 cannot send input **Expected**: - ✓ Smooth transition of primary role - ✓ UI updates immediately - ✓ Input control switches correctly - ✓ No video interruption **Pass Criteria**: All checkmarks verified --- #### TEST-003: Request and Approve Control **Objective**: Verify observers can request and receive control **Steps**: 1. Browser 1 is primary, Browser 2 is observer 2. Browser 2 clicks "Request Control" 3. Verify Browser 2 enters queued state 4. Verify Browser 1 sees approval notification 5. Browser 1 clicks "Approve" 6. Verify Browser 2 becomes primary 7. Verify Browser 1 becomes observer **Expected**: - ✓ Request notification appears for primary - ✓ Queued state displayed correctly - ✓ Approval transfers control smoothly **Pass Criteria**: All checkmarks verified --- #### TEST-004: Request and Deny Control **Objective**: Verify primary can deny control requests **Steps**: 1. Browser 1 is primary, Browser 2 is observer 2. Browser 2 clicks "Request Control" 3. Browser 1 clicks "Deny" 4. Verify Browser 2 returns to observer state 5. Verify Browser 1 remains primary 6. Verify Browser 2 can request again **Expected**: - ✓ Denial returns session to observer - ✓ No disruption to primary - ✓ Can request multiple times (until limit) **Pass Criteria**: All checkmarks verified --- #### TEST-005: Release Control **Objective**: Verify primary can voluntarily release control **Steps**: 1. Browser 1 is primary, Browsers 2&3 are observers 2. Browser 1 clicks "Release Control" 3. Verify Browser 1 becomes observer 4. Verify Browser 2 OR Browser 3 becomes primary (system selects) 5. Verify new primary can send input **Expected**: - ✓ Primary releases control successfully - ✓ Observer auto-promoted - ✓ All sessions functioning correctly **Pass Criteria**: All checkmarks verified --- ### Grace Period Tests #### TEST-006: Grace Period Reconnection (Success) **Objective**: Verify primary can reconnect within grace period **Steps**: 1. Browser 1 is primary 2. Close Browser 1 tab (simulating accidental close) 3. Wait 3 seconds (within 10-second grace) 4. Reopen Browser 1 (same session ID via browser history) 5. Verify reconnects as primary **Expected**: - ✓ Session reconnects successfully - ✓ Primary status restored - ✓ No interruption to other sessions **Pass Criteria**: All checkmarks verified --- #### TEST-007: Grace Period Expiration **Objective**: Verify grace period expires and promotes observer **Steps**: 1. Browser 1 is primary, Browser 2 is observer 2. Close Browser 1 tab 3. Wait 15 seconds (exceeds 10-second grace) 4. Verify Browser 2 becomes primary 5. Reopen Browser 1 6. Verify Browser 1 reconnects as observer **Expected**: - ✓ Browser 2 promoted after grace expires - ✓ Browser 1 returns as observer - ✓ No double-primary state **Pass Criteria**: All checkmarks verified --- #### TEST-008: Intentional Logout (No Grace Period) **Objective**: Verify logout bypasses grace period **Steps**: 1. Browser 1 is primary, Browser 2 is observer 2. Browser 1 clicks "Logout" button 3. Verify Browser 2 immediately becomes primary (no 10-second wait) 4. Reopen Browser 1 5. Verify Browser 1 enters as new session (observer) **Expected**: - ✓ Immediate promotion on logout - ✓ No grace period applied - ✓ Clean session cleanup **Pass Criteria**: All checkmarks verified --- ### Approval System Tests #### TEST-009: Basic Approval Flow **Objective**: Verify approval system gates new connections **Steps**: 1. Enable "Require Approval" 2. Browser 1 connects → Becomes primary 3. Browser 2 connects → Becomes pending 4. Verify Browser 2 sees "Waiting for approval" (no video) 5. Verify Browser 1 sees approval notification 6. Browser 1 clicks "Approve" 7. Verify Browser 2 becomes observer with video access **Expected**: - ✓ Pending session has no video access - ✓ Approval notification displayed - ✓ Video access granted after approval **Pass Criteria**: All checkmarks verified --- #### TEST-010: Approval with Nickname Required **Objective**: Verify nickname requirement gates approval **Steps**: 1. Enable "Require Approval" and "Require Nickname" 2. Browser 1 connects → Becomes primary 3. Browser 2 connects (no nickname provided) → Becomes pending 4. Verify Browser 1 does NOT see approval notification yet 5. Browser 2 enters nickname "TestUser" 6. Verify Browser 1 NOW sees approval notification 7. Browser 1 approves 8. Verify Browser 2 becomes observer **Expected**: - ✓ Approval delayed until nickname provided - ✓ Nickname requirement enforced - ✓ Approval proceeds after nickname entered **Pass Criteria**: All checkmarks verified --- #### TEST-011: Approval Denial and Rejection Limit **Objective**: Verify rejection limit blocks spam **Steps**: 1. Enable "Require Approval", set max rejections = 3 2. Browser 1 is primary 3. Browser 2 connects → Pending 4. Browser 1 denies → Browser 2 disconnected (count=1) 5. Browser 2 reconnects → Pending 6. Browser 1 denies → Browser 2 disconnected (count=2) 7. Browser 2 reconnects → Pending 8. Browser 1 denies → Browser 2 blocked (count=3) 9. Browser 2 tries to reconnect → Connection rejected **Expected**: - ✓ Rejection counter increments - ✓ After 3 rejections, session auto-blocked - ✓ Cannot reconnect after blocking **Pass Criteria**: All checkmarks verified --- #### TEST-012: Emergency Approval Bypass **Objective**: Verify pending session promoted if primary leaves **Steps**: 1. Enable "Require Approval" 2. Browser 1 is primary 3. Browser 2 connects → Pending (no video) 4. Browser 1 disconnects permanently (close browser) 5. Wait 15 seconds (grace period expires) 6. Verify Browser 2 promoted to primary (approval bypassed) 7. Verify Browser 2 now has video access and full control **Expected**: - ✓ Pending session promoted to prevent deadlock - ✓ Emergency bypass logged - ✓ System maintains availability **Pass Criteria**: All checkmarks verified --- ### Timeout Tests #### TEST-013: Primary Inactivity Timeout **Objective**: Verify inactive primary is demoted **Steps**: 1. Set primary timeout = 60 seconds (for faster testing) 2. Browser 1 is primary, Browser 2 is observer 3. Browser 1: Do NOT move mouse or type for 60 seconds 4. After 60 seconds, verify Browser 1 demoted to observer 5. Verify Browser 2 promoted to primary **Expected**: - ✓ Timeout triggers at configured interval - ✓ Inactive session demoted - ✓ Observer auto-promoted **Pass Criteria**: All checkmarks verified --- #### TEST-014: Timeout with Activity **Objective**: Verify activity resets timeout timer **Steps**: 1. Set primary timeout = 60 seconds 2. Browser 1 is primary 3. Wait 50 seconds 4. Browser 1: Move mouse (reset timer) 5. Wait another 50 seconds 6. Browser 1: Type something (reset timer) 7. Wait 50 seconds 8. Verify Browser 1 still primary (timeout keeps resetting) **Expected**: - ✓ Activity prevents timeout - ✓ Timer resets on each action - ✓ Primary retained indefinitely with activity **Pass Criteria**: All checkmarks verified --- ### Permission Tests #### TEST-015: Observer Input Block **Objective**: Verify observers cannot send input **Steps**: 1. Browser 1 is primary, Browser 2 is observer 2. Browser 2: Try clicking on video feed 3. Browser 2: Try typing 4. Browser 2: Open browser console, try: ```javascript // Send keyboard input via RPC rpc.call("keyboardReport", {keys: ["a"]}) ``` 5. Verify all input attempts blocked 6. Verify error: "Permission denied: keyboard.input" **Expected**: - ✓ UI input ignored for observers - ✓ RPC calls return permission error - ✓ No input reaches device **Pass Criteria**: All checkmarks verified --- #### TEST-016: Observer Settings Block **Objective**: Verify observers cannot access settings **Steps**: 1. Browser 1 is primary, Browser 2 is observer 2. Browser 2: Try to navigate to Settings page 3. Verify redirected or blocked 4. Browser 2: Open console, try: ```javascript rpc.call("setNetworkSettings", {dhcp: false}) ``` 5. Verify error: "Permission denied: settings.write" **Expected**: - ✓ Settings page inaccessible - ✓ Settings RPC calls blocked - ✓ No configuration changes possible **Pass Criteria**: All checkmarks verified --- #### TEST-017: Pending Video Block **Objective**: Verify pending sessions have no video access **Steps**: 1. Enable "Require Approval" 2. Browser 1 is primary 3. Browser 2 connects → Pending 4. Verify Browser 2 shows "Waiting for approval" message 5. Verify Browser 2 has NO video feed 6. Browser 2: Open console, try: ```javascript rpc.call("getVideoState", {}) ``` 7. Verify error: "Permission denied: video.view" **Expected**: - ✓ No video feed visible - ✓ Video RPC calls blocked - ✓ Complete video blackout until approved **Pass Criteria**: All checkmarks verified --- ### Edge Case Tests #### TEST-018: Multiple Rapid Disconnects **Objective**: Verify system maintains primary during rapid churn **Steps**: 1. Browser 1 is primary, Browsers 2&3 are observers 2. Close Browser 1 → Browser 2 promoted 3. IMMEDIATELY close Browser 2 (within 1 second) → Browser 3 promoted 4. Verify Browser 3 is now primary 5. Verify rate limiting did NOT block promotion **Expected**: - ✓ System always maintains a primary - ✓ Rate limits bypassed when necessary - ✓ No deadlock state **Pass Criteria**: All checkmarks verified --- #### TEST-019: Transfer with Immediate Refresh **Objective**: Verify transferred session retains primary on refresh **Steps**: 1. Browser 1 is primary, Browser 2 is observer 2. Browser 1 transfers to Browser 2 3. Browser 2 becomes primary 4. IMMEDIATELY: Browser 2 refresh page (F5) 5. Verify Browser 2 reconnects as primary 6. Verify Browser 1 remains observer **Expected**: - ✓ Refresh does not lose primary status - ✓ No reversion to previous primary - ✓ Transfer is permanent **Pass Criteria**: All checkmarks verified --- #### TEST-020: Blacklist Expiration **Objective**: Verify transfer blacklist expires after 60 seconds **Steps**: 1. Browser 1 is primary, Browser 2 is observer 2. Browser 1 transfers to Browser 2 3. Browser 1 now observer and blacklisted 4. Browser 1 tries to request control → Blocked (blacklisted) 5. Wait 60 seconds 6. Browser 1 tries to request control → Allowed 7. Browser 2 approves 8. Verify Browser 1 becomes primary **Expected**: - ✓ Blacklist blocks immediate re-takeover - ✓ Blacklist expires after 60 seconds - ✓ Normal flow resumes after expiration **Pass Criteria**: All checkmarks verified --- ### Stress Tests #### TEST-021: Maximum Session Limit **Objective**: Verify maximum session limit enforced **Steps**: 1. Connect 10 browsers (maximum) 2. Verify all 10 sessions accepted 3. Attempt to connect 11th browser 4. Verify connection rejected with "Maximum sessions reached" **Expected**: - ✓ Exactly 10 sessions allowed - ✓ 11th connection rejected - ✓ Existing sessions unaffected **Pass Criteria**: All checkmarks verified --- #### TEST-022: Rapid Connect/Disconnect **Objective**: Verify system stable under rapid session churn **Steps**: 1. Script or manually: Connect and disconnect 20 sessions rapidly 2. Monitor system logs for errors 3. Verify session manager remains stable 4. Verify at least one session becomes primary **Expected**: - ✓ No crashes or deadlocks - ✓ System maintains consistency - ✓ Clean session cleanup **Pass Criteria**: All checkmarks verified --- #### TEST-023: Grace Period DoS Protection **Objective**: Verify grace period limit prevents memory exhaustion **Steps**: 1. Connect 15 sessions 2. Disconnect all 15 sessions rapidly (create 15 grace periods) 3. Verify only 10 grace periods maintained (oldest evicted) 4. Monitor memory usage (should not spike) **Expected**: - ✓ Grace period limit enforced - ✓ Oldest entries evicted - ✓ No memory exhaustion **Pass Criteria**: All checkmarks verified --- ### Session Settings Tests #### TEST-024: Private Keystrokes **Objective**: Verify keystroke privacy setting works **Steps**: 1. Enable "Private Keystrokes" 2. Browser 1 is primary, Browser 2 is observer 3. Browser 1: Type "test123" 4. Browser 2: Verify NO keystroke events received 5. Disable "Private Keystrokes" 6. Browser 1: Type "test123" 7. Browser 2: Verify keystroke events received (for awareness) **Expected**: - ✓ Private mode blocks keystroke visibility - ✓ Non-private mode shows keystrokes - ✓ Mouse events always visible **Pass Criteria**: All checkmarks verified --- #### TEST-025: Configurable Grace Period **Objective**: Verify grace period duration is configurable **Steps**: 1. Set grace period = 30 seconds 2. Browser 1 is primary 3. Close Browser 1 4. Wait 15 seconds 5. Reopen Browser 1 → Should reconnect as primary 6. Close Browser 1 again 7. Wait 35 seconds 8. Reopen Browser 1 → Should reconnect as observer **Expected**: - ✓ Grace period respects configured duration - ✓ Reconnection successful within window - ✓ Promotion occurs after expiration **Pass Criteria**: All checkmarks verified --- ### Regression Tests #### TEST-026: No Double Primary **Objective**: Verify system prevents multiple primary sessions **Steps**: 1. Connect multiple sessions 2. Perform various transfers and promotions 3. After each action, verify exactly 1 primary exists 4. Check system logs for "Multiple primary sessions detected" **Expected**: - ✓ Always exactly 1 primary - ✓ No double-primary state - ✓ Automatic correction if detected **Pass Criteria**: All checkmarks verified --- #### TEST-027: Orphaned Primary Cleanup **Objective**: Verify orphaned primary IDs are cleaned up **Steps**: 1. Browser 1 is primary 2. Simulate crash: Kill browser without proper disconnect 3. Wait for system to detect disconnect 4. Verify primary slot cleared 5. Verify observer promoted 6. Check no orphaned primary ID remains **Expected**: - ✓ Orphaned primary detected - ✓ Automatic cleanup - ✓ New primary promoted **Pass Criteria**: All checkmarks verified --- ### Performance Tests #### TEST-028: Session List Update Latency **Objective**: Verify session list updates are timely **Steps**: 1. Have 5 sessions connected 2. Transfer primary from Browser 1 to Browser 2 3. Measure time for session list to update on all browsers 4. Verify update within 500ms **Expected**: - ✓ Updates received within 500ms - ✓ All sessions updated simultaneously - ✓ No stale UI states **Pass Criteria**: All checkmarks verified --- #### TEST-029: Broadcast Throttling **Objective**: Verify broadcast throttling prevents spam **Steps**: 1. Rapidly transfer primary 10 times in 1 second 2. Monitor session list update events 3. Verify updates throttled (not 10 updates) 4. Verify final state is correct **Expected**: - ✓ Broadcasts throttled to prevent spam - ✓ Final state accurate - ✓ No performance degradation **Pass Criteria**: All checkmarks verified --- ### Test Results Template For each test, record results in this format: ``` TEST-XXX: [Test Name] Date: YYYY-MM-DD Tester: [Name] Device: JetKVM [Model] Firmware: [Version] Result: PASS / FAIL / BLOCKED Notes: - [Any observations] - [Deviations from expected behavior] - [Performance metrics if applicable] Issues Found: - [Issue #1 description] - [Issue #2 description] ``` --- ## Troubleshooting ### Common Issues #### Issue: Session stuck in Pending mode **Symptoms**: Session shows "Waiting for approval" indefinitely **Causes**: - Primary session disconnected before approving - Pending session timeout (1 minute) not expired yet **Solutions**: 1. Wait for pending timeout (1 minute) 2. If primary exists, manually approve/deny 3. If no primary, system will auto-promote after grace period --- #### Issue: Cannot send input as primary **Symptoms**: Primary session cannot control mouse/keyboard **Causes**: - WebRTC HID channel not established - Permission error - Browser compatibility issue **Solutions**: 1. Refresh browser (F5) 2. Check browser console for errors 3. Verify primary badge is visible 4. Try different browser (Chrome recommended) --- #### Issue: Video not visible for observer **Symptoms**: Observer sees black screen **Causes**: - Permission issue (if pending) - WebRTC connection failure - Video feed not active **Solutions**: 1. Verify session mode is Observer (not Pending) 2. Check WebRTC connection status 3. Verify HDMI input is active 4. Refresh browser --- #### Issue: Grace period not working **Symptoms**: Primary loses control on reconnect **Causes**: - Manual transfer occurred (cleared grace) - Grace period expired - Session blacklisted **Solutions**: 1. Check grace period duration setting 2. Verify reconnection within grace window 3. Check if session was manually transferred (60s blacklist) 4. Review logs for grace period events --- #### Issue: Emergency promotion selecting wrong session **Symptoms**: Unexpected session becomes primary **Causes**: - Trust scoring selected different session - Blacklist blocking preferred session **Solutions**: 1. Review trust score logs 2. Check blacklist status 3. Verify session age and properties 4. Use manual transfer for explicit control --- ## Configuration Reference ### Default Values ```json { "multi_session": { "enabled": true, "max_sessions": 10, "primary_timeout_seconds": 300, "allow_cloud_override": true, "require_auth_transfer": false }, "session_settings": { "requireApproval": false, "requireNickname": false, "reconnectGrace": 10, "primaryTimeout": 300, "privateKeystrokes": false, "maxRejectionAttempts": 3 } } ``` ### Recommended Configurations #### Secure Environment ```json { "session_settings": { "requireApproval": true, "requireNickname": true, "reconnectGrace": 10, "primaryTimeout": 300, "privateKeystrokes": true, "maxRejectionAttempts": 3 } } ``` **Use Case**: High-security environments, data centers, production systems --- #### Collaborative Environment ```json { "session_settings": { "requireApproval": false, "requireNickname": false, "reconnectGrace": 30, "primaryTimeout": 600, "privateKeystrokes": false, "maxRejectionAttempts": 5 } } ``` **Use Case**: Team collaboration, pair programming, training --- #### Personal Use ```json { "session_settings": { "requireApproval": false, "requireNickname": false, "reconnectGrace": 10, "primaryTimeout": 0, "privateKeystrokes": false, "maxRejectionAttempts": 3 } } ``` **Use Case**: Single user accessing from multiple devices --- ## Appendix ### Session State Diagram ``` [New Connection] ↓ ┌───────────────────────────────────────┐ │ Is there a primary? │ └───────────────────────────────────────┘ NO ↓ ↓ YES [Primary] ┌──────────────────┐ │ Approval needed? │ └──────────────────┘ NO ↓ ↓ YES [Observer] [Pending] ↓ ┌─────────────────┐ │ Request Control │ └─────────────────┘ ↓ [Queued] ↓ ┌─────────────────┐ │ Approved/Denied │ └─────────────────┘ ↓ ↓ [Primary] [Observer] ``` ### Glossary **Session**: A WebRTC connection from a browser to the JetKVM device **Primary**: The session with exclusive control over input and settings **Observer**: A session that can view video but cannot control anything **Queued**: A session that has requested control and is waiting for approval **Pending**: A session waiting for approval to even view video **Grace Period**: Time window for reconnection without losing session state **Blacklist**: Temporary block preventing a session from becoming primary **Transfer**: Changing primary status from one session to another **Emergency Promotion**: Automatic promotion when no primary exists **Trust Score**: Calculated value determining promotion priority --- ### Changelog **Version 1.0** (Initial Release) - Multi-session support with 4 session modes - Role-based permissions system - Grace period reconnection - Session approval workflow - Emergency promotion system - Transfer protection (blacklisting) - Automatic nickname generation - Trust-based session selection - Configurable timeouts and limits --- **Document Version**: 1.0 **Last Updated**: 2025 **Maintained By**: JetKVM Development Team