Daemon and thin-client sessions
Why grith optionally runs as a daemon, and what changes when it does.
The simplest way to use grith is one-shot per session: grith exec spawns the agent,
supervises it, exits when the agent exits. That works fine for a single terminal,
single agent, single user.
A persistent daemon is what you run for everything else — multiple concurrent
sessions, the dashboard, notification channels, team sync. The daemon is a
long-running grith process that owns the shared subsystems (audit, digest,
reputation, server) and lets thin clients (each grith exec invocation) talk to
them. Even a lone grith exec uses a daemon: when you haven't started one, it
auto-starts a throwaway daemon that shuts down when the session ends.
Auto-started vs persistent daemon
Every grith exec runs against a daemon — the only question is whether it starts one
for you or connects to one you already have running. There is no daemon-less mode: if a
trusted, version-compatible daemon can't be established, grith exec fails closed and
starts nothing.
Auto-started (no daemon already running):
grith exec -- claude
# No daemon found → grith starts one in the background, then registers this sessionThe daemon grith starts idle-shuts-down once the session ends. For the life of the session it still owns the shared subsystems — one audit database, one digest queue, one reputation table, one session registry.
Persistent (grith daemon start first):
grith exec -- claude
# Detects the running daemon, registers as a thin-client sessionA persistent daemon outlives individual sessions, so:
- The audit log is one central SQLite database across every session.
- Digest is one central queue, reviewable from any terminal or the dashboard at
http://127.0.0.1:3141. - Notification channels (configured once in the daemon) deliver review prompts.
- The reputation table is shared across all sessions.
- Concurrent sessions register together and count against your plan's concurrent-session limit.
What the daemon owns
- The server (
crates/grith-server/) — REST API, WebSocket, dashboard. - The digest queue — single SQLite table, atomic decisions.
- The audit database — central, indexed, retention-managed.
- The reputation table — saved to disk every 5 minutes by default.
- The notification dispatcher — owns channel state and rate-limit budgets.
- The license — refreshed in-band on Pro plans.
- The canary registry — same tokens visible to every session.
A daemon restart can drop in-flight digest items (the process freeze in the thin client times out and auto-denies). On the Pro plan, restarts are clean — the daemon flushes pending state to disk before stopping and recovers it on start.
Thin-client sessions
When a daemon is running, grith exec becomes a thin client:
grith exec --profile claude-code -- claude
# Internally:
1. Connect to daemon UNIX socket
2. POST /ipc/sessions → daemon assigns session id, returns config snapshot
3. fork + exec the supervised target, attach ptrace
4. On each interesting syscall, POST /ipc/proxy/evaluate with the call shape
5. Apply daemon's decision (allow / queue / deny)
6. On exit, DELETE /ipc/sessions/{id}The supervisor (ptrace machinery) still runs in the thin-client process — it has to, because that's the process attached to the agent. But the filter pipeline runs in the daemon, which means scoring decisions can use global context (other sessions' recent behaviour, the central reputation table) that the thin client wouldn't have.
Starting and stopping the daemon
grith dashboard start # background; prints connection details + opens/pairs the browser
grith dashboard status
grith dashboard pair # (re)authorise a browser
grith dashboard stopgrith dashboard start spawns the daemon in the background and returns. It's
also auto-started for you by grith run and grith exec (those instances
idle-shut-down with the session; an explicit start persists until you stop it).
The daemon is local-only by default — binds to 127.0.0.1:3141. To expose it (for
the dashboard from another machine, or for a webhook receiver), either configure
[server.tls] with cert + key, or run a reverse proxy in front. See
Reverse proxy & TLS.
When you'd not keep a persistent daemon
- Single-user laptop, one terminal at a time — the daemon
grith execauto-starts is enough. - Containerised agents where each container is its own short-lived session.
- CI runs where the agent runs once and exits.
When you'd keep a persistent daemon:
- More than one supervised session in flight.
- You want the dashboard.
- You want notifications routed through Slack / Telegram / etc.
- You're on Pro and using team policies / sync.
The auth model
The daemon binds loopback-only, but loopback reachability is not treated as authorisation:
- IPC routes (
/ipc/*,/server/shutdown) require the daemon bearer token (~/.config/grith/daemon.token,0600), constant-time compared. - Browser routes require the per-server dashboard token — mutations and
sensitive reads on every request, gated via the
x-grith-csrfheader (or?token=for WebSockets). Low-sensitivity status stays open. The browser receives the token through the pairing flow, never the terminal.
Exposing beyond loopback additionally requires TLS (or a reverse proxy). For team setups, the Pro plan adds short-lived bearer tokens issued by the dashboard for cross-machine review. See Trust boundaries and Notification security.
See also
- grith daemon — CLI reference
- Running as a daemon — operations guide
- API overview — what the daemon serves