Commit Graph

359 Commits

Author SHA1 Message Date
Alex P f9e190f8b9 fix: prevent multiple getPermissions RPC calls on page load
The getPermissions useEffect had send and pollPermissions in its dependency
array. Since send gets recreated when rpcDataChannel changes, this caused
multiple getPermissions RPC calls (5 observed) on page load.

Fix:
- Add rpcDataChannel readiness check to prevent calls before channel is open
- Remove send and pollPermissions from dependency array
- Keep only currentMode and rpcDataChannel.readyState as dependencies

This ensures getPermissions is called only when:
1. The RPC channel becomes ready (readyState changes to "open")
2. The session mode changes (observer <-> primary)

Eliminates duplicate RPC calls while maintaining correct behavior for
mode changes and initial connection.
2025-10-10 22:23:25 +03:00
Alex P 00e6edbfa8 fix: prevent infinite getLocalVersion RPC calls on refresh
The getLocalVersion useEffect had getLocalVersion and hasPermission in
its dependency array. Since these functions are recreated on every render,
this caused an infinite loop of RPC calls when refreshing the primary
session, resulting in 100+ identical getLocalVersion requests.

Fix: Remove function references from dependency array, only keep appVersion
which is the actual data dependency. The effect now only runs once when
appVersion changes from null to a value.

This is the same pattern as the previous fix for getVideoState,
getKeyboardLedState, and getKeyDownState.
2025-10-10 22:15:44 +03:00
Alex P f90c255656 fix: prevent unnecessary RPC calls for pending sessions and increase rate limit
Changes:
- Add permission checks before making getVideoState, getKeyboardLedState,
  and getKeyDownState RPC calls to prevent rejected requests for sessions
  without VIDEO_VIEW permission
- Fix infinite loop issue by excluding hasPermission from useEffect
  dependency arrays (functions recreated on render cause infinite loops)
- Increase RPC rate limit from 100 to 500 per second to support 10+
  concurrent sessions with broadcasts and state updates

This eliminates console spam from permission denied errors and log spam
from continuous RPC calls, while improving multi-session performance.
2025-10-10 22:10:33 +03:00
Alex P 821675cd21 security: fix critical race conditions and add validation to session management
This commit addresses multiple CRITICAL and HIGH severity security issues
identified during the multi-session implementation review.

CRITICAL Fixes:
- Fix race condition in session approval handlers (jsonrpc.go)
  Previously approveNewSession and denyNewSession directly mutated
  session.Mode without holding the SessionManager.mu lock, potentially
  causing data corruption during concurrent access.

- Add validation to ApprovePrimaryRequest (session_manager.go:795-810)
  Now verifies that requester session exists and is in Queued mode
  before approving transfer, preventing invalid state transitions.

- Close dual-primary window during reconnection (session_manager.go:208)
  Added explicit primaryExists check to prevent brief window where two
  sessions could both be primary during reconnection.

HIGH Priority Fixes:
- Add nickname uniqueness validation (session_manager.go:152-159)
  Prevents multiple sessions from having the same nickname, both in
  AddSession and updateSessionNickname RPC handler.

Code Quality:
- Remove debug scaffolding from cloud.go (lines 515-520, 530)
  Cleaned up temporary debug logs that are no longer needed.

Thread Safety:
- Add centralized ApproveSession() method (session_manager.go:870-890)
- Add centralized DenySession() method (session_manager.go:894-912)
  Both methods properly acquire locks and validate session state.

- Update RPC handlers to use thread-safe methods
  approveNewSession and denyNewSession now call sessionManager methods
  instead of direct session mutation.

All changes have been verified with linters (golangci-lint: 0 issues).
2025-10-10 20:04:44 +03:00
Alex P 825299257d fix: correct grace period protection during primary reconnection
The session manager had backwards logic that prevented sessions from
restoring their primary status when reconnecting within the grace period.
This caused browser refreshes to demote primary sessions to observers.

Changes:
- Fix conditional in AddSession to allow primary restoration within grace
- Remove excessive debug logging throughout session manager
- Clean up unused decrActiveSessions function
- Remove unnecessary leading newline in NewSessionManager
- Update lastPrimaryID handling to support WebRTC reconnections
- Preserve grace periods during transfers to allow browser refreshes

