Companion Daemon
Let your users run their own AI subscriptions for your app, 24/7, from hardware they control - point at an existing shared companion like OpenCompanion, or publish your own branded daemon.
The companion feature lets your users put their own subscription coding CLIs (Claude Code, Codex, opencode, Hermes Agent) 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 the user installs; the user keeps and pays for their own AI subscription, and their keys and source never leave their machine.
It is the user-owned counterpart to the external agent (MCP): the MCP server lets a third-party agent reach into your app, while the companion daemon runs on the user's own hardware and reaches out to your app over authenticated HTTP.
The companion is off by default. It only works when config.companion.enabled is true. With it off, the /companion/* routes are absent entirely and the Companions tab is hidden. The companion also requires the AI product to be on (config.ai.enabled) - it drives the same capability layer the assistant uses.
One feature, two sources
The daemon your users install can come from two places - you choose at init (companionSource) and can switch later.
| Shared (default) | Vendored | |
|---|---|---|
| The daemon | An existing public companion repo - OpenCompanion by default, or any companion built with the same pipeline | Its source lives in your repo (apps/companion), branded with your name |
| Maintenance | None - the shared repo's owner cuts releases; installed daemons auto-update from it | Yours - you publish releases with one command and own the repo, cadence, and issues |
| Branding | The shared companion's identity (your app renders its install command) | Your brand end to end - binary name, service label, env vars |
| Repo weight | Light - no daemon source, no export pipeline, no coding-CLI engine in your installs | The daemon + export pipeline vendor in; pnpm install pulls the coding-CLI engine (~212MB) |
| Best for | Most projects - the shared daemon covers standard dispatch needs | Products that customize device-side behavior or need their own brand in front of users |
Either way, everything else is identical: pairing, the Companions page, dispatch, policy, and audit all live in your app and are documented on install & pairing.
- Point at a shared companion - zero daemon maintenance.
- Vendor & publish your own - full ownership and branding.
Choosing at init
With the companion enabled, init asks for the source - shared is preselected with OpenCompanion prefilled, vendored is one keystroke away. Non-interactive flags: --companion-source <shared|vendored> and --companion-repo-url <url> (shared), or a companion name prompt (vendored). See init.
Switching later
Run generatesaas update and ask your AI assistant to switch the companion source. Both directions are supported:
- shared -> vendored vendors
apps/companion+ the export pipeline, branded from your chosen name. The Companions page shows a publish-first state until your firstpnpm companion:publish. - vendored -> shared removes
apps/companionand the export pipeline. Local customisations to the vendored daemon are abandoned (the update confirms this first), and your users keep running your old published daemon until they install the shared one.
What your app controls
Everything you configure stays in your app; the daemon carries only its brand identity.
config.companion.enabled- the master switch. Off removes the/companion/*routes and hides the Companions page.config.companion.clientId- the Better Auth device-authorizationclient_idthe daemon pairs with (default"companion", distinct from the desktop app's"desktop"client).config.companion.pollIntervalMs- the idle poll cadence handed to the daemon (default 10s, clamped to [1s, 60s] so it stays under the 90s presence TTL). The backend speeds it up on its own while a run is live or the user has your app open, and the transport holds each companion to a per-route request budget. See poll cadence and request budgets.config.companion.daemon- the daemon identity (name, binary, env prefix, repo + install URLs) the Companions page reads to render the install command. Shared: resolved from the companion repo'sbrand.jsonat shape time. Vendored: mirrored from yourapps/companion/brand.jsonand kept in lockstep by a guard test.- The dispatch surface. Your app queues a run; the daemon polls
GET /poll, runs it locally, and pushes result frames toPOST /events, which stream live to the dashboard viewer over SSE. When the agent calls one of your capabilities, the daemon calls backPOST /tool-calland your app resolves it. - Capability composition. The daemon runs whatever your capability layer declares - the same tools your in-app assistant and scheduled runs use. The tools you declare are exactly what the agent can call: you compose the agent's abilities server-side, and the daemon injects them into each run. Tools you declare on the agent yourself reach a device too - see Your Agent on a Companion for the one constraint they must satisfy.
One install, several accounts
A user who signs in to your app under more than one account - a personal login and a work login, or one seat per client - pairs each of them from a single daemon install on the same computer. Every pairing keeps its own bearer, connected CLIs, policy ceiling, and work folder, so a run for one account can never read another's files. 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.
Set up from the desktop app
If your product also ships the desktop app, it runs the companion for each user as a local daemon on their own machine - consent-first, with no terminal, no pairing, and no code to copy. It attaches to any daemon already serving the machine - including the user's own always-on service - and starts its own only when none is running, so one daemon serves both the app and any paired backend. It supervises that daemon while the app is open and, on quit, stops only the one it started - never the user's service. See Companion setup.
A wired CLI session for any user
Any user can open their own Claude Code or Codex against your product, from their own terminal, with your capabilities on the CLI's MCP surface and your instructions in its context: <binary> terminal --url https://your-app.com/api. Your backend composes the session; the daemon runs it on the user's machine, in your product's work folder or in one of their own they granted. See Terminal Sessions.
Dispatching device-side work from your code
Your product code can dispatch runs to a user's companion with dispatchCompanionRun from @repo/api/companion - gated by a per-device user grant, with status reads and audit attribution. See Dispatch from Your Code.
Install & Pairing
What your users do - the one-line install, device approval, connecting their coding CLIs, and the trust surface.
Terminal Sessions
Your users open their own coding CLI wired to your product - your tools, their MCP servers, their folders.
Always-on companions
Run the companion on a VPS so schedules never wait, and what happens while a device is offline.
Your Agent on a Companion
A companion as a third model source - what it can run, the one call it cannot, and the constraint your own agent tools must satisfy.
Dispatch from Your Code
Run device-side work from product code - the per-device grant, statuses, and the results-via-capabilities pattern.
Point at a Shared Companion
Zero-maintenance mode - your app renders the shared companion's install command; its brand.json supplies the identity.
Vendor & Publish Your Own
Own a branded daemon - vendored source, one-command publishing, releases and signing.
External Agent (MCP)
The other side - expose your app's capabilities to an external always-on agent over MCP.
External Agent (MCP)
Expose your app's AI capabilities to an external agent (the user's own Claude Code / Codex / OpenCode) over an authenticated MCP server - it signs in with OAuth and gets the same full capability set the in-app assistant has.
Install & Pairing
What your users do - the one-line install from your configured companion repo, device approval, connecting their coding CLIs, and the daemon's local trust guarantees.