Deploy agents onto remote Linux hosts¶
Editions: OSS, Cloud, Enterprise. Unless stated otherwise, everything on this page ships in OSS.
The console's SSH and GCP deployment actions run the same preloop agents
install-runtime and live validation path as the CLI. Success requires a real
registered agent, its selected model binding, runtime version, and validated
control plugin. Installer output is not streamed into the API or audit log
because upstream tools can include credentials in their diagnostics.
Enable these methods explicitly on the API process:
PRELOOP_AGENT_DEPLOYMENT_ENABLED=true
PRELOOP_URL=https://preloop.example.com
# Optional. Private networks are refused unless the operator lists them.
PRELOOP_DEPLOY_SSH_ALLOWED_CIDRS=192.168.10.0/24
# Optional GCP backend, using the API process's Application Default Credentials.
PRELOOP_DEPLOY_GCP_PROJECT=my-project
PRELOOP_DEPLOY_GCP_ZONE=us-central1-a
PRELOOP_DEPLOY_GCP_NETWORK=global/networks/default
SSH deployment requires an account owner or administrator, a password or
unencrypted private key, and an independently verified SSH host public key
such as ssh-ed25519 AAAA.... Obtain the host key from the host's console or
another trusted administrator. A network scan alone does not authenticate it.
The API never uses its own SSH agent, personal keys, or automatic trust of an
unknown host. It resolves the destination once and pins that address. Loopback,
link-local, multicast and metadata destinations are always refused.
Linux targets need Bash, curl, Python 3, flock, internet access to the runtime
publishers, and any sudo access required by their official installers. Agent
installation may take several minutes. Configure the reverse proxy read timeout
to at least 930 seconds. The shipped Docker and Helm console nginx templates
apply this budget to the exact deployment endpoint. For an NGINX ingress in
front of the console, also set ingress.annotations keys
nginx.ingress.kubernetes.io/proxy-read-timeout and
nginx.ingress.kubernetes.io/proxy-send-timeout to "930" (or longer).
The API bounds work to 700 seconds and reserves up to
200 seconds to clean up a failed GCP VM. Closing the browser may not cancel a
request already accepted by the server. Check the agent registry and GCP
resource before retrying; reuse the deployment's idempotency key after an
uncertain HTTP result. The deterministic VM name prevents duplicate resources.
GCP creates Ubuntu 24.04 VMs without an attached service account. It obtains the new SSH host key from the authenticated Compute serial-output API, blocks project-wide SSH keys, and uses a transient deployment key. The provisioner's cloud credentials remain on the Preloop API server. Failed installations, validation failures, timeouts, and task cancellations remove newly created VMs; a cleanup failure identifies the resource in the API error for manual removal. A successful VM remains until the operator deletes it, and continues to incur normal GCP charges.
Use a dedicated provisioner identity with only:
compute.instances.create,get,delete,getSerialPortOutput,setMetadata,setLabelscompute.disks.createcompute.subnetworks.use,useExternalIp,compute.networks.usecompute.zoneOperations.get
Restrict its instance permissions to the selected zone and names beginning
preloop-agent- using an IAM condition, and permit SSH from the Preloop server
in the selected network's firewall. The Ubuntu public image supplies image-read
access. The service identity needs no IAM administration, storage access,
service-account impersonation, or key-management permission.
For an unreleased CLI candidate, the operator may set PRELOOP_DEPLOY_CLI_URL
to an HTTPS Linux binary URL and PRELOOP_DEPLOY_CLI_SHA256 to its SHA256. Both
are required together and the host verifies the downloaded binary before use.
Otherwise deployment uses the normal published CLI installer. Candidate binaries
must match the host architecture; production should use the published installer.
The deployment API is synchronous: POST /api/v1/agent-deployments accepts
idempotency_key, runtime (hermes or openclaw), model_id, target
(ssh or gcp), optional compute_size, optional desktop (default
false), and SSH connection fields for SSH.
GET /api/v1/agent-deployments/capabilities reports configured methods.
SSH secrets are used only in request memory. Enrollment uses a hashed temporary
API key with the initiating owner's permissions, a twenty-minute expiry, and
revocation after success, failure, or cancellation. Official runtime and uv
installers run before this key is exported; the CLI's --install-only phase
requires no login and skips enrollment. Only the trusted target's subsequent
--skip-install onboarding phase receives it. The target is trusted with these
bootstrap permissions; this is not a narrower enrollment-only scope.
OpenClaw control uses a healthy existing Python environment or installs uv,
aiohttp and PyYAML in ~/.preloop-agent-control/venv, without system pip.
Installation refuses successful onboarding if the required control plugin or
channel is not ready.
Account-scoped start/failure/completion audit events record identifiers and the observed runtime version.
For disposable environments, set PRELOOP_DEPLOY_GCP_MAX_RUN_SECONDS=14400.
GCE will delete successful as well as failed test VMs after four hours. The
optional value must be between 60 seconds and seven days; unset means that a
successful VM has no automatic lifetime limit.
Desktop (optional)¶
Set desktop: true to install a headless desktop on the target after live
validation. The deployment runs preloop agents install-runtime <runtime>
--install-only --skip-install --desktop, so the upstream runtime installer
is not executed again after live validation. On Debian and Ubuntu that
installs Xvfb on display :99, a Chromium browser, and x11vnc. The VNC
server listens on
127.0.0.1:5900 only (-localhost, no -listen 0.0.0.0). A random password
is stored with x11vnc -storepasswd in ~/.preloop/desktop/vncpasswd (mode
0600) and is not written anywhere else. The password is a command argument
for that short-lived x11vnc process, so a desktop install assumes a
single-tenant host. VNC DES uses the first 8 characters of the password.
Package installation refreshes the package lists (apt-get update) and then
runs apt-get install as root, or sudo -n apt-get when the
SSH user is not root. The GCP Ubuntu image grants the metadata SSH user
passwordless sudo; a target without that privilege reports desktop: failed
and does not change the validated runtime. The CLI records
~/.preloop/desktop.json and exports DISPLAY=:99 for the runtime. A systemd
user unit preloop-desktop.service keeps the session up. For a non-root user
the CLI runs sudo -n loginctl enable-linger first so the unit survives the
end of the deployment SSH session. If a systemd user session is unavailable
the CLI starts ~/.preloop/desktop/start.sh with nohup and says so. The
install fails (and the deployment reports desktop: failed) when nothing
accepts connections on 127.0.0.1:5900 within 15 seconds. The desktop step's
output is kept on the host in ~/.local/state/preloop/desktop.log.
The listener is loopback-only. Deployment does not change GCP firewall rules
and does not open port 5900 on any non-loopback interface. Other Linux
distros are refused with desktop_unsupported_distro and do not change the
runtime install. A desktop failure does not change the deployment result
after runtime validation has already passed. The result's desktop field is
installed, failed, or skipped.
Verify on the host:
The socket must be loopback only: 127.0.0.1:5900 (x11vnc 0.9.16 on Ubuntu
24.04 also binds [::1]:5900 despite -noipv6). DISPLAY=:99 chromium --headless=new
--screenshot should write a screenshot, and preloop agents status <runtime>
--json reports desktop.installed when ~/.preloop/desktop.json exists.
Live desktop view¶
Operators watch the display from the console, next to the agent's timeline,
through a brokered live view. No inbound port is opened on the host: the
host keeps one outbound connection to Preloop and relays the screen only
while a view is open. The API must run with PRELOOP_DESKTOP_VIEWS_ENABLED=true
(off by default).
Host side. With desktop: true the deployment's desktop step also creates
a desktop credential bound to the deployed agent, stores it in
~/.preloop/desktop/token (mode 0600), and enables the systemd user unit
preloop-desktop-tunnel.service, which runs preloop desktop serve. The
endpoint registers the host (a stable id kept in ~/.preloop/desktop.json,
hostname, display) and heartbeats. When a view opens it connects to the local
x11vnc, does the VNC password handshake itself and hands Preloop a stream that
needs no password: the VNC password never leaves the host and never reaches
the browser. A failed tunnel step reports desktop: failed.
On a host that already runs an agent (Debian or Ubuntu), enable the same thing after the fact:
--agent creates the desktop credential with your login, so the desktop shows
up on that agent's page and on its sessions. Without a login, pass an existing
agent key with --agent-key or PRELOOP_DESKTOP_TOKEN. --dry-run prints
the steps. Check the tunnel with systemctl --user status
preloop-desktop-tunnel.
From the console. An active agent with no online desktop shows Live
desktop: none under its summary to users with manage_agents. When the agent
has a live Agent Control connection, Enable desktop sends desktop.install
over that channel and the runtime plugin (Hermes, OpenClaw) runs preloop
desktop enable --agent <id> on its host with the CLI login it already has; the
Desktop tab appears once the tunnel registers. An unsupported OS or distro, or
missing root or passwordless sudo, is shown with a link here and leaves the
agent untouched. Without a control channel, or with a plugin that does not
know the command, the console shows the one-line command to run on the host
instead and sends nothing. Requests are audited as
desktop.install.requested, desktop.install.succeeded and
desktop.install.failed.
Sessions on the same host. The installer also exports
PRELOOP_DESKTOP_ENDPOINT_ID (the endpoint id from ~/.preloop/desktop.json)
next to DISPLAY=:99 in the runtime environment. CLI hooks, the Hermes
plugin and the Browser Use reporter send it when they create or report on a
runtime session (desktop_endpoint_id on POST /auth/runtime-sessions/token,
or PUT /runtime-sessions/{id}/desktop-endpoint), so that session gets the
Live desktop button even when it runs with a different credential than the
tunnel. The value only matches desktops in the session's own account.
Console. An agent with an online desktop shows a Desktop tab, and its
live sessions show a Live desktop button. A view is view only: keyboard,
mouse and clipboard input are dropped by the API, not just by the browser.
"Request control" (if you have the permission) forwards input for at most 10
minutes. You give a reason; when the account has another admin who can decide
approvals, the request becomes a desktop_control approval that someone other
than you must approve (the viewer shows "Waiting for another admin to approve
control" and stays view only until then). With no one else to ask, your reason
is the confirmation. A refused or expired request writes
desktop.control.denied; an approved one writes desktop.control.granted
with the approver. One viewer per desktop at a time; a
second operator is told who holds it. Views last 15 minutes by default
(PRELOOP_DESKTOP_VIEW_MAX_SECONDS caps them, default 60 minutes) and close
after 120 seconds without viewer traffic. Opening, ending, and taking or
releasing control are audit events. Nothing is recorded: no frames, no
screenshots.
Native VNC client. From an operator machine:
The CLI listens on 127.0.0.1 only, uses the same view and the same checks as
the console, and ends the view when the VNC client disconnects or on Ctrl-C.
Permissions: opening a view (console or preloop agents desktop) needs
control_managed_agent (the one that guards agent takeover) in the agent's
account, checked when the view is created and again when the viewer connects.
preloop agents desktop also lists agents to resolve a name, which needs
view_agents. preloop desktop enable --agent creates an agent credential,
which needs manage_agents; re-running it reuses the stored credential for
the same agent (--rotate-credential mints a new one). API replicas join the two halves
of a view over NATS, so no sticky routing is required.
Managed employee endpoints¶
For existing employee workstations, use the separate staged MDM recipes. They cover managed-file delivery and scoped inventory rather than remote runtime installation.