agentproto daemon
agentproto daemon install [--dry-run] register service + start it (macOS launchd, Windows Task Scheduler)
agentproto daemon uninstall stop + deregister service
agentproto daemon start start the service (idempotent; never kills healthy)
agentproto daemon restart kill + relaunch
agentproto daemon stop stop the service
agentproto daemon status installed? running? /health reachable?
agentproto daemon logs [--lines <N>] tail daemon.logRuns agentproto serve as a background service via the host's
service manager. Today: macOS launchd and Windows Task
Scheduler (per-user scheduled task at logon). Linux (systemctl --user) ships later; until then the verb prints a clear "not
supported" message and points you at agentproto serve &; disown.
The daemon picks up its config from ~/.agentproto/config.json via
daemon.* keys. Configure once, then daemon install captures the
snapshot into the plist. Re-run install after any config change.
Logs (stdout + stderr) go to ~/.agentproto/daemon.log.
Platform notes
- macOS: plist at
~/Library/LaunchAgents/sh.agentproto.plist.RunAtLoad=true,KeepAliveis crash-only (SuccessfulExit=false),ProcessType=Interactive(so it stays alive at user login and across login/logout). Crash-only means a clean exit-0 stays settled, while a crash respawns automatically. The plist'sProgramArgumentsis[node, cli.mjs, serve, …flags], computed fromprocess.execPath+process.argv[1]of the install invocation — so the daemon runs on the same Node binary that randaemon install(works correctly withnvm/fnm/ Homebrew). - Linux: not yet supported. Fall back to
agentproto serve &; disownor your ownsystemd --userunit pointing atagentproto serve. - Windows:
agentproto daemon installregisters a per-user scheduled task (schtasks /Create /TN agentproto-daemon /SC ONLOGON /F— no admin required) whose payload is a generated~/.agentproto/agentproto-daemon.cmdlauncher that captures the samenode cli.mjs serve …argv snapshot the macOS plist does, and merges stdout+stderr into~/.agentproto/daemon.log.installthen starts the task once;start/restart/stopmap toschtasks /Run,/End+/Run, and/End.statusqueries the task and probes/healthwith the same three-part shape as the macOS branch. The task runs at user logon, so the daemon survives closing the terminal window.
Subverbs
install
# Optional: configure first
agentproto config set daemon.workspace /Users/me/code
agentproto config set daemon.port 18791
agentproto config set daemon.allowedOrigins https://guilde.work
agentproto config set tunnel.host wss://guilde.work/api/v1/agentproto/tunnel
agentproto config set tunnel.autoconnect true
# Optional: end-to-end encrypt the tunnel (needs tunnel.token; the host
# must also support it, else the daemon falls back to plaintext).
agentproto config set tunnel.e2e true
agentproto daemon install
tunnel.e2ewraps the outbound tunnel in an AEAD box negotiated from the sharedtunnel.token, so even the host can't read the frames. It is opt-in and negotiated — an older host that doesn't advertise E2E keeps working over the plaintext tunnel. Seeserve→ End-to-end encryption.
Writes the plist, runs launchctl bootout on any prior version, then
launchctl bootstrap gui/<uid>. Service starts immediately because
of RunAtLoad.
--dry-run prints the would-be plist + the launchctl bootstrap
command without touching the filesystem or launchd. Useful for
verifying the captured flags before committing.
install also prints the exact node + CLI entry it captured, and
whether that entry is the published npm install or a workspace build
(a monorepo/dev checkout). When it is a workspace build the service would
run that local folder, so it warns loudly with the npm fix:
agentproto daemon: service will run:
node: /Users/me/.nvm/versions/node/v22.22.0/bin/node (nvm, global prefix /Users/me/.nvm/versions/node/v22.22.0)
entry: /code/agentproto/packages/cli/dist/cli.mjs
source: workspace build (LOCAL FOLDER — not the npm install)
! this is a workspace/dev checkout, not the npm-installed CLI — the service
will run that local folder. To run the published CLI instead:
npm i -g @agentproto/cli@latest (then re-run: agentproto daemon install)This is the field-observed failure mode: run agentproto setup from a
monorepo checkout and the daemon is installed from the local dist/ instead
of npm. agentproto doctor/agentproto setup flag the same thing in their
preflight step.
uninstall
agentproto daemon uninstalllaunchctl bootout + delete plist. Idempotent — "already absent" is
not an error.
start / restart / stop
agentproto daemon start # launchctl kickstart (idempotent; leaves a healthy daemon running)
agentproto daemon restart # launchctl kickstart -k (kill + relaunch)
agentproto daemon stop # launchctl kill SIGTERMstart is idempotent: it asks launchd to start the service if it isn't
running and is a no-op if a healthy daemon already is. It never kills the
incumbent. restart is the force-cycle: kickstart -k kills the running
daemon (if any) and relaunches it — the clean replacement for manually
killing the port. Crash-only KeepAlive means launchd only respawns the
daemon when it exits non-zero; daemon stop sends a single SIGTERM and
exits.
start/restart also self-heal the plist's EnvironmentVariables.PATH:
each kickstart probes a login shell for the current PATH and rewrites the
plist if it changed. This removes the need to re-run daemon install after
installing new CLI tools (for example via uv tool install).
status
agentproto daemon statusPrints a multi-line summary:
agentproto daemon status
plist: installed (/Users/me/Library/LaunchAgents/sh.agentproto.plist)
launchd: loaded · pid=12345 · state=running
/health: ok · v0.20.0 (workspace abc1234, built 2026-09-11T…) · pid 12345 · up 2h (http://127.0.0.1:18790)
bin: /Users/me/.local/share/fnm/node-versions/v22/bin/node /Users/me/.npm/lib/node_modules/@agentproto/cli/cli.mjs
config: /Users/me/.agentproto/config.json
logs: /Users/me/.agentproto/daemon.log
recent logs:
…last 5 lines of daemon.log…The /health line now includes build identity when the running binary was
built with one: source (workspace or published), short git sha, and a
built ISO timestamp. This lets you distinguish a workspace distribution from a
published tarball of the same version. bin shows the exact Node executable and
script launchd (or the shell) invoked — useful when multiple Node managers are
in play.
Exit code is 0 only when plist exists AND launchctl reports the
service loaded. /health reachability is informational — it can be
red briefly during a restart without changing the exit code.
logs
agentproto daemon logs # last 10 lines
agentproto daemon logs --lines 100 # last 100 lines (short: -n 100)Tails ~/.agentproto/daemon.log.
When to use the daemon vs serve directly
agentproto serve— run-as-foreground. Good for ad-hoc development, debugging the tunnel, the--interactiveTUI, or short-lived test sessions where Ctrl-C should kill everything.agentproto daemon— managed by the OS. Survives logout/login, restarts on crash, logs to a file. Use this when you want the tunnel "always up" for a hosted agent (Guilde, etc.).
Both share the same config.json so flipping between them is just
which way you launched.