Skip to content

Installing the Runner

Archi edited this page Oct 20, 2026 · 2 revisions

Installing the Runner

nautelle-runner is the small daemon that runs on every machine that holds your repositories and agents. It dials out to the Nautelle API over a WebSocket, reports the machine's runtime profiles, streams events up and executes typed commands that come down. The control plane never touches a project's files — the runner does. This page covers installing, pairing, running it as a service, and the local security policy that only you, on the machine, can change.

sequenceDiagram
  participant You
  participant Web as Nautelle web app
  participant Runner as nautelle-runner
  participant API as Nautelle API
  You->>Web: Admin › Runners › Add runner
  Web-->>You: one-time code XXXX-XXXX
  You->>Runner: pair --server <api-origin> --code XXXX-XXXX --allow-root ~/dev
  Runner->>API: POST /runners/pair
  API-->>Runner: runner id + token (stored in runner.json, 0600)
  Runner->>API: outbound WebSocket /runner (hello, capabilities, profiles)
  API-->>Runner: typed commands only
Loading

Requirements

  • macOS or Linux on darwin-arm64, darwin-x64, linux-x64 or linux-arm64.
  • git, tmux and gh on the machine, plus the agent CLIs you use (Claude Code, Codex).
  • Network access from the machine to the API. No inbound port is needed.

1. Install

Homebrew (macOS and Linux):

brew install andreybegma/tap/nautelle-runner

npm (or bun add -g nautelle-runner):

npm i -g nautelle-runner

The npm package is a thin wrapper that pulls the one prebuilt binary for your platform through optionalDependencies on nautelle-runner-{darwin,linux}-{arm64,x64}.

From source (needs Bun):

git clone https://github.com/AndreyBegma/Nautelle && cd Nautelle
bun install && bun run build
apps/runner/dist/nautelle-runner version

Signed releases

Every release publishes SHA256SUMS and an Ed25519 signature, SHA256SUMS.sig. The Homebrew formula verifies the signature before it installs, and the npm shim verifies it before it runs the binary — there is no postinstall step to skip. To check a release by hand against apps/runner/release-key.pub:

openssl pkeyutl -verify -pubin -inkey release-key.pub -rawin -in SHA256SUMS -sigfile SHA256SUMS.sig

2. Pair

  1. In the web app, open Admin › Runners › Add runner and copy the one-time code.

  2. On the machine, run:

    nautelle-runner pair --server <api-origin> --code XXXX-XXXX --allow-root ~/dev

<api-origin> is the API, not the web app: http://localhost:8180 for a local setup, https://<host>/api behind the reference proxy.

Flag Meaning
--server <url> the API origin. Must be https://; plain http:// is accepted only for loopback (localhost, 127.0.0.0/8, ::1)
--code <XXXX-XXXX> the one-time pairing code
--allow-root <dir> lets projects under <dir> be watched. Repeatable. Without it the runner watches nothing
--no-detect skip runtime-profile detection. By default pair detects profiles when the config has none; existing profiles are never overwritten
--insecure allow plain http:// to a non-loopback host. The token, prompts and session content travel in cleartext; run and status warn on every start
--access-client-id <id> a Cloudflare Access service token id, sent on pairing and on every connect
--access-client-secret <secret> / --access-client-secret-stdin its secret, as an argument or on stdin. It may also come from NAUTELLE_ACCESS_CLIENT_SECRET

Every Nautelle server also serves a public /install page with these steps filled in for that server.

3. Run it as a service

Install method Start the service
Homebrew brew services start nautelle-runner
npm / source nautelle-runner install-service, then the command it prints

install-service writes a systemd user unit on Linux ($XDG_CONFIG_HOME/systemd/user/nautelle-runner.service) or a launchd agent on macOS (~/Library/LaunchAgents/com.nautelle.runner.plist). To run it in the foreground instead: nautelle-runner run.

Exit code 78 means the server closed the connection for good — protocol mismatch, revoked token, or the runner was replaced — and the service is not restarted.

4. Connect Claude Code

nautelle-runner setup claude [--profile <key>] [--no-telemetry] [--dry-run] [--uninstall]

