Deployment
Docker (Recommended for a Signaling Server)
printf 'LRD_BOOTSTRAP_TOKEN=%s\n' "$(openssl rand -hex 32)" > .env
docker compose up -dAccess http://localhost:8081, enter the token from .env, and set up the admin account. Keep .env after initialization: Compose validates the required variable on every startup. To build a custom image, use ./build_docker.sh.
Persisted State
The server writes to the platform-standard paths described in the config.toml Reference, not to its working directory. It runs as root in the container, which selects the Linux system scope, so the Compose file bind-mounts the three directories that must outlive the container:
| Host path | Container path | Contents |
|---|---|---|
./conf | /etc/lcxl-remote-desk | config.toml |
./data | /var/lib/lcxl-remote-desk | Signal and execution-ledger databases, remote-access state |
./logs | /var/log/lcxl-remote-desk | Rolling logs |
Losing ./data discards the admin account's signaling state, access codes, AI provider configuration, and audit history — back it up alongside ./conf.
TURN Relay Ports
The relay port range ([turn] relay_min_port / relay_max_port, 50000-50050 by default) is not published by default. When this server should relay media itself, uncomment the range in docker-compose.yml, open it in the security group, and set [[turn.interfaces]] external to the host's public address — see Networking & NAT Traversal below.
Production Build from Source
# Backend
cargo build --release
# Frontend
cd vite-project
npm run buildSystem Dependencies
Linux
sudo apt install -y build-essential pkg-config libssl-dev libasound2-dev \
libpipewire-0.3-dev libx11-dev libxcb1-dev libxcb-randr0-dev libxext-dev \
clang libclang-dev cmake libvpx-devmacOS
Install via Homebrew (x264 and libvpx are resolved through pkg-config; cmake builds the bundled Opus from source):
brew install pkgconf libvpx x264 cmakeOn Apple Silicon, make sure pkg-config can locate the Homebrew .pc files:
export PKG_CONFIG_PATH="/opt/homebrew/lib/pkgconfig:$PKG_CONFIG_PATH"For unattended access, open System Settings → macOS Status → Automatic Login in the web console. The card reports FileVault, the configured automatic-login user and the current user, and provides copyable enable/disable commands. The enable command uses sysadminctl ... -password -: run it in Terminal so macOS requests the password interactively; the web page never reads or handles the password. FileVault and automatic login are mutually exclusive, so the page disables the enable action while FileVault is active.
Windows
No extra dependencies; everything is managed automatically through Cargo. (AV1 encoding optionally needs nasm — see Quick Start.)
Networking & NAT Traversal
The server bundles signaling, STUN, and TURN. For connections across NATs:
- Connections prefer direct WebRTC P2P and fall back to TURN relay only when traversal fails.
- For external access, ensure the signaling endpoint is reachable and that the configured relay port range is open/mapped.
- TURN realm, credentials, interfaces, and the relay port range are configured under
[turn]— see the config.toml Reference.
Scaling the Process Layout
For multi-session hosts or capturing secure surfaces, run the service-daemon mode, or split the signaling service out with --startup-mode signaling.
Public Deployment Hardening
When exposing the server to the public internet (typically behind a TLS-terminating reverse proxy), note:
LRD_COOKIE_SECURE— controls the session cookieSecureattribute. It defaults tofalseso a local / LAN HTTP setup keeps its session. SetLRD_COOKIE_SECURE=truefor an HTTPS deployment so the cookie is only ever sent over HTTPS.LRD_BOOTSTRAP_TOKEN— optional outside the provided Compose example; when set, the initialization wizard and its pre-initialization connection probes require this value. A present but blank value is a startup error. Use at least 32 random bytes and never put it in a URL or log. The Compose example makes it mandatory with${...:?}.LRD_TRUSTED_PROXIES— comma-separated proxy IP/CIDR list. Loopback (127.0.0.0/8,::1) is trusted by default; other proxy/container networks must be explicit. Only trusted peers may supplyX-Forwarded-For. Do not use*unless the server cannot be reached except through a proxy that overwrites XFF.LRD_AUTH_IPV6_PREFIX_LEN— IPv6 rate-limit prefix, default64(1..=128). IPv4 always uses/32.LRD_AUTH_RATE_LIMIT_MAX_BUCKETS— bounded login and redeem capacity tier, default65536. Ordinary deployments should leave it unchanged.LRD_PROVIDER_SSRF_MODE— guards the central brain's outbound dial to a user-configured model providerbase_urlagainst SSRF (an internal service or a cloud metadata endpoint). Governs private reachability only (orthogonal to the TLS switch below); the cloud-metadata ranges are blocked in every mode. Values:relaxed(default) — allows private / loopback targets (local model gateways likehttp://localhost:11434).strict— rejects private / loopback / CGNAT / ULA targets; re-validates the resolved IP at connect time (anti DNS-rebinding). Use this if untrusted users can configure the provider.
LRD_ENFORCE_PUBLIC_TLS— whether a public target may be dialed over plaintext (http). Defaults totrue(only an explicitfalse/0/no/offturns it off). When on, a plaintext dial to a public address is refused before connecting, so the api_key never leaves in the clear; private / loopback / LAN targets are always exempt and the cloud-metadata floor is always blocked regardless. Orthogonal to the SSRF mode: to allow a public plaintext provider, turn this off — you do not needrelaxed(which would additionally open private targets).- There are no runtime API docs endpoints: the server serves no Swagger UI / ReDoc / RapiDoc / Scalar and no
/openapi.json, so none of that is probeable on a public host. Generate the spec offline withdump-openapiwhen you need it (see the REST API reference). - Put the server behind a reverse proxy that terminates TLS, passes through
Host, and forwards the WebSocketUpgradeheaders for signaling. - A native reverse proxy on the same host normally connects from loopback and works with the default trust. A proxy container normally connects from a bridge/container address, so add that actual peer CIDR explicitly. If Docker or a layer-4 proxy has already discarded the original source address and supplies no XFF, the application cannot reconstruct it and clients share one rate-limit bucket.
- The server currently has no CORS middleware. Browsers therefore cannot use a cross-origin preflight to send a custom XFF header to a loopback peer. Adding CORS, Private Network Access allowances, or XFF header allowances requires re-reviewing the default loopback trust boundary.