The fix ensures that when a primary session refreshes their browser:
1. RemoveSession adds a grace period entry
2. New connection checks wasWithinGracePeriod and wasPreviouslyPrimary
3. Session correctly reclaims primary status

Blacklist system prevents demoted sessions from immediate re-promotion
while grace periods allow legitimate reconnections.
2025-10-10 19:33:49 +03:00
Alex P 309126bef6 [WIP] Bugfixes: session promotion 2025-10-10 10:16:21 +03:00
Alex P 8dbd98b4f0 Merge branch 'dev' into feat/multisession-support 2025-10-10 00:23:50 +03:00
Alex P ce1cbe1944 fix: move nil check before accessing session.ID to satisfy staticcheck 2025-10-10 00:05:08 +03:00
Aveline cc9ff74276
feat: add HDMI sleep mode (#881) 2025-10-09 14:52:51 +02:00
Alex P c8b456bf6a fix: handle intentional logout to trigger immediate observer promotion
When a user explicitly logs out via the logout button, the session should
be removed immediately without grace period, allowing observers to be
promoted right away instead of waiting for the grace period to expire.

Changes:
- Close WebRTC connection immediately on logout
- Clear grace period marker for intentional logout detection
- Add logging to track logout vs disconnect differentiation

This complements the accidental disconnect handling which uses grace period.
2025-10-09 12:56:57 +03:00
Alex P 57f4be2846 fix: clear transfer blacklist on primary disconnect to enable grace period promotion
When a primary session disconnects accidentally (not intentional logout), the
60-second transfer blacklist from previous role transfers was blocking observer
sessions from being promoted after the grace period expires (~10s).

The blacklist is intended to prevent immediate re-promotion during manual
transfers (user-initiated), but should not interfere with emergency promotion
after accidental disconnects (system-initiated).

Changes:
- Clear all transfer blacklist entries when primary enters grace period
- Add logging to track blacklist clearing for debugging
- Preserve blacklist during intentional logout to maintain manual transfer protection

This ensures observers are promoted after grace period (~10s) instead of
waiting for blacklist expiration (~40-60s).
2025-10-09 12:55:25 +03:00
Alex P b388bc3c62 fix: reduce observer promotion delay from ~40s to ~11s
1. Terminal access permission check:
   - Add Permission.TERMINAL_ACCESS check to Web Terminal button
   - Prevents observer sessions from accessing terminal

2. Immediate websocket cleanup:
   - Close peer connection immediately when websocket errors
   - Previously waited 24+ seconds for ICE to transition from disconnected to failed
   - Now triggers session cleanup immediately on tab close

3. Immediate grace period validation:
   - Trigger validateSinglePrimary() immediately when grace period expires
   - Previously waited up to 10 seconds for next periodic validation
   - Eliminates unnecessary delay in observer promotion

Timeline improvement:
Before: Tab close → 6s (ICE disconnect) → 24s (ICE fail) → RemoveSession → 10s grace → up to 10s validation = ~50s total
After: Tab close → immediate peerConnection.Close() → immediate RemoveSession → 10s grace → immediate validation = ~11s total
2025-10-09 11:39:00 +03:00
Alex P 7901677551 fix: increase RPC rate limit from 20 to 100 per second
The previous limit of 20 RPC/second per session was too aggressive for
multi-session scenarios. During normal operation with multiple sessions,
legitimate RPC calls would frequently hit the rate limit, especially
during page refreshes or reconnections when sessions make bursts of calls
like getSessions, getPermissions, getLocalVersion, and getVideoState.

Increased the limit to 100 RPC/second per session, which still provides
DoS protection while accommodating legitimate multi-session usage patterns.
2025-10-09 11:19:10 +03:00
Alex P ba8caf3448 debug: add detailed logging to trace session addition flow
Add comprehensive logging to identify why sessions fail to be added to
the session manager:
- Log entry/exit points in AddSession
- Track reconnection path execution
- Log max sessions limit checks
- Trace AddSession call and return in handleSessionRequest

This will help diagnose why sessions get stuck at ICE checking state
without being properly registered in the session manager.
2025-10-09 10:58:06 +03:00
Alex P 541d2bd77d fix: correct grace period protection during primary reconnection
- Remove broken bypass logic that caused immediate observer promotion on refresh
- Add session map debugging logs to validateSinglePrimary
- Ensure grace period properly blocks auto-promotion until expiration
2025-10-08 23:58:41 +03:00
Alex P f9ebd6ac2f feat: add strict observer-to-primary promotion controls and immediate logout promotion
Observer-to-primary promotion protections:
- Block auto-promotion during active primary grace periods
- Prevent creating multiple primary sessions simultaneously
- Validate transfer source is actual current primary
- Check for duplicate primaries before promotion

Immediate promotion on logout:
- Trigger validateSinglePrimary() immediately when primary disconnects
- Smart grace period bypass: allow promotion within 2 seconds of disconnect
- Provides instant promotion on logout while protecting against network blips

Enhanced validation and logging:
- Log session additions/removals with counts
- Display session IDs in validation logs for debugging
- Track grace period timing for smart bypass decisions
2025-10-08 23:44:10 +03:00
Alex P ffc4a2af21 fix: prevent getLocalVersion call for sessions without video permission
Sessions in pending mode do not have PermissionVideoView and should not
attempt to call getLocalVersion RPC method. Add permission check before
calling getLocalVersion to prevent unnecessary permission denied errors.
2025-10-08 21:42:59 +03:00
Alex P a1548fe5b1 feat: improve session approval workflow with re-request and rejection limits
Backend improvements:
- Keep denied sessions alive in pending mode instead of removing them
- Add requestSessionApproval RPC method for re-requesting access
- Fix security issue: preserve pending mode on reconnection for denied sessions
- Add MaxRejectionAttempts field to SessionSettings (default: 3, configurable 1-10)

Frontend improvements:
- Change "Try Again" button to "Request Access Again" that re-requests approval
- Add rejection counter with configurable maximum attempts
- Hide modal after max rejections; session stays pending in SessionPopover
- Add "Dismiss" button for primary to hide approval requests without deciding
- Add MaxRejectionAttempts control in multi-session settings page
- Reset rejection count when session is approved

This improves the user experience by allowing denied users to retry without
page reloads, while preventing spam with configurable rejection limits.
2025-10-08 21:37:02 +03:00
Alex P b0494e8eef security: prevent video access for pending/denied sessions
CRITICAL SECURITY FIX: Pending sessions (awaiting approval) were granted
video.view permission, allowing denied sessions to see video when they
reconnected.

**Vulnerability:**
1. Session requests access and enters pending mode
2. Primary session denies the request
3. Denied session clicks "Try Again" and reconnects
4. New session enters pending mode but has video.view permission
5. User can see video stream despite being denied

**Fix:**
Remove PermissionVideoView from SessionModePending. Pending sessions now
have NO permissions until explicitly approved by the primary session.

This ensures:
- Denied sessions cannot access video on reconnection
- Only approved sessions (observer/queued/primary) can view video
- CanReceiveVideo() properly blocks video frames for pending sessions
2025-10-08 20:26:18 +03:00
Alex P b322255684 fix: resolve all Go and TypeScript linting issues
Address all linting warnings and errors in both backend and frontend code:

**Go (golangci-lint):**
- Add error checking for ignored return values (errcheck)
- Remove unused RPC functions (unused)
- Fix import formatting (goimports)

**TypeScript/React (eslint):**
- Replace all 'any' and 'Function' types with proper type definitions
- Add RpcSendFunction type for consistent JSON-RPC callback signatures
- Fix React Hook exhaustive-deps warnings by adding missing dependencies
- Wrap functions in useCallback where needed to stabilize dependencies
- Remove unused variables and imports
- Remove empty code blocks
- Suppress exhaustive-deps warnings where intentional (with comments)

All linting now passes with 0 errors and 0 warnings.
2025-10-08 20:15:45 +03:00
Alex P cd70efb83f feat: multi-session support with role-based permissions
Implements concurrent WebRTC session management with granular permission control, enabling multiple users to connect simultaneously with different access levels.

Features:
- Session modes: Primary (full control), Observer (view-only), Queued, Pending
- Role-based permissions (31 permissions across video, input, settings, system)
- Session approval workflow with configurable access control
- Primary control transfer, request, and approval mechanisms
- Grace period reconnection (prevents interruption on network issues)
- Automatic session timeout and cleanup
- Nickname system with browser-based auto-generation
- Trust-based emergency promotion (deadlock prevention)
- Session blacklisting (prevents transfer abuse)

Technical Implementation:
- Centralized permission system (internal/session package)
- Broadcast throttling (100ms global, 50ms per-session) for DoS protection
- Defense-in-depth permission validation
- Pre-allocated event maps for hot-path performance
- Lock-free session iteration with snapshot pattern
- Comprehensive session management UI with real-time updates

New Files:
- session_manager.go (1628 lines) - Core session lifecycle
- internal/session/permissions.go (306 lines) - Permission rules
- session_permissions.go (77 lines) - Package integration
- datachannel_helpers.go (11 lines) - Permission denied handler
- errors.go (10 lines) - Error definitions
- 14 new UI components (session management, approval dialogs, overlays)

50 files changed, 5836 insertions(+), 442 deletions(-)
2025-10-08 18:52:45 +03:00
Marc Brooks b144d9926f
Remove the temporary directory after extracting buildkit (#874) 2025-10-07 11:57:26 +02:00
Aylen e755a6e1b1
Update openSUSE image reference to Leap 16.0 (#865) 2025-10-07 11:57:10 +02:00
Marc Brooks 99a8c2711c
Add podman support (#875)
Reimplement #141 since we've changed everything since
2025-10-07 11:43:25 +02:00
Marc Brooks 317218a682
docs: debugging UI builds because of ui symlink (#873)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-04 12:05:14 +02:00
Aveline 62b8dee170
chore: downgrade gin to v1.10.1 (#869) 2025-10-03 08:48:51 +02:00
Alex bdd6f4247b
fix: segfault in cGo 2025-10-02 19:15:03 +02:00
dependabot[bot] 63aa940f42
build(deps): bump github.com/prometheus/client_golang (#851)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 21:44:24 +02:00
dependabot[bot] 043ef9ddfc
build(deps): bump github.com/gin-gonic/gin from 1.10.1 to 1.11.0 (#852)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 21:44:08 +02:00
Marc Brooks 437f0b854a
upgrade ui packages (#861) 2025-10-01 21:43:46 +02:00
dependabot[bot] a45d55123c
build(deps): bump github.com/prometheus/common from 0.66.0 to 0.66.1 (#855)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 21:42:08 +02:00
dependabot[bot] 213e750e04
build(deps): bump github.com/coder/websocket from 1.8.13 to 1.8.14 (#854)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 21:41:48 +02:00
dependabot[bot] 6dcb0286e3
build(deps): bump golang.org/x/net from 0.43.0 to 0.44.0 (#856)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 21:40:57 +02:00
dependabot[bot] 74ccca0b1a
build(deps): bump golang.org/x/crypto from 0.41.0 to 0.42.0 (#849)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 21:39:31 +02:00
dependabot[bot] 0ad435475b
build(deps): bump github.com/go-co-op/gocron/v2 from 2.16.5 to 2.16.6 (#859)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 21:39:15 +02:00
dependabot[bot] 23bf3978fa
build(deps): bump actions/setup-go from 5 to 6 (#848)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-01 21:35:35 +02:00
Marc Brooks 9438ab7778
Fix the lazy loading for the settings page. (#837)
Move SettingsItem into it's own page

Packages upgraded
@headlessui/react 2.2.8 -> 2.2.9
framer-motion 12.23.18 -> 12/23/22
react-router 7.9.1 -> 7.9.3
@tailwindcss/typography 0.6.18 -> 0.5.19
@types/react 19.1.13 -> 19.1.14
eslint-plugin-react-refresh 0.4.21 -> 0.4.22
2025-10-01 11:43:15 +02:00
Alexander 12d31a3d8e
Add da_DK keyboard Layout (#839) 2025-09-30 14:27:17 +02:00
Aveline 657a177462
feat: jetkvm native in cGo 2025-09-29 14:09:30 +02:00
Marc Brooks 8f4081a5b1
Cleaning up action versions (#828) 2025-09-26 11:22:17 +02:00
Aveline 359778967f
chore: remove msgpack from gin dependency (#833) 2025-09-26 08:52:07 +02:00
Adam Shiervani 703625d59a
fix: faster paste speed (#824)
* fix: update delay handling in PasteModal component

- Changed default delay value to 20 and adjusted validation to allow values between 0 and 65534.
- Cleaned up code formatting for better readability.

* fix: formatting
2025-09-25 10:26:11 +02:00
Marc Brooks cb24ef6d4f
Update UI dependencies (#819) 2025-09-24 12:16:02 +02:00
Siyuan Miao fe77acd5f0 chore: bump to 0.4.8 2025-09-22 12:51:19 +02:00
Aveline 83caa8f82d
feat: get local version only (#813) 2025-09-19 13:45:59 +02:00
Adam Shiervani 27750b9cc2
feat: re-add keyboard and keypress report handlers to RPC (#811) 2025-09-18 17:33:08 +02:00
Adam Shiervani 5112bef19c
fix: remove unnecessary grow-0 utility from in keyboard (#810) 2025-09-18 17:02:08 +02:00
Siyuan Miao 1ffdca4fd6 build: use immediate assignment for VERSION_DEV and other vars 2025-09-18 15:41:39 +02:00
Siyuan Miao c6dba4d59f chore: bump to 0.4.7 2025-09-18 13:53:08 +02:00
Aveline afb146d78c
feat: release keyPress automatically (#796)
* feat: release keyPress automatically

* send keepalive when pressing the key

* remove logging

* clean up logging

* chore: use unreliable channel to send keepalive events

* chore: use ordered unreliable channel for pointer events

* chore: adjust auto release key interval

* chore: update logging for kbdAutoReleaseLock

* chore: update comment for KEEPALIVE_INTERVAL

* fix: should cancelAutorelease when pressed is true

* fix: handshake won't happen if webrtc reconnects

* chore: add trace log for writeWithTimeout

* chore: add timeout for KeypressReport

* chore: use the proper key to send release command

* refactor: simplify HID RPC keyboard input handling and improve key state management

- Updated `handleHidRPCKeyboardInput` to return errors directly instead of keys down state.
- Refactored `rpcKeyboardReport` and `rpcKeypressReport` to return errors instead of states.
- Introduced a queue for managing key down state updates in the `Session` struct to prevent input handling stalls.
- Adjusted the `UpdateKeysDown` method to handle state changes more efficiently.
- Removed unnecessary logging and commented-out code for clarity.

* refactor: enhance keyboard auto-release functionality and key state management

* fix: correct Windows default auto-repeat delay comment from 1ms to 1s

* refactor: send keypress as early as possible

* refactor: replace console.warn with console.info for HID RPC channel events

* refactor: remove unused NewKeypressKeepAliveMessage function from HID RPC

* fix: handle error in key release process and log warnings

* fix: log warning on keypress report failure

* fix: update auto-release keyboard interval to 225

* refactor: enhance keep-alive handling and jitter compensation in HID RPC

- Implemented staleness guard to ignore outdated keep-alive packets.
- Added jitter compensation logic to adjust timer extensions based on packet arrival times.
- Introduced new methods for managing keep-alive state and reset functionality in the Session struct.
- Updated auto-release delay mechanism to use dynamic durations based on keep-alive timing.
- Adjusted keep-alive interval in the UI to improve responsiveness.

* gofmt

* clean up code

* chore: use dynamic duration for scheduleAutoRelease

* Use harcoded timer reset value for now

* fix: prevent nil pointer dereference when stopping timers in Close method

* refactor: remove nil check for kbdAutoReleaseTimers in DelayAutoReleaseWithDuration

* refactor: optimize dependencies in useHidRpc hooks

* refactor: streamline keep-alive timer management in useKeyboard hook

* refactor: clarify comments in useKeyboard hook for resetKeyboardState function

* refactor: reduce keysDownStateQueueSize

* refactor: close and reset keysDownStateQueue in newSession function

* chore: resolve conflicts

* resolve conflicts

---------

Co-authored-by: Adam Shiervani <adam.shiervani@gmail.com>
2025-09-18 13:35:47 +02:00