6. Capability enforcement
Grant a caller a set of operation classes, and everything outside them scores 10.0.
| Phase | 1, static |
| Score | 10.0, on all three failure paths |
| Source | crates/grith-proxy/src/filters/capability.rs |
| Config | config/filters/capabilities.toml |
The strictest filter grith ships, and the only one that is inert out of the box.
ℹ️It ships permissive
config/filters/capabilities.toml contains no grants, only commented examples. With no grants
defined the filter scores nothing on every call. It switches to enforcement the moment you add
your first [[grants]] entry - and from then on any caller without a grant is denied.
Capability strings
Each call maps to exactly one capability string.
| Capability | Covers |
|---|---|
fs:read | FileRead |
fs:write | FileWrite, FileAppend, FileRename, FileLink, FileChmod, DirCreate |
fs:delete | FileDelete |
fs:list | DirList |
fs:ownership | OwnershipChange |
fs:mount | FilesystemMutation |
shell:exec | ShellExec, ProcessSpawn |
net:http | HttpRequest |
net:connect | NetConnect, DnsQuery |
net:listen | NetListen |
process:ptrace | CrossProcessAccess |
process:namespace | NamespaceOp |
ipc:dbus | DbusMethodCall |
* | everything |
Configuring it
config/filters/capabilities.toml
[[grants]]
plugin = "claude-code"
capabilities = ["fs:read", "fs:list", "fs:write", "shell:exec", "net:http"]
plugin is the caller identity on the call. Repeated entries for the same plugin are merged.
Once grants exist, an ungranted operation scores 10.0 - past the deny line on its own, before any other filter has contributed:
$ grith proxy test '{"type":"ShellExec","command":"ls","args":["-la"]}'
Score: 11.0
Decision: DENY
Reason: Shell execution: ls -la; Plugin 'cli-test' has no capability grants
Filter Breakdown:
+ operation-risk 1.0 [notice] Shell execution: ls -la
+ capability 10.0 [critical] Plugin 'cli-test' has no capability grants
Exit code: 2 (deny)That call came from cli-test, which the file above never mentions - and once any grant
exists, a caller with no entry is denied outright. A caller that does have an entry but not
the right capability is denied identically, at the same 10.0, with Plugin '<id>' lacks capability '<capability>'. Enforcement is all-or-nothing: once one grant exists, every caller
needs one.
A broken file turns enforcement off, quietly
⚠️Check your file parses
If your capabilities.toml does not parse, grith falls back to the grant-free copy embedded
in the binary. The daemon starts, the filter registers, and it scores nothing - so enforcement
is silently off. There is no error at the default log level. After editing the file, run one
call you expect to be denied and confirm it is.
See also
- Supervisor profiles - the per-session control most people want
- Filter config files