Configuration lives at ~/.config/graith/config.toml (or $XDG_CONFIG_HOME/graith/config.toml). Every field is optional, with sensible defaults.

Manage config with:

  gr config show     # print effective (merged) config
gr config diff     # show changes from defaults
gr config reset    # write built-in defaults to config file
  

The daemon reloads config on gr daemon reload without restarting, and also watches config.toml to reload in place when you save it:

  [config]
reload_debounce = "200ms"  # quiet period after the last write before reloading
  

reload_debounce coalesces an editor’s write-truncate-write burst into a single reload. It is read at daemon start, so changing it takes effect only after a gr daemon restart. Most other settings re-read on reload; startup-only settings call that out below.

Telemetry runtime settings are startup-only once enabled. Enabling or disabling metrics or tracing, or changing settings for an enabled telemetry runtime, requires gr daemon restart; a daemon reload rejects that runtime-affecting config generation and keeps the previous telemetry runtime active.

Daemon process logs rotate by size. These settings are read when the daemon starts, so restart after changing them:

  [logging]
daemon_max_bytes = 104857600 # rotate daemon.log at 100 MiB; 0 disables rotation
daemon_max_backups = 3       # retain daemon.log.1 through daemon.log.3
  

A reload publishes one config generation. If an optional runtime cannot be prepared, the command reports the affected subsystem as degraded while unrelated settings still take effect. Remote listener replacement remains fail-closed: graith closes the old listener before the new bind, so a failed remote-transport reload publishes the generation with remote access closed until a corrected reload succeeds. For remote.mode = "interface", start tailscaled and run gr daemon reload again after the dependency is available. See Orchestrator & remote access.

Full default config. The complete, annotated defaults live in internal/config/default_config.toml — the file gr config reset writes, and the authoritative reference.

This reference is organized by area:

Global settings

  default_agent      = "claude"             # agent used when --agent is not given
github_username    = ""                   # expands {username} in branch_prefix
branch_prefix      = "{username}/graith"  # template for new branch names
fetch_on_create    = true                 # fetch origin before creating a worktree
data_dir           = ""                   # override data directory (default: XDG data home)
allowed_repo_paths = []                   # restrict which repo paths the daemon accepts
  

default_agent may be empty or must match a configured [agents.<name>] key, including a built-in agent. An unknown name is rejected during startup and reload; a rejected reload keeps the previous configuration active.

agent_prompt

A multiline string injected into the agent’s environment, teaching agents to use gr status, gr msg, gr store, and other graith primitives. Claude gets it via --append-system-prompt; Cursor via .cursor/rules/graith.mdc; Codex via a per-session -c developer_instructions=... override (never written to a repository AGENTS.md). Other agents (OpenCode, Agy) and custom agents get no injection by default but can opt in via prompt_injection under [agents.<name>] (see Agents). Set inject_prompt = false per-agent to disable.

For Codex, developer_instructions is single-valued, so graith’s override replaces (not appends to) any set in ~/.codex/config.toml, a project .codex/config.toml, or a selected profile — the CLI override is highest-precedence. To keep your own, set inject_prompt = false under [agents.codex].

allowed_repo_paths

When non-empty, the daemon rejects --repo / -C paths not under one of these prefixes. Paths support ~ expansion and resolve to absolute before comparison. They also feed the repo autocomplete in the create-session form (ctrl+b c or n in the overlay) — each is scanned one level deep for git repositories.

  allowed_repo_paths = ["~/Code", "~/Work"]
  

macOS daemon service environment

Signed packaged installs on macOS 13 or newer start the daemon through launchd, which does not inherit the first terminal’s full environment. Graith projects a small validated base into the one-use startup request:

  • PATH (absolute, non-empty entries), SHELL, and TMPDIR (unless its canonical path would expose Graith’s protected macOS service tree);
  • LANG and LC_*; and
  • absolute XDG_CONFIG_HOME, XDG_CACHE_HOME, XDG_DATA_HOME, XDG_STATE_HOME, and XDG_RUNTIME_DIR overrides.

HOME, USER, and LOGNAME come from the effective user’s OS account, and the canonical profile comes from the protected service lease. To opt additional variable names into the daemon and its eligible agent processes:

  [daemon_service]
inherit_env = ["SSH_AUTH_SOCK", "ANTHROPIC_API_KEY"]
  

This is an explicit credential grant. Values are not stored in the durable service receipt or logs, but they briefly exist in an owner-only startup file below ~/Library/Application Support/Graith/services/control/bootstrap until the daemon consumes and unlinks it; macOS does not promise secure deletion. A variable absent from the shell that wins a startup race is simply omitted. gr doctor prints the effective variable names, never values, and calls out common current-shell variables that were not opted in.

Identity, loader, and launch-service variables cannot be opted in: HOME, USER, LOGNAME, GRAITH_PROFILE, all GRAITH_*, DYLD_*, LD_*, XPC_*, and __CF* names are rejected. Linux, macOS 11/12, source/go install builds, and unmanaged development artifacts retain the full direct-spawn environment, so the same configuration intentionally differs across managed and fallback installs. Restart a dormant service after changing inherit_env; reload cannot replace a running process environment.

File locations

graith follows the XDG base directory spec (override data_dir to change the base data directory):

PathContents
~/.config/graith/config.tomlConfiguration file
~/.local/share/graith/state.jsonPersisted session state
~/.local/share/graith/messages.sqliteInter-agent message store
~/.local/share/graith/daemon.logDaemon log (slog, JSON format)
~/.local/share/graith/daemon.stderr.logDaemon runtime stderr (panic tracebacks, SIGQUIT goroutine dumps, race detector output)
~/.local/share/graith/worktrees/<repo>/<hash>/<id>/Session worktrees
~/.local/share/graith/store/<repo-name>-<hash>/Per-repo document stores
~/.local/share/graith/store/shared/Shared document store
~/.local/share/graith/tmp/<repo-name>/<hash>/Per-repo temp directories
$XDG_RUNTIME_DIR/graith/graith.sockUnix control socket
$XDG_RUNTIME_DIR/graith/graith.pidDaemon PID file
~/Library/Application Support/Graith/services/macOS signed service generations, global profile-slot receipt, and bootstrap control (owner-only, independent of HOME, XDG settings, and data_dir)

On managed macOS, Graith resolves that location from the effective user’s OS account record, not the HOME environment variable. One-use requests live in the services/control/bootstrap subtree. Graith-generated safehouse and nono policies do not grant that subtree or an enclosing directory; disabling the sandbox or explicitly granting a broad home directory remains an operator exposure. TMPDIR, XDG settings, profile paths, runtime paths, caller config, and data_dir never choose the service-control boundary.

Graith also rejects a managed daemon start before registration if inherited TMPDIR is the services tree, an enclosing directory, or a symlink to either; this prevents sandbox base policies from implicitly making service state writable.