GenerateSaaS

Runner Daemon

Let your users run their own coding-CLI subscriptions for your app 24/7 from hardware they control, by installing AgentRunner - the daemon we publish and maintain.

The runner feature puts your users' own subscription coding CLIs (Claude Code, Codex) to work on your app around the clock, from a machine they control - a laptop, a home server, a VPS. Your app dispatches work to a small terminal daemon they install; their keys and source never leave their machine. It is the user-owned counterpart to the external agent (MCP), which lets a third-party agent reach into your app instead.

The runner is off by default. It needs config.runner.enabled - with it off the /runner/* routes are absent and the Runners tab is hidden - and it also requires config.ai.enabled, since it drives the same capability layer the assistant uses.

The daemon your users install

The daemon is AgentRunner, published and maintained by us and installed by your users from its own public releases. Nothing about it lives in your repo and there is nothing for you to publish or keep updated: installed daemons auto-update, and releases ship with a SHA256SUMS manifest and provenance attestations a user can verify before installing.

What your app controls

Everything you configure stays in your app; the daemon carries only its brand identity.

SurfaceWhat it is
config.runner.enabledThe master switch. Off removes the /runner/* routes and hides the Runners page.
config.runner.clientIdThe Better Auth device-authorization client_id the daemon pairs with (default "runner", distinct from the desktop app's "desktop").
config.runner.daemonThe daemon identity (name, binary, env prefix, repo and install URLs) the Runners page reads to render the install command. Written by the CLI as AgentRunner's published identity.
TransportOne SSE connection per paired device, so dispatch is immediate and an idle fleet costs no requests. Nothing to tune - see how work reaches a device.
DispatchYour app queues a run; the daemon receives it on the connection it holds at GET /stream, runs it locally, and pushes result frames to POST /events, which stream live to the dashboard. Capability calls come back to POST /tool-call for your app to resolve.
CapabilitiesThe daemon runs whatever your capability layer declares - the same tools chat and automated runs use. What you declare is exactly what the agent can call. Your own agent tools reach a device too; see Your Agent on a Runner.

One install, several accounts

A user who signs in under more than one account - a personal login and a work login, or one seat per client - pairs each from a single install on the same computer. Every pairing keeps its own bearer, connected CLIs, and work folder, so two accounts never share state. Commands take --user <id> beside --url to pick the account, and existing installs re-key themselves at the first boot after upgrading. See several accounts on one machine.

The desktop app needs none of this

If your product also ships the desktop app, that app is self-contained: it forks its own agent runtime from its own binary and runs chat, automations, and terminal sessions there, so a desktop user installs nothing and pairs nothing. The runner is for your web users - people driving your product from a browser who want the work to happen on their own machine. The two never meet: the app neither installs a runner nor looks for one. See Agent runtime.

Two more surfaces it unlocks

  • A wired CLI session for any user. agentrunner terminal --url https://your-app.com/api opens their own Claude Code or Codex with your capabilities on its MCP surface and your instructions in its context. See Terminal Sessions.
  • Dispatch from your own code. dispatchRunnerRun from @repo/api/runner sends device-side work to a user's runner, authorized by pairing alone, with status reads and audit attribution. See Dispatch from Your Code.

On this page