grithdocs

Performance and tuning

What grith actually costs at startup and per syscall, and the handful of settings that change it.

grith's cost is per syscall, not per second. An idle agent pays nothing. A busy one pays in proportion to how many interesting syscalls it makes - and most of its syscalls are not interesting.

Startup

The measurements below were taken on 2026-03-10, on a development build from before the 0.1.0 release, on Linux 6.17 x86_64: each tool spawned in a PTY, timed from execvp() to its banner appearing, three runs averaged.

ToolDirectVia grith execOverhead
Claude Code702ms901ms+199ms (+28%)
Codex6,083ms6,275ms+192ms (+3%)

A synthetic sh -c 'printf ...' isolates grith's own startup with no tool startup in the way: 3ms direct, about 122ms under grith exec. That ~119ms is the floor for the current design - PTY allocation, ptrace attach, seccomp install, first TUI frame and session registration - and it is largely kernel work.

These are the only startup numbers grith has published, and they are old enough that you should treat them as an order of magnitude rather than a specification. Measure your own tool if it matters.

Per syscall, in steady state

Costs from the same baseline, per intercepted syscall once a session is running:

ComponentCost
seccomp-BPF filter (kernel)~0.5µs
SIGCHLD wakeup and waitpid1-5µs
ptrace register read~1µs
Syscall classification (average)15-40µs
Session allowlist check~1µs
Full proxy evaluation, when it happens2-10ms

grith treats 87 syscall identities as security-relevant; the seccomp filter returns ALLOW for everything else, so the vast majority of syscalls never stop the process at all. Of the ones that do, most are answered by the session allowlist or the noise filters without the proxy being invoked. Typical per-syscall overhead lands under 50µs.

grith proxy test reports the pipeline half of that directly, as an Eval time: line. On the machine these docs were written on, a full 18-filter pass took about 2ms for a file read and about 3ms for an HTTPS request, run to run.

The settings that actually move the needle

Audit completeness. audit.completeness decides how much routine activity is written to the audit database. spawns is the default. io adds compact rows for routine file reads and writes, roughly +100 MB/day under typical development workloads; all adds noise-path events too, roughly +500 MB/day. Both cost write volume on the supervisor thread as well as disk.

toml
[audit]
completeness = "spawns"   # decisions | spawns | io | all

Noise reduction. These reduce the number of syscalls that reach the pipeline at all, which is usually a bigger win than shaving latency off each one. Both switches are on by default; turn them off only when you are recording a session for forensics.

toml
[supervisor.noise_reduction]
ignore_read_only  = true
batch_rapid_reads = true
batch_window_ms   = 50

A workspace boundary (--workspace-only, or [supervisor.trust] restrict_to_workspace) overrides ignore_read_only for reads outside the workspace. That is a security decision, and it costs a little throughput on purpose.

Log level. general.log_level defaults to info. debug and trace are verbose enough to be felt on a busy session; use them while tuning filters, not in normal running.

The behavioural filter. It records call types from the first syscall but scores nothing until a session has made min_calls_for_baseline calls, so short sessions never pay for it. Raise the threshold if it fires on legitimate work, or set enabled = false.

toml
[proxy.filters.behavioural]
enabled                    = true
min_calls_for_baseline     = 200
mild_deviation_score       = 1.0
significant_deviation_score = 3.0

The cost you actually feel is prompts

A session that stops eight times to ask about the same scratch directory feels far slower than one paying 40µs a syscall. Most of that work is already done for you in v0.3.1: Chromium scratch directories, mktemp scratch paths, ephemeral port binds, and read-only D-Bus probes no longer prompt, and approving a docker command covers that command's family for the rest of the session. One measured Chromium run answered 57 fewer prompts; one docker session answered 14 fewer.

Where a prompt still repeats across sessions, answer it with [l] rather than [a]. That persists a learned rule for the active profile, so the same call is allowed next time without asking. Sensitive targets are never persisted this way.

When grith feels slow

  1. Compare against the tool running unsupervised. Model latency dominates most agent workloads and grith cannot make it worse.
  2. Check the prompt count, not the syscall count. Sessions that feel slow are usually sessions that keep stopping.
  3. Check the audit database size. Deleting rows does not shrink the file - run grith audit compact with the daemon stopped to reclaim the space.
  4. Drop audit.completeness back to spawns if something raised it.

See also

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