Policy reference
Every field of the policy format, every rule type and every match operator.
version: 1
default: block
rules:
- id: only-public-repo
match: { tool: "github_*", args: { repo: { notGlob: "acme/public-app" } } }
decision: block
reason: Only acme/public-app is in scope
- id: hold-comments
match: { tool: github_post_comment }
decision: hold
approvers: { roles: [approver, admin] }
timeoutSeconds: 300
- id: interns-read-only
match: { tool: "github_*", user: { group: intern } }
except: { tool: [github_list_issues, github_get_issue] }
decision: block
- id: allow-in-plan
match: { inPlan: true }
decision: allowTop level
Section titled “Top level”| Field | Value |
|---|---|
version |
Always 1. |
default |
Always block. Applies when no rule matches (no_matching_rule). |
rules |
An ordered list. The first rule that matches decides. |
Unknown fields are errors, so a typo never silently does nothing.
Every rule has:
| Field | Required | Meaning |
|---|---|---|
id |
yes | Kebab-case (hold-comments), unique in the policy. Recorded with every decision it makes. |
match |
yes | Which calls the rule applies to. All given fields must match. |
except |
no | Same shape as match. Calls that match it are excluded from the rule. |
decision |
yes | allow, block or hold. |
reason |
no | Shown to the model and in the inbox when the rule blocks. |
- id: allow-in-plan
match: { inPlan: true }
decision: allowRuns the tool. Policies are checked after the plan, so an allow rule can never let an off-plan call through. Keep allow-in-plan last: rules after it never apply to calls that are in the plan.
- id: no-prs
match: { tool: [github_create_pull_request, github_merge_pull_request] }
decision: block
reason: This agent never opens or merges pull requestsStops the call. The model receives the rule id and reason as an error result.
- id: hold-large-refunds
match: { tool: issue_refund, args: { amount: { gte: 100 } } }
decision: hold
approvers: { roles: [admin], emails: [finance@acme.dev] }
timeoutSeconds: 900Pauses the call until someone approves or rejects it. See Holds.
| Field | Default | Meaning |
|---|---|---|
approvers.roles |
[] |
Anyone with one of these roles (admin, approver, member). |
approvers.emails |
[] |
These people. |
approvers.self |
false |
The person the agent acts for may approve their own hold. |
timeoutSeconds |
300 |
1 to 86,400. The hold expires after this. |
At least one of roles, emails or self: true is required, so every hold can be approved by someone.
Match fields
Section titled “Match fields”| Field | Matches when |
|---|---|
tool |
The tool name matches a glob ("github_*") or one of a list of globs. |
args |
Every listed argument satisfies its condition. |
user.email |
The person’s email matches a glob. Case-insensitive. |
user.role |
The person has this role. |
user.group |
The person is in this group. |
inPlan |
The call is (or, with false, is not) in the signed plan. |
Roles and groups come from the team on the server and are snapshotted into the token when the plan is signed.
Argument conditions
Section titled “Argument conditions”| Operator | Matches when the argument |
|---|---|
eq |
equals the value exactly. |
in |
equals one of the values. |
notIn |
does not equal any of the values, or is missing. |
glob |
is a string matching the glob. |
notGlob |
is not a string matching the glob, or is missing. |
regex |
is a string the regular expression matches in full. |
lte |
is a number less than or equal to the value. |
gte |
is a number greater than or equal to the value. |
Details:
eqandincompare JSON values exactly, ignoring key order."20"is not equal to20.glob:*does not cross/, dotfiles are matched, and a leading!is a literal character, not a negation.regexmust match the whole string; it is wrapped in^(?:…)$.- Positive operators (
eq,in,glob,regex,lte,gte) match only when the argument is present and has the right type. notInandnotGlobare exact negations, so they also match a missing or wrong-typed argument. In ablockrule that is the safe outcome:only-public-repoabove also blocks GitHub tools that have norepoargument. Keep this in mind if you use them in anallowrule.
Plan steps use the positive operators only.
Checking a policy
Section titled “Checking a policy”intentlock policy validate policy.yaml
intentlock policy test --agent github-assistant policy.yaml --replay 50validate checks the syntax offline. test runs a dry run on the server against sample calls or the agent’s last decisions, and shows which ones would change. The inbox editor does both as you type.