setup claude installs the nautelle Claude Code plugin (hooks and the MCP server) and points Claude Code's OpenTelemetry exporter at the runner's local receiver (127.0.0.1:4318). It backs up settings.json before the first change, prints the diff, and changes nothing on a rerun. --uninstall reverses both. It never enables prompt or tool-content export. See Claude Code Integration.

Runtime profiles

A runtime profile says how to launch an agent CLI on this machine: the binary, its config directory (CLAUDE_CONFIG_DIR, CODEX_HOME) and extra arguments.

nautelle-runner profiles                 # list configured profiles
nautelle-runner profiles --detect        # propose profiles found on this machine
nautelle-runner profiles --detect --write

The local security policy

The runner keeps a security policy in its own runner.json. Only the policy command on the machine changes it — the server cannot. A server, real or fake, can therefore not run bypassPermissions, type into a terminal, install into a profile or point the runner at a directory you did not allow. A running daemon picks up a change within about 5 seconds.

nautelle-runner policy show
nautelle-runner policy allow-root ~/dev ~/work
nautelle-runner policy deny-root ~/work
nautelle-runner policy set <key> <value>
Key Values Default Controls
allowedRoots via allow-root / deny-root none directories a project may live under; symlinks out of them are refused
maxPermissionMode manual · acceptEdits · auto · bypassPermissions auto the highest permission mode any launch may ask for
terminal.write true · false false typing into an attached terminal
skills.profileInstall true · false false installing a skill into a Claude profile
commands.<name> true · false false orchestrator.message, slot.message, session.message — messaging a running session

A refused command returns policy_refused, and the message names the rule and the exact command that lifts it, e.g. refused by this runner's policy (allowedRoots): … On the runner's machine: \nautelle-runner policy allow-root …``. The runner's page in the web app shows the effective policy.

Note

The policy bounds the blast radius; it is not a sandbox. Below the cap, an agent in auto still runs what its prompt asks within Claude Code's own permission checks.

Checking and upgrading

  • nautelle-runner status — config, spool and whether the server answers.
  • nautelle-runner version — version and target, e.g. 0.1.0 darwin-arm64.
  • nautelle-runner away on|off — route permission prompts to the web app and Telegram while you are away.

Upgrade with brew upgrade nautelle-runner or npm i -g nautelle-runner, then restart the service. Deploy the API before the runners. A runner upgraded from a version without the local policy watches no project until you run policy allow-root once; policy show lists the cached projects that are now outside.

Upgrading from agentdock-runner. The product was renamed from AgentDock to Nautelle; runner 0.14.0 is the first release with the new names. Install nautelle-runner (Homebrew follows the formula rename on brew upgrade; on npm, agentdock-runner is now a deprecated shim that depends on nautelle-runner, so install nautelle-runner directly). On first start the runner moves ~/.config/agentdock and ~/.local/state/agentdock to their nautelle names and leaves a symlink, so pairing, policy and sessions carry over. Run nautelle-runner install-service once to replace the old service, and nautelle-runner setup claude to replace the agentdock@agentdock plugin with nautelle@nautelle. The old AGENTDOCK_* environment variables and X-AgentDock-* webhook headers keep working until runner 0.16.0.

Files and environment

What Where
Config (mode 0600) ~/.config/nautelle/runner.json ($XDG_CONFIG_HOME/nautelle/)
Event spool ~/.local/state/nautelle/spool/ ($XDG_STATE_HOME/nautelle/)
Skill runs ~/.local/state/nautelle/runs/<runId>/
launchd log (macOS) ~/Library/Logs/nautelle-runner.log
Homebrew service log $(brew --prefix)/var/log/nautelle-runner.log
Variable Effect
NAUTELLE_LOG debug · info (default) · warn · error; JSON lines on stderr, token redacted
HOME, XDG_CONFIG_HOME, XDG_STATE_HOME where the files above live
CODEX_HOME the Codex profile profiles --detect proposes
NAUTELLE_ACCESS_CLIENT_SECRET the Cloudflare Access secret for pair

Everything the runner sends the server — pane frames, logs, reports, PR bodies, patches — passes one redactor for token shapes, private keys, .env assignments, Authorization headers and URL credentials. It is pattern-based and best-effort.

See also

Decisions: ADR-0001 · ADR-0010 · ADR-0017 · ADR-0023 · ADR-0047 · ADR-0049 · Spec: #5, #6

Clone this wiki locally