Skip to Content
GuidesPermissionsRule grammar

Permission rules

A permission rule names a tool and optionally narrows which calls to it match, in one of three forms: the bare tool, tool(argGlob), or tool(operation:argGlob). The rule everyone gets wrong is precedence — rules are not applied in the order you added them. deny beats ask, and ask beats allow, so a broad allow can never re-enable what an explicit deny covers.

A rule names a tool and, optionally, narrows which calls to that tool it matches. Add the narrowest rule that supports a task you have actually tested:

cmdop permissions allow 'execute_command(git status)' cmdop permissions ask 'execute_command(git diff *)' cmdop permissions deny 'execute_command(rm *)' cmdop permissions list

Quote rules containing spaces or shell metacharacters so your shell passes them to Cmdop unchanged.

The three forms

FormMatchesExample
toolEvery call to that tool.file_write
tool(argGlob)Calls whose main argument matches the glob.execute_command(git *), file_write(/app/**)
tool(operation) or tool(operation:argGlob)One operation of a tool that dispatches several, optionally narrowed by a glob.connect(exec), connect(exec:prod-*)

* matches within one path or command segment; ** crosses separators, which is why file_write(/app/**) covers a whole tree while file_write(/app/*) does not.

Precedence: deny beats ask beats allow

Rules are not evaluated in the order you added them. deny wins over ask, and ask wins over allow — always. This is the behavior people most often get wrong.

Two consequences worth internalizing:

  • A broad allow can never re-enable something an explicit deny covers, so a deny for a high-risk pattern is safe to leave in place permanently.
  • Adding an ask on top of an existing allow does downgrade that path to a prompt — the ask wins.

Beneath all of it sits the protected floor, which no rule and no mode can override.

Add and remove a rule

cmdop permissions allow 'execute_command(git *)' \ --reason 'Reviewed read-only Git operations' cmdop permissions list cmdop permissions revoke 'execute_command(git *)'

revoke expects the exact raw rule text shown by list, not an equivalent pattern. Review the list after every change, especially when automation generates rules.

Keep patterns honest

A pattern will match more than the example that motivated it. Before you commit a rule:

  • prefer an exact command or path over a *;
  • keep explicit denies for high-risk actions rather than relying on the absence of an allow;
  • test against a non-destructive operation first, then confirm with cmdop permissions audit that the rule you expected is the rule that matched.

Common questions

How do I write a Cmdop permission rule?

Name the tool, then optionally narrow its argument with tool(argGlob) or tool(operation:argGlob). Quote the rule so your shell passes spaces and metacharacters to Cmdop unchanged.

Which permission rule wins?

deny wins over ask, and ask wins over allow. Rule precedence is not the order you added rules in, so a broad allow cannot reopen something an explicit deny covers.

How narrow should an allow rule be?

As narrow as the tested task allows. Prefer exact commands and paths over broad globs, add a reason, then confirm the matched rule with cmdop permissions audit.

Last updated on