Adaptive reputation
The learned trust table: what it observes, where it is applied, and why it only ever flips a queue to an allow.
Two different things in grith are called reputation, and confusing them is the single most common misreading of how scoring works.
- The
reputationfilter is filter 13 in the pipeline. It scores destinations against fixed lists: known-safe -1.0, raw IP +3.0, suspicious TLD +2.0, known-malicious higher, unknown 0.0. It learns nothing. See filter 13. - The learned reputation table is this page. It is not a filter, it is not in the
pipeline, and
grith reputationshows and resets it.
Where it is applied
After the pipeline has finished, in the daemon, and only when the decision is QUEUE.
An ALLOW is never touched. A DENY is never revisited - reputation cannot rescue a denied call, no matter how trusted the shape.
It is all or nothing
This is the part that surprises people. The table does not gently lower a score until a call drifts across the allow line. At the point where it is applied, it either flips the whole decision to allow or it changes nothing at all.
Every one of these must hold for the flip:
| Condition | Default |
|---|---|
| Observations on the matched key | at least 8 |
| Trust score | at least 0.92 |
| Raw composite score | 7.0 or below |
| Safety ceiling | none triggered |
A safety ceiling blocks auto-allow outright, and there are three ways to hit one: any
single filter scored 5.0 or more, or secret-scan matched at all, or the call is a write
to a sensitive directory. A ceiling ends the question - there is no trust level that
overrides it.
On a fresh install the table is empty, nothing clears eight observations, and the whole system behaves exactly as if it were switched off.
$ grith reputation show
No reputation data. Data is learned from approve/deny decisions during grith exec sessions.What it learns from
Only human decisions made at a prompt during a grith exec session. The model cannot
train it and the supervised tool cannot feed it - the daemon's observe endpoint clamps
hostile input, capping weights and key counts so a client cannot self-whitelist its own
future spawns.
| Decision | Weight |
|---|---|
| Approve | 1.0 |
Approve and save a rule (l) | 1.5 |
| Deny | 3.0 |
| Deny and stop the tool | 5.0 |
| Auto-deny | 1.0 |
Denials carry three times the weight of approvals by default. Trust is easy to lose and slow to build, which is the correct asymmetry for a security control.
Approve replay does not feed the table, so hammering the same call cannot launder trust.
The model
One Beta distribution per key, with trust as the ratio of successes to total observations, and an hourly exponential decay of 0.98 - about 61% of a signal remains after a day. Trust that is not renewed fades.
Keys are built at several granularities from the profile, action, process, destination and
path class: the exact path, its parent directory, and the process alone. The grandparent
level is deliberately missing so trust cannot cross sibling directory boundaries. Paths
under sensitive roots - .ssh, .aws, .gnupg, .pki, .config/gh, .kube,
.docker, .config/gcloud, .azure - only ever get exact-path entries, so approving one
file there never generalises.
The table lives in ~/.config/grith/reputation.toml. The daemon owns it; sessions record
observations over IPC; saves merge with what is on disk, so concurrent sessions do not
clobber each other.
Profiles seed it
A profile's routine paths, commands and destinations get optimistic priors at session start, so a fresh install is not maximally noisy on day one. Seeding only fills in where no richer entry already exists - it never overwrites something you taught it.