grithdocs

The daemon and thin-client sessions

One process owns the audit log, the filters and the queue; every supervised session is a client of it over loopback HTTP.

grith runs one daemon per machine. It owns everything that has to be single-writer or shared, and every supervised session talks to it as a client. There is no second mode, no embedded fallback and no way to run a session without it.

What the daemon owns

  • The audit database, as the exclusive writer. The lock is an flock held for the process lifetime, so the kernel releases it however the process dies. Everything else - grith audit, grith log, grith digest - opens the database read-only and says so.
  • The security proxy and all 18 filters, including the compiled secret-pattern set that is expensive to build and pointless to build twice.
  • The digest queue.
  • The session registry, the canary registry and the learned reputation table.
  • The HTTP and WebSocket server on 127.0.0.1:3141, which serves the dashboard.

Because there is one filter registry and one reputation table, two sessions on the same machine score consistently and learn from each other's approvals.

Sessions are thin clients

grith exec, grith supervisor and grith reputation are thin-client commands. They find the daemon, authenticate, and do their work through it.

The transport is loopback HTTP with a bearer token, not a unix socket. The token lives in ~/.config/grith/daemon.token at mode 0600 and is rotated on every daemon start; a client that gets a 401 re-reads it from disk, so a daemon restart heals a live session rather than killing it.

Inside a supervised session the digest store, the audit sink and the session registration are all remote shims. A supervised call is scored by asking the daemon over that channel, which is why the audit log is complete and the reputation table is shared.

grith exec fails closed

There is no in-process fallback daemon. If grith exec cannot reach a healthy, version-matched daemon, it refuses to start and your tool is never spawned. The failure modes it distinguishes are worth knowing:

ConditionMeaning
SpawnFailedThe daemon could not be started
PortOwnedByForeignProcessSomething that is not grith holds port 3141
VersionMismatchA daemon of a different build is running
TokenRejectedThe IPC token did not authenticate
AuditQuarantinedThe audit chain failed verification - no session is admitted
AuditReadOnlyThe audit database cannot be written

AuditQuarantined is the sharp one. If the hash chain is broken, the daemon refuses to admit sessions rather than write new records onto a chain it cannot vouch for. grith audit diagnose is read-only and works in exactly that state.

Setting server.enabled = false makes grith exec refuse outright.

Starting and stopping

Most people never start it by hand. grith exec, grith run and the REPL auto-start a daemon if one is not already listening, re-execing themselves detached and polling until a daemon of the same version answers.

The difference between an auto-started daemon and grith daemon start is what happens when the last session ends:

  • Auto-started: a watchdog polls the session registry every 5 seconds and shuts the daemon down 30 seconds after the registry empties.
  • Explicitly started: idle shutdown is disabled. It stays up until you stop it.

A separate reaper runs regardless, evicting sessions whose root process is dead and whose heartbeat has gone stale.

grith daemon stop asks over the authenticated shutdown route first and falls back to SIGTERM. The command is also spelled grith dashboard - a retained alias, and several messages the binary prints still use it. Both work.

Runtime files

All under ~/.config/grith/:

FileWhat it is
dashboard.pidThe daemon's pid and port
daemon.tokenThe IPC bearer, 0600, rotated on every start
dashboard.tokenThe browser token, 0600, stable across restarts
daemon.jsonInstance id, pid, port, version, protocol version, audit database path
reputation.tomlThe learned reputation table

daemon.token and daemon.json are removed on exit. dashboard.token is deliberately kept, so a browser tab you already paired stays authorised across a restart.

Capacity

supervisor.max_concurrent_sessions defaults to 4, but the licence caps it: a community licence allows 2 concurrent supervised sessions, paid plans allow 64. The effective limit is the lower of the two, which on a stock paid install means the config value of 4 is what bites, not the licence.

Capacity is reserved before your tool is spawned, so a rejection can never arrive after code has already run.

See also

Last updated: 2026-08-24Edit this page on GitHub →