Repository navigation
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
- macOS or Linux on
darwin-arm64,darwin-x64,linux-x64orlinux-arm64. -
git,tmuxandghon the machine, plus the agent CLIs you use (Claude Code, Codex). - Network access from the machine to the API. No inbound port is needed.
Homebrew (macOS and Linux):
brew install andreybegma/tap/nautelle-runnernpm (or bun add -g nautelle-runner):
npm i -g nautelle-runnerThe 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 versionEvery 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-
In the web app, open Admin › Runners › Add runner and copy the one-time code.
-
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.
| 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.
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.
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 --writeThe 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.
-
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.
| 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.
Decisions: ADR-0001 · ADR-0010 · ADR-0017 · ADR-0023 · ADR-0047 · ADR-0049 · Spec: #5, #6
Nautelle · Website · Discord · Engineering docs · MIT
Start
Work
Cost and health
Surfaces
Operate
Reference