NxtSoftLabs
← All writing

One daemon per repo, always warm: cgraph daemon install

August 4, 2026·5 min read

To keep a code graph warm for every repository you work in — without ever starting it by hand — cgraph daemon install registers a supervisor with macOS launchd that discovers your CGraph-enabled repos, runs one resident graphd per repo with idle shutdown disabled, restarts any daemon that crashes, and brings the whole set back up at login. Install it once and the ~10ms warm query is simply always available.

This post covers what the supervisor actually manages, how it decides which repos to track, and the four commands that run it: install, sync, status, uninstall.

The problem: warm queries need a running daemon

Everything fast about CGraph depends on the daemon being up. graphd builds the graph once, holds it resident in memory, watches the tree, and folds your edits in as you work — that's what makes impact analysis interactive instead of a rebuild-and-wait.

But a plain graphd is deliberately polite: after 5 minutes of inactivity it shuts itself down so idle per-project daemons don't linger. Come back from lunch, ask a question, and your first query pays the cold-start build. And nothing starts a daemon for the repo you cloned yesterday, or restarts one that died, or brings any of them back after a reboot.

Those are lifecycle problems, and the fix is the platform's service manager, not a shell script you have to remember. cgraph daemon install hands the lifecycle to launchd.

What install actually sets up

One command:

cgraph daemon install --search-root ~/code

This registers two kinds of launchd LaunchAgents and reconciles once:

  • A supervisor (com.cgraph.supervisor) that runs at load and then on a recurring interval — 5 minutes by default, tunable with --interval — re-scanning your search roots and reconciling the daemon set against what it finds.
  • One LaunchAgent per tracked repo, each running a resident graphd in never-idle mode. KeepAlive means a daemon that exits unexpectedly is restarted; RunAtLoad means the whole set comes up at login with no manual command.

"Never-idle" is the point. A supervised daemon doesn't use the 5-minute idle shutdown — it stays present and keeps folding edits into the graph continuously, so a query at 9am Monday is as warm as one mid-refactor.

How repos get tracked: discovery by .mcp.json

The supervisor doesn't guess. It scans your search roots and tracks exactly the directories whose .mcp.json registers a cgraph MCP server — the same file you already created when you wired CGraph into your agent. A repo with no .mcp.json, or one whose .mcp.json registers other servers but not cgraph, is left alone.

Discovery is deterministic, and each tracked repo is keyed by the same canonical-root hash the daemon uses for its socket identity — so a repo's discovery key, its LaunchAgent label, and the endpoint your MCP tools connect to all agree by construction.

Search roots come from a config file, flags, or both. The file is ~/.config/cgraph/supervisor.json:

{
  "search_roots": ["/Users/you/code"],
  "exclude": ["/Users/you/code/archive"]
}

--search-root (repeatable) and --exclude do the same thing per invocation.

sync and status: reconcile now, see the set

cgraph daemon sync runs one reconcile immediately instead of waiting for the interval. Newly discovered repos gain a daemon, repos that disappeared (or dropped their cgraph registration) have their daemon stopped and their LaunchAgent removed, and everything present in both sets is left running undisturbed:

sync: +1 daemon(s), -0 removed
  + /Users/you/code/new-service

Reconciliation is idempotent — run sync twice with nothing changed and the second run starts, stops, and rewrites nothing.

cgraph daemon status lists each tracked repo and whether a live daemon is serving it. Here it is against this machine, real output:

tracked repos (2)
  [live]    /Users/taylorgagne/tools/cgraph
  [live]    /Users/taylorgagne/tools/project-cybertron

What the supervisor will never do

Two guarantees worth stating, because a background process touching every repo you own should earn some trust:

  • It never mutates a repo. The supervisor performs no git operation and changes no tracked file — it only reads the working tree to build the graph. Your checkouts are inputs, not targets.
  • Uninstall leaves no residue. cgraph daemon uninstall stops and removes the supervisor and every managed per-repo LaunchAgent. Install followed by uninstall leaves no managed agents behind and no managed daemon running.

One platform caveat, stated in the tool itself: launchd-based supervision is macOS-only. On Linux, cgraph daemon tells you to run graphd --root PATH directly or supervise it with your own init system, such as a systemd user unit — the daemon itself is portable; the supervisor is the launchd-specific part.

Why always-warm matters for agents

An AI coding agent doesn't retry politely. If its first impact call hits a cold daemon and waits out a full build, the agent may conclude the tool is slow and stop reaching for it — and our op-stats ledger exists precisely because what agents actually call diverges from what you wired up. Keeping every repo's graph resident removes the cold-start penalty from the agent's decision entirely: every call, in every repo, on every morning, is a warm one.

Try it

cgraph daemon install --search-root ~/code
cgraph daemon status

Then edit something and query — no build step, the resident daemon already folded your change in. The supervisor, the reconcile logic, and the LaunchAgent generation are all open source — read the code on GitHub. For what the daemon does once it's running, start with the daemon docs and incremental updates.