grithdocs

Tuning scoring thresholds

Where the allow and deny lines actually fall, how to move them, and when moving them is the wrong fix.

Two numbers decide what happens to every tool call: proxy.auto_allow_threshold (3.0) and proxy.auto_deny_threshold (8.0). Everything a filter contributes is added into one composite score, and the score is routed against those two lines.

Where the lines fall

Both boundaries are strict greater-than, so both boundary values land on the less restrictive side.

Composite scoreDecision
3.0 or belowallow
above 3.0, up to and including 8.0queue for review
above 8.0deny

A score of exactly 8.0 queues. It does not deny. That is deliberate: on the boundary a human gets to look, rather than the tool getting a bare EPERM it cannot explain.

terminal
$ grith proxy test '{"type":"FileRead","path":"/home/u/.aws/credentials"}'
Proxy Test Result
==================================================
Tool call:   FileRead(/home/u/.aws/credentials)
Score:       8.0
Decision:    QUEUE (Critical)
Reason:      Score 8.0 in escalation zone
Eval time:   1.79ms
Thresholds:  allow < 3, deny > 8
Filters:     18 active

Look at the score before you move the line

grith proxy test runs the real pipeline on a call you describe and prints every filter's contribution. Run it on the call that annoyed you rather than guessing.

CallScoreDecision
FileRead /home/u/project/src/main.rs0.0allow
FileWrite /home/u/project/out.txt0.5allow
ProcessSpawn npm install1.0allow
HttpRequest GET registry.npmjs.org-1.5allow
FileRead /etc/passwd2.8allow
NetConnect 198.51.100.7:4433.5queue
FileRead /home/u/.env6.0queue
FileRead /home/u/.aws/credentials8.0queue
ShellExec rm -rf /10.0deny

Note the negative score. On that npm row egress-policy and reputation each returned -1.0 - a trusted outbound destination and a known-safe domain - against +0.5 for the request itself. The allowlist filter subtracts the same -1.0 for an explicit allow entry, so evidence can subtract as well as add. Raising the allow threshold to silence something that already scores 6.0 also waves through everything between 3.0 and 6.0 that you have not looked at.

Making the change

Edit ~/.config/grith/config.toml:

~/.config/grith/config.toml

toml
[proxy]
auto_allow_threshold = 3.5
auto_deny_threshold  = 8.0

Or grith config set proxy.auto_allow_threshold 3.5, which rewrites the whole user config file (losing comments) rather than patching one line.

Either way the daemon reads thresholds at startup. Run grith daemon restart before you expect the new numbers to apply - a running session keeps the values it was born with.

⚠️Do not hand-write a sparse config file

Every config layer contributes its Rust struct defaults for keys it omits, so a two-line config.toml overwrites more than the two lines you wrote. Start from what grith init wrote, or use grith config set.

What the daemon refuses

Both thresholds are validated at load, so a bad pair stops the next command rather than silently clamping. --config is the quickest way to try a file before it becomes your live one:

terminal
$ grith --config config.toml config
Error: configuration invalid:
proxy.auto_allow_threshold (9) must be less than auto_deny_threshold (8)
terminal
$ grith --config config.toml config
Error: configuration invalid:
proxy thresholds must be in range [0.0, 10.0]

The rules are: allow must be strictly less than deny, and both must sit within 0.0 to 10.0.

When the threshold is the wrong fix

If the same shape of call queues over and over, the threshold is not the problem - that one call is. Answering the prompt with [l] records a profile-scoped learned rule that is merged into the session allowlist on every future session with that profile, so you are not asked again; sensitive targets are never saved that way. In the digest, p writes a durable entry to ~/.config/grith/filters/allowlist.toml instead.

Approvals also feed the learned reputation table, which can auto-allow a queued call once its key has at least eight observations and enough trust. It never revisits a denial.

The threshold is a global knob. Loosening it to quieten one chatty pattern loosens everything else at the same time.

See also

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