Open enrollment (OPEN_ENROLLMENT=true in .env):
- Agents can register with just --server <url>, no token needed
- Machines assigned to OPEN_ENROLLMENT_USER_EMAIL, first admin, or first user
- Falls back gracefully if env var not set
- agent.py register() now takes optional token; --server alone triggers registration
Agent CLI changes:
- --server without --enroll triggers open enrollment registration on first run
- --enroll still works for token-based or re-enrollment
- Error message updated to reflect new syntax
NSIS installer changes:
- Interactive mode: custom page prompts for server URL + optional token
- Silent mode: /SERVER= alone works with open enrollment, /ENROLL= still supported
- Cleans up config on uninstall
agent.spec: add pyperclip, base64, struct, uuid to hidden imports
docker-compose + .env: OPEN_ENROLLMENT and OPEN_ENROLLMENT_USER_EMAIL vars
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- exec_script: relay now enforces admin role before forwarding to agent
- relay CORS: restrict allow_origins via ALLOWED_ORIGINS env var (docker-compose passes app URL)
- session-code: replace Math.random() with crypto.randomInt, add per-key rate limit (10 req/min)
- sessions GET: fix IDOR — users can only read their own sessions (admins see all)
- signal API: validate session ownership on create; enforce ownerUserId on all subsequent actions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Wrap root layout with ThemeProvider (next-themes, defaultTheme=system)
- Remove hardcoded dark class from <html>
- Add ThemeToggle component with Sun/Moon/Monitor icons and checkmark
on active selection
- Mount toggle in dashboard header next to user menu
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Auto-detect DISPLAY on Linux by scanning /tmp/.X11-unix/ sockets,
falling back to 'w' output, then :0 — runs before mss/pynput import
- ScreenCapture no longer raises on init failure; agent stays connected
and notifies the viewer with an error message if capture unavailable
- stream_frames skips None frames instead of crashing the WebSocket
- Relay: check for websocket.disconnect message type to avoid
'Cannot call receive once a disconnect message has been received'
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
http://10.10.20.70:3000 was becoming ws://10.10.20.70:3000:8765 instead
of ws://10.10.20.70:8765. Extract hostname only via urlparse.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- WebSocket relay service (FastAPI) bridges agents and viewers
- Python agent with screen capture (mss), input control (pynput),
script execution, and auto-reconnect
- Windows service wrapper, PyInstaller spec, NSIS installer for
silent mass deployment (RemoteLink-Setup.exe /S /SERVER= /ENROLL=)
- Enrollment token system: admin generates tokens, agents self-register
- Real WebSocket viewer replaces simulated canvas
- Linux agent binary served from /downloads/remotelink-agent-linux
- DB migration 0002: viewer_token on sessions, enrollment_tokens table
- Sign-up pages cleaned up (invite-only redirect)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>