Filter config files
The ten TOML files filters load, where an override has to sit, and why an override replaces rather than extends.
Filters that carry too much data for config.toml load it from separate files. The shipped
copies are compiled into the binary - the installer writes nothing to disk - and the only
override that a plain install honours is a ./config/filters/ directory relative to the
working directory grith runs in.
The files
| File | Root shape | Holds |
|---|---|---|
paths.toml | [[rules]] | 15 path rules, scored 3.0-5.0 |
secrets.toml | [[patterns]] | 1618 credential regexes |
commands.toml | [[rules]] | 12 dangerous-command patterns, 2.0-4.0 |
meta_rules.toml | [[meta_rules]] | 5 composite rules, each with its own [[meta_rules.conditions]] |
domains.toml | [known_safe], [known_malicious] | 27 safe domains, 13 malicious |
egress.toml | [egress] | Mode, 51 trusted domains, scheme and port lists, entropy bounds |
containment.toml | [containment] | The 600s window, 13 sensitive sources, three scores |
dlp.toml | [dlp] | enabled and policy |
canary.toml | [canary] | enabled, plus optional [[canary.tokens]] |
capabilities.toml | [[grants]] | Empty as shipped, which is why the capability filter runs permissive |
There is no single common shape - the root key differs per file, and getting it wrong fails
quietly either way: a file missing a required root key (rules, patterns, meta_rules) does
not parse and grith falls back to the built-ins, while domains.toml and capabilities.toml
default every field and load as an empty ruleset.
An eleventh file, allowlist.toml, is not shipped. It is optional at
./config/filters/allowlist.toml and at ~/.config/grith/filters/allowlist.toml, which is
where an always-allow answer at a prompt is persisted. It is the only file with a
home-directory layer, and the only one that merges rather than replaces.
Where grith looks
For every file except allowlist.toml, three candidates in order, first one that parses wins:
./config/filters/<file>.toml- relative to the current working directory- the same path relative to the source checkout, if you are running from one
- the copy embedded in the binary
Paths are hard-coded. config.toml has no key that points at any of them - the
config_file and domains_file keys you may see in old configs are dead.
⚠️An override replaces the whole file
grith does not merge your file with the built-in one. The first candidate that parses is the
entire ruleset. Write a two-rule paths.toml and you have two path rules, not seventeen.
Prove it to yourself. Baseline, with no override anywhere:
$ grith proxy test '{"type":"FileRead","path":"/home/u/.ssh/id_rsa"}'
Proxy Test Result
==================================================
Tool call: FileRead(/home/u/.ssh/id_rsa)
Score: 9.0
Decision: DENY
Reason: Access to SSH private key; read access to key/certificate file
Filters: 18 active
Filter Breakdown:
. operation-risk 0.0 + path-match 5.0 [critical] Access to SSH private key + sensitive-path-heuristic 4.0 [error] read access to key/certificate fileNow with a ./config/filters/paths.toml holding one unrelated rule:
$ grith proxy test '{"type":"FileRead","path":"/home/u/.ssh/id_rsa"}'
Proxy Test Result
==================================================
Tool call: FileRead(/home/u/.ssh/id_rsa)
Score: 4.0
Decision: QUEUE (Medium)
Reason: Score 4.0 in escalation zone
Filters: 18 active
Filter Breakdown:
. operation-risk 0.0
. path-match 0.0 + sensitive-path-heuristic 4.0 [error] read access to key/certificate fileThe SSH rule is gone, the score drops below the deny line, and nothing warned you. A file that
fails to parse is worse: grith falls through to the next candidate and uses the built-ins,
silently. Test every edit with grith proxy test.
Adding a rule
Copy the shipped file first, then add yours - and check with grith proxy test that the
built-in rules survived.
config/filters/paths.toml
[[rules]]
id = "customer-data"
pattern = "~/work/customer-data/*"
operations = ["read", "write", "delete"]
score = 5.0
severity = "critical"
message = "Customer data must not leave the machine"$ grith proxy test '{"type":"FileRead","path":"/home/u/work/customer-data/accounts.csv"}'
Proxy Test Result
==================================================
Tool call: FileRead(/home/u/work/customer-data/accounts.csv)
Score: 5.0
Decision: QUEUE (Medium)
Reason: Score 5.0 in escalation zone
Filters: 18 active
Filter Breakdown:
. operation-risk 0.0 + path-match 5.0 [critical] Customer data must not leave the machine
. sensitive-path-heuristic 0.0An internal secret shape goes in secrets.toml:
config/filters/secrets.toml
[[patterns]]
id = "acme-internal-id"
regex = "ACME-CID-[0-9]{8}"
score = 4.0
severity = "critical"
message = "Internal customer ID format detected"An extra trusted destination goes in egress.toml's trusted_domains array, a never-trusted
one in blocked_domains. Both are flat arrays inside [egress], not [[allow]] / [[deny]]
tables.
Applying a change
These files are read at process start and not watched. Restart the daemon:
$ grith daemon stop && grith daemon startThere is no grith config reload - that command does not exist. If no persistent daemon is
running, the next grith exec starts one that picks the change up.
Two that will surprise you
meta_rules.toml is required, not an optional extra - it loads on every start and a file
that will not parse is a hard config error. Its five rules adjust the composite after phase 3,
so they only reach calls that get that far: the ~/.ssh/id_rsa read above is already at 9.0 and
denied at the end of phase 1, before the ssh-key-access override ever runs. See
Meta-rules.
Half of domains.toml does nothing. Only the domains array inside [known_safe]
and [known_malicious] is deserialised, so the patterns array (*.onion, *.bit, *.i2p)
and the whole [suspicious] table are prose left in the file, not policy.