Skip to content

Runners

A Runner is a small native app you install on a real machine (Windows, macOS, or Linux) so agents can act on that machine: capture a genuine full-desktop screenshot, drive a browser you are already logged into, run a command, or read and write files.

It exists for the cases a headless browser cannot cover. When an auditor wants a screenshot with the real desktop, the OS clock, and the signed-in identity - not an AI-generated or headless render - a runner captures the true screen of a real session. The same channel lets an agent operate an authenticated browser or run a check directly on a workstation or server.

Runners are pull-based: the app connects outbound to XTM One and pulls work. The platform never opens a port on your machine, and there is nothing inbound to firewall.

Install a runner

You download and enroll runners from Profile > Runners. On a personal machine the graphical installer needs no command line; for servers and fleets there is a fully headless command-line path (see the next section).

  1. Open Profile > Runners and download the installer for your operating system. The main button is the x86_64 (Intel/AMD) build; ARM machines take the arm64 link next to it. Macs need no choice at all - one universal bundle covers both Intel and Apple Silicon.
  2. Run the installer (next, next, finish on Windows; drag to Applications on macOS; an AppImage or a .deb / .rpm on Linux).
  3. Launch XTM One Runner. On the Connect screen, paste this instance's URL and an enrollment key, then connect.

If the OS blocks the first launch

A build that is not code-signed for your operating system trips the first-open gate: Windows SmartScreen shows "unrecognized app" (click More info > Run anyway), and macOS reports it "could not verify ... is free of malware" - click Done (not Move to Trash), then open System Settings > Privacy & Security, click Open Anyway, and launch again. This only ever concerns the first manual install: self-updates are signature-verified and never re-prompt, and the headless install path is not affected.

Create an enrollment key from the same Profile > Runners screen. A key is single-use for the first machine but can be configured for reuse across a fleet, and it can carry defaults (labels, capabilities, permission mode) so an unattended roll-out lands already configured. After enrollment the app stores a durable credential in the machine's OS keychain and stays connected across reboots - you never paste the key again.

One record per machine

Each enrolled machine is one runner. Installing on ten machines gives you ten runners you can name, label, and target individually - not ten integrations. Re-enrolling a machine that is already in the list (a reinstall, a re-run of the install script) updates its existing record - new credential, refreshed host facts, your name/permissions/labels kept - instead of creating a duplicate. Revoked runners stay revoked: enrolling that machine again deliberately creates a fresh record.

Install headless (command line)

Servers, VMs, and MDM-managed fleets rarely have someone sitting at a desktop. For those machines, Profile > Runners also shows a one-line install command per operating system under Headless install (command line): copy it, replace the key placeholder with an enrollment key, and run it on the target machine - no graphical session is ever needed.

The command downloads the runner build matched to your instance's version, verifies its integrity, installs the binary, enrolls the machine, and registers it to start automatically (a LaunchAgent on macOS, a systemd user service on Linux, a logon task on Windows). The same binary is also a CLI afterwards: xtm-one-runner status, xtm-one-runner run, and xtm-one-runner reset manage it without any UI.

The headless build (Linux servers)

On Linux the installer picks between two builds of the same runner:

Build Ships Capabilities
Desktop (default) Everywhere else, and any Linux with a graphical session Screenshots, mouse/keyboard, shell, files, browser
Headless Linux hosts with no graphical session Shell, files, browser

This is not a matter of taste. The desktop build links the desktop libraries (GTK, X11) at load time, so on a server without them it fails before it runs - no flag or setting can work around that. The headless build links none of them.

The installer decides from the environment: no DISPLAY and no WAYLAND_DISPLAY, and no graphical session on the machine, means headless — so SSH-ing into your own Linux desktop to run the installer still gets you the desktop build. Force either one with --headless or --desktop (or XTM_RUNNER_FLAVOR=headless for cloud-init and MDM). The choice sticks: a headless runner is only ever offered headless updates.

Two things a server does differently:

  • Credential storage. Servers usually run no keyring daemon. When there is no OS keychain, the runner stores its credential in a file readable only by its own user, next to its config, and says so in the log.
  • Staying up after logout. The service runs under your user account, so enable lingering or it stops when your SSH session ends. The installer does it for you when it can: sudo loginctl enable-linger <user>.

Desktop capabilities need a desktop

Screenshots and mouse/keyboard control require a logged-in graphical session. Shell commands, file operations, and browser control work on fully headless servers. A headless runner asked for a screenshot answers with a clear error rather than a black frame - assign that work to a runner on a real desktop.

The runner app

Once enrolled, the app lives in the system tray (or menu bar) behind the XTM One icon. From there you can:

  • see live status - Connected, Connecting, or Offline
  • open an overview panel with the runner name, the enrolled instance URL, the app version, and the host facts reported to the platform (OS build, available shells, installed browsers, monitors, timezone and locale)
  • change a few settings - automatic updates, launch-at-login, and the browser CDP URL
  • unenroll the machine locally

