Daemon & other commands
Daemon lifecycle, config, completion, command discovery, and internal commands.
Daemon management
gr daemon start
Start the daemon — normally automatic and rarely needed.
gr daemon stop
Stop the daemon gracefully. On a supported packaged macOS installation this
leaves the Graith user service registered but dormant. It does not restart at
login or after a crash; the next ordinary gr command starts it on demand.
Graith waits for the daemon’s normal shutdown budget rather than the shorter
startup budget. If the daemon still does not exit, the error includes the daemon
PID and exact recovery commands.
Stopping a daemon is not the same as removing its background-item registration.
Daemon stop, restart, service removal, and replacement are user-only controls;
commands run from an agent session are refused. Session agents can still use
ordinary commands such as gr stop <session>.
Managed nono sandboxes restrict signalling to the sandbox, so clearing session
environment markers cannot grant direct host-process signalling authority.
Safehouse cannot enforce this signal boundary; use nono when it is required.
gr daemon restart
Restart the daemon, preserving live sessions via exec.
| Flag | Description |
|---|---|
--force | Clean stop/start that kills running sessions; escalates from SIGTERM to diagnostic SIGQUIT and then SIGKILL if shutdown wedges |
After rebuilding gr, run gr daemon restart to pick up the new daemon binary.
Crossing from the approval-era protocol to the non-interactive Graith protocol is
an intentional breaking restart: rather than preserving them, graith gracefully
stops the old daemon and all its PTY and headless sessions once it confirms the
exact old socket peer has exited and its socket has disappeared — if either check
fails, no competing daemon starts. Resume stopped sessions to relaunch under the
new security model.
Before it hands off any live terminal, graith checks that the exact replacement binary can adopt every session and understands the current helper handoff. It refuses the attempt while session creation or another lifecycle launch is in progress. Such a refusal leaves the existing daemon and agents running; retry after the reported work finishes. If preparation or exec fails, inheritable terminal descriptors are restored and the existing daemon continues serving. The replacement may use a newer state schema when it has the forward migrations needed to load the running daemon’s exact snapshot. A replacement with an older state schema is rejected as a downgrade, while manifest and adoption protocol versions must still match exactly. Compatibility errors report the bounded numeric target and running versions for each mismatched boundary.
When upgrading from a release with Graith-owned external-tool management, remove the obsolete lifecycle and security keys identified by the new config validation error. The daemon rejects them instead of silently ignoring them. The old daemon drains its managed children, sockets, stderr pipes, and reconnect loops before exec. A live PTY can still be adopted, but any proxy injected into that already-running agent is permanently unavailable. Restart or resume the session to relaunch it without Graith-generated integration arguments or files.
Before migrating older durable state, the replacement writes an exact versioned
backup beside state.json. If permissions, disk capacity, or another storage
failure prevents that backup, startup and exec adoption stop before migration or
session handoff and leave the old state bytes unchanged. Fix the reported storage
problem and retry; do not remove the old state file to bypass this safeguard.
Live adoption requires persisted state and the handoff manifest to prove the exact process identity; the manifest records every live process, not just transferable PTYs. Whether that process uses Graith’s sandbox or the agent’s native approval TUI is preserved, not an adoption requirement. Sessions from pre-transition releases — and headless sessions, which have no adoptable PTY — are identity-checked, terminated, and marked stopped rather than left unmanaged.
The replacement arms cleanup before loading configuration, paths, state, or
authentication, so an early failure still identity-checks and terminates
inherited agents. Once live session adoption is committed, the replacement starts
serving, removes the consumed upgrade journal first, and then repairs interrupted
deletes, soft-delete kills, stale orphan processes, or scenario-add cleanup; those
repairs continue in daemon-owned background work and are logged as startup
recovery phases. If preserve is
accepted but the replacement isn’t ready after the configured startup wait,
graith rechecks the live daemon and falls back to a clean start only after
proving the exact process that answered the pre-upgrade handshake has exited — a
stale PID file isn’t enough — then checks that result for the requested version
and a fresh daemon generation. Otherwise it leaves the possible in-progress
replacement alone: retry once startup finishes, or use --force to kill the
sessions intentionally.
If a preserve restart cannot drain accepted daemon mutations before its
deadline, the error and daemon log identify every active holder by an opaque
lease ID, operation, bounded caller identity, and age. Payloads, tokens, and
message contents are never included. Wait for the named work to finish and
retry; use --force only when intentionally interrupting it.
With --force, graith still first authenticates the daemon socket peer and
checks its process identity so it does not signal an unrelated recycled PID. If
the daemon ignores the graceful stop budget, graith asks the Go runtime for a
SIGQUIT diagnostic dump and then sends SIGKILL to the same verified process.
After a forced exit it removes only the stale socket and PID file entries that
still match the stopped daemon, then starts a fresh daemon. If even this fails,
the error names the PID and gives the manual kill -9 and gr doctor --autofix
recovery commands.
Ordinary commands use the same preserved exec path after a daemon/CLI version
mismatch. If a managed auto-upgrade cannot prove a fresh daemon generation, the
old daemon and sessions keep running; retry with gr daemon restart, then check
gr daemon service status and gr daemon service repair if it repeats.
gr daemon reload
Reload configuration on the already-running daemon without starting, restarting,
or auto-upgrading it. If the daemon is not reachable, start or restart it
explicitly instead. Invalid settings or a non-optional runtime apply failure
return an error and leave the previous config generation in place. Remote
transport replacement closes the old listener first and stays closed if the
replacement fails; the command still succeeds after the config generation is
applied and reports remote access as degraded and closed. Fix the setting or
dependency, then reload again through the local socket; the command prints this
retry guidance whenever it reports remote access as degraded. In
remote.mode = "interface", this includes starting tailscaled and rerunning
gr daemon reload. See remote hot reload.
macOS user service
On macOS 13 or newer, signed Homebrew and release-tarball installations run the
foreground daemon as an app-associated service for the logged-in user. Activity
Monitor and System Settings → General → Login Items can therefore attribute
it to Graith, rather than to the terminal that ran the first command. The
headless Graith.app has no Dock icon or menu-bar UI. It is never installed as
a root or system daemon.
The service is demand-started: install and login do not launch it, and there is no unconditional crash restart. Closing Terminal does not stop a healthy daemon. A crash, logout, reboot, or intentional stop leaves the registered job dormant until the next eligible command. If Login Items says Graith requires approval or is disabled, startup fails with guidance and never bypasses that choice with a Terminal-owned process.
The default profile has its own service label. Named profiles lease distinct static labels, so their labels, sockets, PIDs, config, state, tokens, and lifecycle controls cannot collide. Up to 64 named profiles may remain registered at once (running or dormant); remove a dormant profile service to free a slot. A supported package at capacity fails instead of falling back to a directly spawned daemon.
A Homebrew graith-dev package that includes the signed Graith.app uses the
same service identity but an isolated dev profile and service slot, so it can
coexist with stable Graith. A transitional dev package without that bundle
retains the previous direct-spawn behavior rather than installing an unsigned
service app. After upgrading a managed dev package, run gr-dev daemon restart;
before uninstalling it, run gr-dev daemon service remove. macOS 11/12, source
builds, go install, and Linux keep the direct-spawn behavior. gr doctor
reports which mode is active. The dev and stable channels do not publish
separately named rollback archives; see the
native dev canary guidance
for supported platform artifacts and channel changes.
gr daemon service status
Show the active profile’s service label, slot, lease state, Service Management status, launchd state, PID, and registered/running bundle generations.
| Flag | Description |
|---|---|
--all-profiles | Include the default, every named-profile lease, and quarantined slots |
Use --json for structured output.
gr daemon service remove
Stop the exact profile daemon, unregister its user service, and release a named profile’s slot only after launchd confirms the job is gone. Config, state, worktrees, tokens, messages, and logs are preserved.
| Flag | Description |
|---|---|
--all-profiles | Remove the default and every registered named-profile service |
Run gr daemon service remove --all-profiles before uninstalling a macOS
package. An ordinary upgrade must not remove the services; Graith keeps signed,
versioned app generations and rotates each dormant registration safely.
gr daemon service repair
Validate the owner-only service receipt and its backup, inspect only Graith’s 65
exact compiled labels and signed cached apps, and repair state that is proven
dormant. A valid backup restores the primary. Unknown live, disabled, or
indeterminate jobs are quarantined rather than killed or reassigned. Reinstall a
signed package first if gr or Graith.app was deleted before service removal;
then run this command instead of using wildcard launchctl cleanup.
The managed service starts with a deliberately small environment projection.
See [daemon_service]
before upgrading if agents depend on SSH_AUTH_SOCK, cloud credentials, or API
keys inherited from your terminal.
Agent introspection
gr agent list (alias: gr agent ls)
List the agents configured in the daemon’s effective configuration. The table shows the agent name, whether it is the configured default, the launch command, and any configured provider info keys.
Use --json for the raw catalog:
{
"agents": [
{
"name": "cursor",
"command": "agent",
"info_keys": ["model", "version"]
}
],
"default_agent": "claude"
}
gr agent info <agent> [key]
Run configured provider info commands for an agent. Without [key], graith
runs every key under [agents.<agent>.info] in sorted order. With [key], it
runs only that key, for example:
gr agent info cursor model
gr agent info cursor --json
gr agent info cursor --refresh --json
gr agent info cursor --no-cache
The daemon runs the configured agent command with the selected info argv in
a daemon-owned provider context with the agent’s optional Graith sandbox. It
does not require the calling shell to have the provider CLI available. Normal
session launch args, prompt injection, hooks, and included-repo flags are not
applied.
Successful results are cached in daemon memory for one hour by default, keyed by
agent, info key, and effective command configuration. Configure the default with
[agent_info].cache_ttl; set it to "0" to disable caching. When global caching
is enabled, rich info-command tables can override one key with cache_ttl. The
cache is empty after daemon restart. --refresh bypasses the read side and
updates the cache on a successful provider command; --no-cache bypasses both
cache reads and writes. The two flags are mutually exclusive. Expired entries
are not returned. If a provider command fails after expiry, the failure is
returned instead of stale output. Concurrent misses or refreshes for the same
agent/key share one provider command where possible.
The human-readable single-key form prints the provider stdout directly. With
--json, stdout, stderr, cache metadata, exit code, truncation flags, warnings,
structured fields, and per-result errors are separate fields:
{
"agent": "cursor",
"results": [
{
"key": "model",
"command": "agent",
"args": ["--list-models"],
"format": "model_list",
"stdout": "auto - Auto\n",
"models": [
{
"id": "auto",
"description": "Auto"
}
],
"cache": {
"enabled": true,
"hit": false,
"fetched_at": "2026-07-29T12:00:00Z",
"expires_at": "2026-07-29T13:00:00Z"
},
"exit_code": 0
}
]
}
Unknown agents, unknown info keys, and agents without info configuration fail
the request. Provider command failures and timeouts are returned on the specific
result, then the CLI exits non-zero after printing the plain or JSON output.
Configuration and lookup errors are returned before running probes. Each info
command has a 30 second daemon timeout. Captured stdout and stderr are capped at
1 MiB each; JSON results set stdout_truncated or stderr_truncated when a
stream exceeds that cap, and plain output prints a truncation marker. Cleanup
diagnostics that do not invalidate successful provider output are returned as
warnings.
Other commands
gr config show
Print the effective (merged) configuration.
gr config diff
Show changes from built-in defaults.
gr config reset
Write built-in defaults to the config file.
| Flag | Description |
|---|---|
--force | Overwrite without confirmation |
gr completion <shell>
Generate a shell completion script. Supported shells: bash, zsh, fish, powershell.
gr help-tree [COMMAND...]
Print a compact command tree for quickly discovering the CLI. By default it prints text with two-space indentation, branch command descriptions, and leaf command args plus usable non-global flags. Hidden and deprecated commands are omitted.
Pass a command path to inspect only that subtree:
gr help-tree msg
gr help-tree scenario start
| Flag | Description |
|---|---|
--depth <n> | Limit nesting depth; 1 shows the selected command plus direct children, 0 is unlimited |
Use --json for the same tree as structured output.
gr version
Print version information.
Hidden/internal commands
Used by graith internally, not intended for direct use:
| Command | Purpose |
|---|---|
gr report-status | Report agent status (used by hooks) |
gr check-inbox | Check unread inbox messages (used by hooks) |