A production-ready, enterprise-grade web interface for Apache Guacamole remote desktop connections with Red Hat branding. This solution addresses common Guacamole implementation challenges including copy/paste functionality, stuck keys, mouse cursor synchronization, and dynamic resolution handling.
- JWT-Based Authentication - Secure token-based connection establishment
- Apache Guacamole Integration - Built on guacamole-common-js 1.6.0 SDK
- Multi-Protocol Support - RDP, VNC, SSH via Guacamole backend
- Red Hat Enterprise Branding - Professional, production-ready UI
- Bidirectional clipboard synchronization (local ↔ remote)
- Automatic clipboard monitoring with permission handling
- Clipboard API with execCommand fallback
- Handles both text and formatted content
- Automatic key state management
- All keys released on window blur/visibility change
- Prevents stuck modifiers (Ctrl, Alt, Shift, Win)
- Handles Alt+Tab and focus changes gracefully
- Remote cursor displayed with correct hotspot
- Local cursor hidden to prevent confusion
- Accurate click positioning
- Smooth cursor transitions
- Fully Automatic - No manual controls needed
- Real-time resolution updates on window resize
- DPI-aware size calculation
- No dead space in framebuffer
- Seamless resolution changes without reconnect
- 250ms debounced updates for smooth experience
- Efficient WebSocket message handling
- Hardware-accelerated Canvas rendering
- Layer caching for static content
- Minimal bandwidth usage
- Docker and Docker Compose
- Node.js 18+ (for development)
- guacamole-common-js library files
git clone <repository-url>
cd guacamole-web-interface
npm installmkdir -p public/guacamole-common-js
cd public/guacamole-common-js
wget https://downloads.apache.org/guacamole/1.6.0/binary/guacamole-common-js-1.6.0.tar.gz
tar -xzf guacamole-common-js-1.6.0.tar.gz --strip-components=1
rm guacamole-common-js-1.6.0.tar.gzcp .env.example .env
# Edit .env with your Guacamole server URLnpm run devAccess at: http://localhost:5173/?token=YOUR_JWT_TOKEN
docker-compose up -dThis starts:
- guacd (Guacamole daemon) on port 4822
- guacamole (server) on port 8080
- web-interface (this app) on port 80
Access at: http://localhost/?token=YOUR_JWT_TOKEN
docker build -t guacamole-web-interface .
docker run -d -p 80:80 guacamole-web-interfaceThe application requires a JWT token with connection parameters:
{
"sub": "user-id",
"exp": 1234567890,
"connection": {
"protocol": "rdp",
"hostname": "192.168.1.100",
"port": 3389,
"username": "administrator",
"password": "secure-password",
"security": "nla",
"ignore-cert": true,
"resize-method": "display-update"
}
}Pass token via URL:
http://your-domain.com/?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
src/
├── components/
│ ├── GuacamoleClient.tsx # Main client UI component
│ ├── ConnectionStatus.tsx # Connection status indicator
│ └── ErrorBoundary.tsx # Error handling wrapper
├── lib/
│ ├── guacamole/
│ │ ├── GuacamoleConnection.ts # Connection manager
│ │ ├── ClipboardManager.ts # Clipboard handling
│ │ ├── KeyboardStateManager.ts # Keyboard fixes
│ │ ├── MouseCursorManager.ts # Cursor synchronization
│ │ └── ResolutionManager.ts # Dynamic resolution
│ ├── auth/
│ │ └── JWTAuthManager.ts # JWT token management
│ └── utils/
│ └── logger.ts # Logging utility
└── types/
└── guacamole.d.ts # TypeScript definitions
- GuacamoleConnection: Connection lifecycle, client initialization
- ClipboardManager: Bidirectional clipboard sync
- KeyboardStateManager: Prevents stuck keys, tracks key state
- MouseCursorManager: Displays remote cursor, hides local cursor
- ResolutionManager: Automatic resolution updates on window resize
- JWTAuthManager: Token parsing, validation, WebSocket URL building
Development (.env):
VITE_GUACAMOLE_WS_URL=ws://localhost:8080/guacamole/websocket-tunnel
VITE_DEBUG=trueProduction:
VITE_GUACAMOLE_WS_URL=wss://guacamole.yourdomain.com/guacamole/websocket-tunnel
VITE_DEBUG=falseEnable JWT authentication on Guacamole server:
guacamole.properties:
auth-provider: com.guacamole.auth.jwt.JWTAuthenticationProvider
jwt-secret-key: YOUR_SECRET_KEY_HERE
jwt-auth-header: Authorization- Chrome/Edge 90+
- Firefox 88+
- Safari 14+
Required APIs:
- WebSocket
- Canvas
- ResizeObserver
- Clipboard API (with fallback)
- Page Visibility API
- Latency: 30-150ms (typical)
- Bandwidth: 100KB/s - 5MB/s (varies with usage)
- CPU Usage: ~5-15% (browser, one core)
- Memory: ~100-300MB (browser)
# Check Guacamole server is running
curl http://localhost:8080/guacamole/
# Verify JWT token is valid
# Check token expiration in payload
# Test WebSocket endpoint
wscat -c ws://localhost:8080/guacamole/websocket-tunnel- Grant clipboard permissions in browser
- Check browser console for permission errors
- Verify ClipboardManager is initialized (debug logs)
- Verify
resize-method: display-updatein JWT token - Check remote desktop supports dynamic resize (RDP RemoteFX)
- Enable debug mode to see resize events
This is automatically handled. If issues persist:
- Check KeyboardStateManager logs
- Verify window blur events firing
- Test with different browsers
- ARCHITECTURE.md - Complete system architecture
- DEPLOYMENT.md - Deployment guide and operations
- IMPLEMENTATION_NOTES.md - Technical deep dive
npm run dev # Development server
npm run build # Production build
npm run preview # Preview production build
npm run lint # Lint code
npm run typecheck # TypeScript type checkingVITE_DEBUG=true npm run devDebug logs appear in browser console with prefixes:
[GUAC-DEBUG]- Detailed debugging info[GUAC-INFO]- General information[GUAC-WARN]- Warnings[GUAC-ERROR]- Errors
- JWT tokens stored in memory only (not localStorage)
- Token removed from URL after extraction
- HTTPS/WSS enforced in production
- Content Security Policy headers configured
- XSS prevention via React escaping
- No sensitive data in console logs (production mode)
- One responsibility per file/class
- TypeScript for type safety
- Comprehensive error handling
- Detailed logging for debugging
- Clean separation of concerns
- Copy/paste works bidirectionally
- No stuck keys after Alt+Tab
- Single cursor visible (remote)
- Resolution updates on window resize
- No dead space in display
- Clean disconnect/reconnect
- Error handling works correctly
This project integrates with Apache Guacamole (Apache 2.0 License).
- Built on Apache Guacamole
- Designed for Red Hat Enterprise environments
- Uses guacamole-common-js SDK 1.6.0
For issues and questions:
- Check TROUBLESHOOTING section
- Review browser console logs (debug mode)
- Verify Guacamole server configuration
- Check Apache Guacamole documentation
1.0.0 - Production Release