agentjail policies are written in Rego, the same language used by Open Policy Agent. Each policy file contributes candidate verdict entries that are resolved to a single decision by a central resolver.
The shape of a tool call
Every evaluation receives a structured input describing the call the agent
wants to make:
{
"hook_event": "PreToolUse",
"tool_name": "Bash",
"tool_input": { "command": "rm -rf ~/.ssh/" },
"session_id": "abc123",
"cwd": "/home/user/project"
}
Note: The hook bridge sends
hook_event_namein its stdin payload; the daemon maps this tohook_eventin the OPA input document. Useinput.hook_eventin your Rego rules.
Candidate → resolver → decision
Every policy file (core, library, and custom) contributes entries to a shared
partial rule set called candidate. A file in resolver.rego is the sole
producer of data.agentjail.decision: it reads all candidates, builds an
effective_candidate set (filtering out any disabled_rules), and picks the
most restrictive:
deny > ask > allow
Within the same priority tier, the candidate with the lexicographically smallest
rule_id wins, making evaluation deterministic for cache hits and audit replay.
When no candidate fires at all, resolver.rego returns a safe default of
ask (fail-safe, not silent allow).
A rule that fires
A candidate rule matches when its body holds. Each candidate entry is an
object with action, rule_id, and reason:
package agentjail
import future.keywords.if
import future.keywords.contains
candidate contains r if {
input.tool_name == "Bash"
contains(input.tool_input.command, "/.ssh/")
r := {
"action": "deny",
"rule_id": "command_policy/no-ssh-access",
"reason": "Blocked: command targets sensitive path ~/.ssh/",
"impact": "would expose SSH private keys",
}
}
Breaking that down:
package agentjail: all hook-path rules must use this package.candidate contains r if { ... }: the partial rule entry pattern. Never declaredecision = ...in your own files;resolver.regoowns that.ris an object with a mandatoryaction,rule_id, andreason(plus an optionalimpact).- The body is a conjunction: every line must hold for the candidate to be added.
Three-way verdicts
The resolver produces one of three verdicts:
| Verdict | Meaning |
|---|---|
allow | Tool call proceeds immediately. |
ask | The agent is prompted to confirm before continuing. |
deny | Tool call is blocked; the agent receives a rejection message. |
The fail-safe default (no candidates fire) is ask, not allow. This
means an unknown or unexpected tool call escalates to the user rather than
silently proceeding.
Rule IDs and namespacing
Every rule carries a namespaced rule_id:
| Prefix | Used by |
|---|---|
file_policy/… | Built-in file path rules |
command_policy/… | Built-in shell command rules |
mcp_policy/… | Built-in MCP server rules |
library/… | Opt-in library rules |
custom/<stem>/… | Your own rules (enforced by agentjail policy add) |
The custom/<stem>/<rule> namespace is reserved for user-authored rules
installed via agentjail policy add. For example, a file named my_rule.rego
must emit only IDs like custom/my_rule/no-something.
Disabled rules
Any rule except the locked self-protection set can be suppressed by adding its
rule_id to disabled_rules in ~/.agentjail/policy.yaml, or by running
agentjail policy disable <rule_id>. The resolver drops disabled candidates
before resolving the verdict. See Configuration
for the full schema.
Config overlay
The daemon loads ~/.agentjail/policy.yaml, merges it over built-in defaults,
and injects it into OPA as data.agentjail.config. Rules read allowlists,
blocklists, and tuning values from there. The config is re-injected on SIGHUP
(the daemon hot-reloads without restart), and the decision cache is invalidated
on every reload.
Evaluation semantics
- Rules are evaluated locally: there is no network call at decision time.
resolver.regocollects all candidate entries from all loaded policy files and picks the most restrictive.- The default when nothing fires is
ask(fail-safe). - Policies are plain text you can read, diff, and version-control alongside the rest of your project.
See Installation to get a working policy bundle on your machine, and Writing your first rule to author your own candidate rules.