How profiles work
What a supervisor profile trusts, how grith picks one for a session, and why you cannot write your own.
Every supervised session runs under exactly one profile. A profile lists what a kind of tool touches as a matter of routine - paths, commands, destinations, listen addresses - and grith turns that list into a session allowlist when the session starts. A call that matches the allowlist is allowed immediately and never reaches the proxy: no filters run, no score is computed, and the call is recorded as filtered noise rather than as a decision.
That is the whole job. A profile removes noise. It does not grant a capability, move a threshold, or change what any filter scores.
How grith picks one
First match wins:
--profile <name>passed togrith exec.- Auto-detection from the basename of the command being supervised.
supervisor.default_profile, which ships asgeneric.
$ grith config | grep default_profile
default_profile = "generic"Auto-detection is a fixed basename table and nothing else - no config inspection, no version sniffing, no fuzzy matching:
| Command basename | Profile |
|---|---|
claude, claude-code | claude-code |
codex | codex |
cursor-agent | cursor |
copilot, copilot-cli | copilot |
aider | aider |
goose | goose |
cline | cline |
openclaw | openclaw |
Anything else falls through to the default. A --profile value that is not one of the 11 built-in profiles is fatal before the session starts, with no supervisor profile '<name>' found.
What a profile declares
| Field | What matches | What it covers |
|---|---|---|
routine_paths | A path prefix. A trailing /**, /* or * is trimmed to the prefix, so /tmp/claude-* trusts everything beginning /tmp/claude-. | All file I/O under the prefix - read, write, delete, rename, chmod. |
routine_commands | A command name, resolved through $PATH once at session start. | Spawning that exact binary. Before honouring a match grith re-canonicalises the path and walks every component: each must be owned by root or by you, never world-writable, and never group-writable when root owns it. |
routine_destinations | A domain suffix, so datadoghq.com also covers foo.bar.datadoghq.com. | Outbound connections. These are loaded into the egress-policy filter as trusted domains for the profile too, so they also affect scoring inside the pipeline. |
routine_exec_roots | A directory prefix. | Executing anything underneath it. |
routine_listen_addresses | An address, without a port. A wildcard (0.0.0.0, ::) is refused at parse time. | Binding a listener. |
readonly_paths | An exact path, canonicalised at session start. No prefix, no glob - and an entry for a file that does not exist on this machine is silently dropped. | Reads only. A write, append, delete, rename or chmod on the same path still runs the whole pipeline. |
readonly_path_patterns | The same, with a single-segment * wildcard. | Reads only. |
scratch_roots | A directory prefix. | Nothing in the pipeline. Deletes and renames under it are left out of the volume signal that watches for a mass-delete spree, so routine cache churn never trips it. Every filter still runs. |
launch_contract | - | Arguments grith adds to the tool's own command line at launch. |
local_listener_policy | A port plus an address family. | A declared bind that is allowed instead of queued, with the address rewritten to loopback when the entry sets allow_clamp, and the rewrite audited. The one shipped entry is Codex's kernel-assigned MCP port. |
${HOME} and ${PROJECT_DIR} expand at session start. ${PROJECT_DIR} is the directory grith exec was launched in, not a curated value.
Two further fields, permit_authority_delegating and permit_control_sockets, switch enforcement off for named binaries and sockets. The supervisor honours both, but no shipped profile sets either.
Merge order
A profile resolves once, highest priority first:
- the profile's own entries,
- its parent chain, via
extends, - the
[defaults]block, which every profile receives.
The merge is additive with de-duplication. There is no override and no subtract - a child cannot take back something its parent or the defaults granted.
That is why generic is not as narrow as its own block looks. It declares one entry, ${PROJECT_DIR}/**; after the defaults merge it carries 34 routine paths and 64 routine commands, including git, ssh, the ordinary file and text utilities, and the toolchain scratch directories under /tmp.
Four fields are never inherited and must be declared on the profile that uses them: launch_contract, namespace_users, permit_authority_delegating and permit_control_sockets. Security capabilities do not travel down an extends chain by accident.
What a profile cannot do
The allowlist is a fast path, not an override. Five things it never wins:
- Session containment. While a session is contained the allowlist is not consulted at all, profile-trusted destinations included. Every call goes to the proxy. See session containment.
- Delegating spawns and control sockets. A spawn that hands authority to another process, or a connect to a control socket, cannot be short-circuited by the allowlist.
openclawlistsdockerinroutine_commands; that does not bypass enforcement. - Credential stores inside a trusted project. Project-derived trust carries a marker the allow gate re-checks, so a
.ssh/,.aws/or.gnupg/directory, an.npmrcor.git-credentials, and in-project secrets like.envor a Railsmaster.keyall still go to the proxy even when they sit under a trusted${PROJECT_DIR}. - Explicit read-only scopes. A scope you approved for a sensitive directory still runs the pipeline, so taint and audit state are recorded; the queue is converted to an allow afterwards.
- A dangerous launch directory. If
grith execwas launched at/, at your home directory or at an ancestor of it, every${PROJECT_DIR}entry is dropped for that session with a warning. Run the tool from inside the project.
ℹ️Subtraction is checked first
Both subtractive controls run before the session allowlist. A directory you block from a prompt with [b] beats the allowlist outright. grith exec --workspace-only denies file access outside the workspace at the same point, but it steps aside for the profile's own routine paths and for anything approved earlier in the session - it fences the session in without breaking the tool.
You cannot write your own
The profile set is compiled into the binary. There is no ~/.config/grith/profiles.toml, no config key pointing at one, and no dashboard editor. grith profile has a single subcommand, audit, which is a maintainer tool for reviewing a forensic trace against a profile. There is no grith profile list.
What you have instead:
- Approve once, keep it. The always-allow answer at a prompt records a learned rule in
~/.config/grith/learned_rules.toml, scoped to the profile it was approved under, and loads it back into the allowlist on every later session under that profile. - Widen project trust.
supervisor.trust.additional_project_rootsandinclude_linked_worktreesextend the workspace across sibling checkouts. See supervisor configuration. - Pick a different built-in.
--profile generic-cliand--profile genericare both reasonable choices for a tool with no profile of its own. - Share rules across a team. Pro pulls team learned rules into the same allowlist. See team sync.