Closing the window hides the app to the tray; the runner keeps serving. The app updates itself from signed bundles matched to your instance, so a fleet stays current without anyone touching each machine.

Uninstall and re-enroll

Uninstalling the app removes the local enrollment with it - the stored credential, the configuration, and the launch-at-login entry - so the next install always starts fresh at the Connect form. The runner's record on the platform is deliberately left alone (an uninstall can happen offline): revoke it in Profile > Runners when you retire a machine for good.

If a running runner's record is revoked on the platform, the app shows a fatal error with a Re-enroll this machine button: one click drops the dead local credential and reopens the Connect form, prefilled with your instance URL and ready for a fresh enrollment key.

Permissions the first time

  • macOS asks for Screen Recording and Accessibility permission the first time an agent captures the screen or controls input. Grant them in System Settings > Privacy & Security; the app links you straight there and shows whether each one is granted.
  • Linux capture and input need a graphical session. On Wayland, capture goes through the desktop portal; the app warns when the current session cannot be captured.

What an agent can do on a runner

When an agent has the computer_use built-in tool and at least one of your runners is online, it can:

  • take a full-desktop screenshot (the audit-evidence case)
  • perform mouse and keyboard actions
  • run a shell command
  • read, write, and list files

Flows can drive runners too: the Runner command node dispatches the same command kinds through the same transport — the runner's permission mode, allow-lists, and approval gating apply unchanged — and can wait for the result. Flows can also react to runner lifecycle through the built-in Runner online, Runner offline, and Runner command completed event types with the On event and Wait for event nodes. - control a browser you already authenticated

Screenshots and file reads come back into the conversation as inline images or downloadable files, exactly like other tool results, so they can flow straight into an evidence workflow. The PNG is also forwarded to the model as a native vision block (high-detail, PNG preserved) so the agent can read the URL bar, labels, errors, and OS clock itself - it should never ask you to describe or verify the capture.

Control what a runner allows

Each runner carries its own permission model, set in Profile > Runners:

  • Restricted (default) - least privilege. Only the capabilities you enable are allowed (screenshot, mouse/keyboard, shell, file operations, browser control). Enabling a capability is the consent: once it is on, it runs without further prompts. Shell commands are the exception and get finer control:
    • Allow-list (standing approvals) - a matching command runs immediately and never asks for approval. With approval off, only allow-listed commands can run at all.
    • Block-list (standing refusals) - a command containing a blocked word or phrase is always refused. It never runs and never reaches the approval queue, and the block-list wins over the allow-list.
    • Require approval - any shell command not covered by the lists waits for a human decision in Profile > Runners. When approving or rejecting, Approve & always allow / Reject & always block persists the decision into the corresponding list, so the same command never asks again. Approval applies to shell commands only - screenshots, input, file operations and browser control never queue.
    • File operations can additionally be narrowed with a path allow-list.
  • Full control - a deliberate opt-in that allows every capability and every command with no lists and no approval. It is clearly flagged in the UI, and every command is still audited.

Whatever the mode, every command is recorded server-side with its result, and revoking a runner invalidates its credential immediately - the next command fails and the app stops.

Choose which machines an agent can reach

Runners are targeted, not global:

  • Give runners labels (for example audit, eu-west, workstation).
  • On an agent, set a runner selector - specific runners or a set of labels - so the agent can only reach the machines you intend.
  • An assignment can refine that selection further, never widen it, so an agent-driven automation can never reach a machine its agent could not. A flow's Runner command node targets one explicit runner instead — gated by the runner's own permission mode, allow-lists, and visibility (a flow can only use runners visible to the person who runs it: their own, group-shared, or company-managed).

When more than one eligible machine is online, mutating actions require the agent to name the target runner explicitly, so a command can never land on the wrong machine by accident. The list_runners tool lets an agent see which machines are online before acting.

Availability and limits

The whole subsystem is governed by platform configuration: an administrator enables it, sets where installers are downloaded from, and can cap how many runners a user may enroll and how many commands may run on one machine at once. If downloads are not configured on your deployment, the page explains it and you can still enroll a runner you obtained separately. See the runner variables in Configuration for the full list.

Good habits

  • Name each machine so you can tell an agent exactly where to work.
  • Start in Restricted mode and enable only the capabilities you need; reserve Full control for machines you fully trust.
  • Use labels for fleets and set agent selectors so each agent reaches only its own machines.
  • Turn on shell-command approval when a person should stay in the loop, and build up the allow/block lists from real decisions with "Approve & always allow" / "Reject & always block".
  • Revoke a runner as soon as a machine is decommissioned.
  • My profile - where you download runners and manage enrollment keys.
  • Agents - bind the computer_use tool and set the runner selector.
  • Configuration - the RUNNERS_ENABLED and RUNNER_* deployment variables.