fix(guacamole): coalesce VNC wheel floods and label meta keys by device - #1385
fix(guacamole): coalesce VNC wheel floods and label meta keys by device#1385neooriginal wants to merge 1 commit into
Conversation
High-latency proxies like Cloudflare queue guacamole-common-js scroll clicks so the desktop keeps crawling after the user stops. Cap in-flight wheel buttons, and show Super/Cmd in the VNC toolbar on non-Windows clients. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
🔵 Needs a closer look
WebSocket JWT-via-subprotocol requires server-side subprotocol selection to avoid handshake failures, and the PR scope materially exceeds the stated Guacamole-focused description.
Pull request overview
This PR improves client remote-connection UX and security primitives across the Termix UI/backend, including Guacamole input behavior changes and broader platform work (WebSocket auth, collab, credential/secret-source plumbing, RBAC hardening, and multiple infra tweaks).
Changes:
- Coalesce/adjust Guacamole input behaviors (e.g., wheel flood handling, meta key labeling) and related UX/error handling.
- Move WebSocket JWT transport away from URL query params toward protocol/header-based handling with shared helpers (UI + backend).
- Add/expand several adjacent capabilities and migrations (collab rail/tab scaffolding, Step CA auth type plumbing, secret sources, RBAC/system-role seeding, settings APIs, and extensive tests).
File summaries
| File | Description |
|---|---|
| src/ui/user/C2STunnelPresetManager.tsx | Backfill source host syncId |
| src/ui/tests/sidebar/tree/HostItem/HostItem.test.tsx | Right-click menu test |
| src/ui/tests/sidebar/rail-items.test.ts | Add collab rail id |
| src/ui/tests/sidebar/HostManagerData.test.ts | Preserve origin/shared id tests |
| src/ui/tests/sidebar/HostEditorData.test.ts | Auto-tmux defaulting test |
| src/ui/tests/sidebar/hidden-rail-tabs.test.ts | Hidden rail tabs storage test |
| src/ui/tests/sidebar/AdminUserManagePanel.test.tsx | Mock getUserList |
| src/ui/tests/lib/ws-auth.test.ts | ws subprotocol helper test |
| src/ui/tests/lib/useConnectionRetry.test.ts | Hook error-state test |
| src/ui/tests/lib/ui-font.test.ts | UI font persistence tests |
| src/ui/tests/lib/base-path.test.ts | Base path meta override test |
| src/ui/tests/lib/app-keyboard-shortcuts.test.ts | Shortcut dispatch tests |
| src/ui/tests/features/terminal/terminal-clipboard-actions.test.ts | Clipboard action tests |
| src/ui/tests/features/host-metrics/temperature-preference.test.ts | Temperature preference tests |
| src/ui/tests/features/guacamole/rdp-credential-prompt.test.ts | RDP prompt helper tests |
| src/ui/tests/features/guacamole/guacamole-pointer.test.ts | Wheel flood coalescing test |
| src/ui/tests/features/guacamole/guacamole-meta-key.test.ts | Meta-key label tests |
| src/ui/tests/features/guacamole/guacamole-filesystem.test.ts | File stream test additions |
| src/ui/tests/features/guacamole/guacamole-file-drop.test.ts | RDP drive upload gating test |
| src/ui/tests/features/guacamole/guacamole-clipboard.test.ts | Clipboard test rename/scope |
| src/ui/tests/features/file-manager/file-manager-utils.test.ts | Concurrency limiter test |
| src/ui/tests/features/ai/AiProviderSettings.test.tsx | Provider model refresh error test |
| src/ui/tests/components/connection/ConnectionScreen.test.tsx | Status role/data-status assertions |
| src/ui/tests/api/host-metrics-status-api.test.ts | Heartbeat behavior tests |
| src/ui/ssh/dialogs/OPKSSHDialog.tsx | Optional label in title |
| src/ui/sidebar/tree/SidebarTree.tsx | Selection bar enter animation class |
| src/ui/sidebar/SshToolsPanel.tsx | Add copy-on-select toggle |
| src/ui/sidebar/SerialPanel.tsx | WS auth via protocols |
| src/ui/sidebar/rail-items.ts | Add collab rail item |
| src/ui/sidebar/KeybindingsDialog.tsx | Add global actions + recorder marker |
| src/ui/sidebar/HostManagerData.ts | Add connectionOrigin + shared cred fields |
| src/ui/sidebar/HostManager.tsx | Credential share modal wiring |
| src/ui/sidebar/HostEditorGeneralTab.tsx | Connection toolbar setting |
| src/ui/sidebar/HostEditorFeatureTabs.tsx | Add stepca auth type |
| src/ui/sidebar/HostEditorData.ts | Auto-tmux from defaults |
| src/ui/sidebar/hidden-rail-tabs.ts | Shared hidden-tabs reader |
| src/ui/sidebar/FleetsPanel.tsx | Results list scrolling change |
| src/ui/sidebar/CredentialEditorView.tsx | Secret source manager UI |
| src/ui/sidebar/credential-tree/CredentialSidebarTree.tsx | Share action hook |
| src/ui/sidebar/ConnectionsPanel.tsx | Background desc includes timeout |
| src/ui/sidebar/AppRail.tsx | Add collab to rail view union |
| src/ui/shell/MobileBottomBar.tsx | Use shared hidden-tabs reader |
| src/ui/shell/CommandPalette.tsx | Controlled selection value |
| src/ui/onboarding/steps/AiAssistantStep.tsx | Use shared hidden-tabs reader |
| src/ui/main-axios.ts | Load copyOnSelect from Electron settings |
| src/ui/locales/translated/zh_CN.json | New translated strings |
| src/ui/lib/ws-auth.ts | JWT->subprotocol helper |
| src/ui/lib/useConnectionRetry.ts | Catch async connect failures |
| src/ui/lib/theme.ts | UI font support + applyUiFont |
| src/ui/lib/terminal-local-echo.ts | Rollback erase semantics |
| src/ui/lib/terminal-local-echo.test.ts | Rollback behavior tests |
| src/ui/lib/keybinding-dispatch.ts | Dispatch global keybinding events |
| src/ui/lib/connection-origin.ts | Return ws target + protocols |
| src/ui/lib/base-path.ts | Prefer meta base-path (CSP-safe) |
| src/ui/lib/app-keyboard-shortcuts.ts | Shift detection + Ctrl+W dispatch |
| src/ui/features/terminal/terminal-clipboard-actions.ts | Context menu resolution helpers |
| src/ui/features/serial/Serial.tsx | WS auth via protocols |
| src/ui/features/host-metrics/temperature-preference.ts | Host-scoped sensor preference |
| src/ui/features/host-metrics/cards/managers/ProcessInspectorCard.tsx | Prefer args over comm |
| src/ui/features/homepage/widgets/ClockWidget.tsx | Validate timezone before rendering |
| src/ui/features/homepage/dialogs/WidgetEditDialog.tsx | Validate clock timezone on save |
| src/ui/features/homepage/dialogs/ClockEditForm.tsx | Inline invalid timezone UX |
| src/ui/features/homepage/clock-timezone.ts | Normalize/validate timezone helpers |
| src/ui/features/guacamole/rdp-credential-prompt.ts | Decide RDP prompt need |
| src/ui/features/guacamole/GuacamoleFileBrowser.tsx | Upload error translation helper |
| src/ui/features/guacamole/guacamole-zoom.ts | Zoom clamp/step helpers |
| src/ui/features/guacamole/guacamole-zoom.test.ts | Zoom helper tests |
| src/ui/features/guacamole/guacamole-pointer.ts | Wheel flood coalescer integration |
| src/ui/features/guacamole/guacamole-filesystem.test.ts | Upload error description tests |
| src/ui/features/guacamole/guacamole-file-drop.ts | RDP drive upload eligibility helper |
| src/ui/features/guacamole/guacamole-clipboard.ts | Remove Firefox-only helper |
| src/ui/features/file-manager/FileManagerToolbar.tsx | Add density toggle |
| src/ui/features/file-manager/file-manager-utils.ts | Add concurrency runner |
| src/ui/features/file-manager/components/TransferProgressBar.tsx | Shared progress bar component |
| src/ui/features/file-manager/components/DownloadProgressToast.tsx | Use shared progress bar |
| src/ui/features/docker/components/ConsoleTerminal.tsx | Use ws protocols from origin resolver |
| src/ui/features/ai/AiProviderSettings.tsx | Model-detect warning details |
| src/ui/dashboard/DashboardTab.tsx | Metrics heartbeat pruning |
| src/ui/components/switch.tsx | Narrow transition properties |
| src/ui/components/proxmox/proxmox-import-auth.ts | stepca treated as secretless |
| src/ui/components/connection/ConnectionScreen.tsx | Accessible status + new spinner |
| src/ui/components/button.tsx | Narrow transition properties |
| src/ui/api/user-management-api.ts | Delete user successor parameter |
| src/ui/api/system-status-api.ts | TOTP setup credential param |
| src/ui/api/ssh-file-operations-api.ts | Chunk upload as octet-stream |
| src/ui/api/settings-api.ts | Terminal session + Step CA settings |
| src/ui/api/session-sharing-api.ts | Export resolveApiBaseUrl |
| src/ui/api/secret-sources-api.ts | Secret sources CRUD API |
| src/ui/api/host-terminal-config-api.ts | Patch host terminal config API |
| src/ui/api/host-metrics-status-api.ts | Heartbeat returns alive boolean |
| src/ui/api/audit-log-api.ts | Audit forwarding settings APIs |
| src/types/ui-types.ts | stepca + collab + UI font + shared creds |
| src/types/keybindings.ts | Add global keybinding actions |
| src/types/host-sidebar-preferences.ts | Add stepca filter |
| src/types/auth-protocols.ts | Support all override protocols |
| src/main.tsx | Apply UI font + collab guest view |
| src/backend/utils/ws-message.ts | Export MAX_WS_MESSAGE_BYTES |
| src/backend/utils/ws-auth.ts | Extract JWT from ws protocols/cookie/auth |
| src/backend/utils/system-roles.ts | Ensure seeded system roles exist |
| src/backend/utils/step-ca-egress.ts | Step CA private allowlist key |
| src/backend/utils/shared-host-auth-resolver.ts | Generalize owner-auth sharing logic |
| src/backend/utils/secret-source-egress.ts | Secret-source private allowlist key |
| src/backend/utils/request-origin.ts | Use proxy-validated req.ip |
| src/backend/utils/permission-manager.ts | Admin check fallback |
| src/backend/utils/permission-catalog.ts | Add credentials.share + role defaults |
| src/backend/utils/notification-sender.ts | Allowlisted private egress |
| src/backend/utils/notification-egress.ts | Parse/read notification allowlist |
| src/backend/utils/folder-access-inheritance.ts | Apply folder access rules |
| src/backend/utils/discord-sender.ts | Allowlisted private egress |
| src/backend/utils/cors-config.ts | Centralize origin allow decision |
| src/backend/utils/audit-forwarder.ts | Settings-backed audit forwarding |
| src/backend/tests/utils/ws-auth.test.ts | ws-auth extraction tests |
| src/backend/tests/utils/request-origin.test.ts | Updated IP trust tests |
| src/backend/tests/utils/onepassword-connect.test.ts | Secret reference parsing tests |
| src/backend/tests/utils/notification-egress.test.ts | Allowlist parser tests |
| src/backend/tests/utils/cors-config.test.ts | CORS allow/deny tests |
| src/backend/tests/utils/audit-username.test.ts | IP sourcing test updates |
| src/backend/tests/utils/audit-logger.test.ts | IP sourcing test updates |
| src/backend/tests/utils/audit-forwarder.test.ts | Mock settings reader |
| src/backend/tests/hosts/tunnel/c2s-source-host.test.ts | Stable sync-id resolution test |
| src/backend/tests/hosts/metrics/widgets/temperature-collector.test.ts | Device-qualified labels tests |
| src/backend/tests/hosts/metrics/status-poll-auth.test.ts | Guard against auth during polling |
| src/backend/tests/hosts/guacamole/rdp-settings.test.ts | Auth type resolution tests |
| src/backend/tests/hosts/guacamole/drive-settings.test.ts | Drive-path filling tests |
| src/backend/tests/hosts/file-manager/vault-auth.test.ts | Vault auth ordering guard |
| src/backend/tests/hosts/collab/runtime-store.test.ts | Runtime store behavior tests |
| src/backend/tests/hosts/collab/room-share-access.test.ts | Room stage share access tests |
| src/backend/tests/database/routes/snippets-shared-host.test.ts | Shared-host resolver regression test |
| src/backend/tests/database/routes/rbac-host-auth-override.test.ts | Auth override route behavior updates |
| src/backend/tests/database/routes/proxmox-jump-hosts.test.ts | Jump-host serialization tests |
| src/backend/tests/database/routes/keybinding-validation.test.ts | Global keybinding validation tests |
| src/backend/tests/database/routes/host-normalizers.test.ts | Protocol field normalization tests |
| src/backend/tests/database/routes/host-bulk-routes.test.ts | Username inference tests |
| src/backend/tests/database/routes/desktop-auto-session.test.ts | Native UA token export tests |
| src/backend/tests/database/routes/automations.test.ts | Permission middleware mocking |
| src/backend/tests/ai/tool-catalog.test.ts | Tool catalog boundary wording |
| src/backend/tests/ai/provider-http.test.ts | Private egress fetch path test |
| src/backend/tests/ai/egress.test.ts | Allowlisted DNS private hosts tests |
| src/backend/services/homepage.ts | Trust proxy loopback |
| src/backend/services/dashboard.ts | Trust proxy + bind localhost |
| src/backend/hosts/usable-credential.ts | Resolve shared credential snapshots |
| src/backend/hosts/tunnel/index.ts | Trust proxy + bind localhost |
| src/backend/hosts/tunnel/c2s-relay-utils.ts | Use ws-auth extractor |
| src/backend/hosts/tmux/index.ts | Trust proxy + bind localhost |
| src/backend/hosts/terminal/host-session-status.ts | Track host online state by count |
| src/backend/hosts/terminal/host-session-status.test.ts | Host session status tests |
| src/backend/hosts/session-sharing/live-sessions.ts | Live session ownership helpers |
| src/backend/hosts/serial.ts | Bind localhost + ws protocol auth |
| src/backend/hosts/metrics/widgets/temperature-collector.ts | Device-qualified sensors parsing |
| src/backend/hosts/metrics/helpers.ts | Treat issued cert auth as unsupported |
| src/backend/hosts/issued-certificate-auth.ts | Shared issued-certificate auth helper |
| src/backend/hosts/host-resolver.ts | Use shared credential gate + secret refs |
| src/backend/hosts/guacamole/rdp-settings.ts | Resolve auth type for connect |
| src/backend/hosts/guacamole/guacamole-server.ts | Bind guac WS localhost |
| src/backend/hosts/guacamole/drive-settings.ts | Per-user drive-path defaults |
| src/backend/hosts/file-manager/list-routes.ts | Avoid double-decoding path |
| src/backend/hosts/docker/routes.ts | Treat issued cert auth uniformly |
| src/backend/hosts/docker/index.ts | Trust proxy + bind localhost + auth order |
| src/backend/hosts/collab/stage-control.ts | Controller state helpers |
| src/backend/hosts/collab/room-share-access.ts | Room stage share auth |
| src/backend/hosts/collab/guest-rate-limit.ts | Guest join rate limiter |
| src/backend/hosts/cloudflare-websocket.ts | ws open wait + duplex wrapper |
| src/backend/hosts/auth-manager.ts | Resolve secret refs for creds |
| src/backend/database/routes/user-totp-routes.ts | Additional authenticator enrollment |
| src/backend/database/routes/session-log-routes.ts | Unref prune interval |
| src/backend/database/routes/proxmox.ts | Jump-host parse/serialize helpers |
| src/backend/database/routes/proxmox-jump-hosts.ts | Jump-host parsing utilities |
| src/backend/database/routes/opkssh-html.ts | Export escapeHtml |
| src/backend/database/routes/keybinding-validation.ts | Validate new action types |
| src/backend/database/routes/host-step-ca-routes.ts | Step CA callback route |
| src/backend/database/routes/host-normalizers.ts | Normalize enable fields + rdpAuthType |
| src/backend/database/routes/host-network-routes.ts | Add view permission checks |
| src/backend/database/routes/host-command-history-routes.ts | Add view/data checks |
| src/backend/database/routes/host-autostart-routes.ts | Add view/edit checks |
| src/backend/database/routes/homepage-rss-routes.ts | Limit response size + simplify catch |
| src/backend/database/routes/homepage-favicon-routes.ts | Limit favicon bytes + simplify catch |
| src/backend/database/routes/desktop-auto-session.ts | Native token export predicate |
| src/backend/database/routes/delete-user-data.ts | Ownership transfer + more cleanup |
| src/backend/database/routes/credential-key-routes.ts | Add credential view/data checks |
| src/backend/database/routes/credential-deploy-routes.ts | Add credential/host perms checks |
| src/backend/database/routes/credential-bulk-routes.ts | Add edit/data checks |
| src/backend/database/repositories/snippet-repository.ts | List folder subtree snippets |
| src/backend/database/repositories/shared-host-auth-override-repository.ts | Bulk list recipient overrides |
| src/backend/database/repositories/session-share-repository.ts | Add room share type |
| src/backend/database/repositories/role-repository.ts | Role perms updates + list members |
| src/backend/database/repositories/rbac-access-repository.ts | Reassign grantedBy on transfer |
| src/backend/database/repositories/host-repository.ts | Transfer hosts between users |
| src/backend/database/repositories/factory.ts | Add new repositories factories |
| src/backend/database/repositories/credential-repository.ts | Transfer credentials between users |
| src/backend/automations/http.ts | Use allowlisted safeOutboundFetch |
| src/backend/ai/tools/catalog.ts | Add stepca to forbidden domains |
| src/backend/ai/providers/http.ts | Use fetchWithProxy for private egress |
| src/backend/ai/egress.ts | Treat allowlisted hostnames as private |
| SECURITY.md | Document external secrets mode |
| scripts/patch-guacamole-lite.test.ts | Reject malformed token test |
| scripts/install-macos-sharp.cjs | Force-install sharp macOS deps |
| scripts/generate-dialect-schema.cjs | Add foreignKey to imports |
| scripts/electron-security-boundary.test.ts | Renderer IPC allowlist guard test |
| README.md | Document guacd drive + redis guidance |
| index.html | CSP-safe base path meta |
| electron/remote-sync.cjs | Atomic file writes + startup timer cleanup |
| electron/preload.js | Restrict IPC invoke channels |
| drizzle/sqlite/meta/_journal.json | Add migrations journal entries |
| drizzle/sqlite/0015_glossy_shotgun.sql | Add folder_access table |
| drizzle/sqlite/0013_tranquil_master_mold.sql | Add secret_sources table |
| drizzle/sqlite/0012_yellow_firedrake.sql | Add collab guest token |
| drizzle/sqlite/0011_wakeful_titanium_man.sql | Add collab rooms/members |
| drizzle/postgres/meta/_journal.json | Add migrations journal entries |
| drizzle/postgres/0019_oval_puff_adder.sql | Folder_access + varchar folder fields |
| drizzle/postgres/0017_curved_pet_avengers.sql | Add secret_sources table |
| drizzle/postgres/0016_slippery_anita_blake.sql | Add collab guest token |
| drizzle/mysql/meta/_journal.json | Add migrations journal entries |
| drizzle/mysql/0019_certain_archangel.sql | Folder_access + varchar folder fields |
| drizzle/mysql/0017_spicy_proteus.sql | Add secret_sources table |
| drizzle/mysql/0016_unusual_tyrannus.sql | Add collab guest token |
| docs/readme/README-CN.md | Redis guidance (CN) |
| docker/nginx.conf | Add security headers + extend timeouts |
| docker/nginx-https.conf | Add security headers |
| docker/entrypoint.sh | Base path validation + meta injection |
| docker/docker-compose.yml | Add redis/drive/external secrets docs |
| docker/compose-dev.yml | Add redis/drive env docs |
| charts/termix/values.yaml | External secrets defaults + keys |
Review details
- Files reviewed: 10/10 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
ZacharyZcR
left a comment
There was a problem hiding this comment.
Reviewed at d44a7b3. The scroll coalescer preserves Guacamole press/release pairs, caps queued wheel clicks, clears pending timers on teardown, and leaves the remote keysym unchanged while adjusting only the device-facing label. Local verification passed: TypeScript project check and all 15 focused Guacamole tests.
|
The code review and focused local verification passed, but this head has no GitHub Actions PR Check suite at all. Please rebase/update the branch onto the current |
Summary
Test plan