Reference
Policy language
Policies are YAML files of rules: a condition, an effect, a reason. This page lists every field, every condition, the four shipped packs rule by rule, and how to write, check, load and roll out your own.
When to read this
When a decision names a rule and you want to know what it tests; when you want a rule the shipped packs do not have (an export cap, a recipient domain, a stricter threshold for one team); or before you promote a pack from observe to enforce. For threshold ladders written in plain terms, use business rules instead; they compile to the same engine.
The document
| Field | Values | Default | Meaning |
|---|---|---|---|
key | text | required | Unique id. A project pack with the key of a shipped pack replaces it. |
name, description | text | "" | For people. |
version | integer | 1 | The first stored version number. Saving a changed body stores a new immutable version; decisions record the version in force. |
mode | observe | enforce | observe | The mode the pack is bound in when first loaded. Afterwards the binding decides (agentfox policy enforce / observe). |
default_effect | an effect | allow | The verdict when no rule fires. |
fail_mode | open | closed | open | What a detector timeout or error does to this pack's checks: see fail_mode. |
scope | {agents: [globs], environments: [names]} | {} | Which agents and environments the pack applies to at runtime. Empty means all. |
rules | list | [] | The rules. |
A rule
| Field | Default | Meaning |
|---|---|---|
id | required | Unique within the pack, dotted by family: billing.large_export. Decisions, findings and the explanation name it. |
description | "" | For authors. |
when | matches everything | A condition. Every field present must match; absent fields are ignored. |
effect | block | See effects. |
reason | generated | Shown to the caller and written to the audit log. Write it for a person. When empty, one is generated from what matched. |
severity | medium | critical, high, medium or low. Becomes the finding's severity. |
controls | [] | Control ids this rule is evidence for, in the compliance catalog. |
enabled | true | A disabled rule never fires (and lint says so). |
redaction | mask | The style used when the effect is redact, mask or tokenize. |
overridable | false | Whether a narrower level of the hierarchy may weaken this rule. |
Effects
When several rules fire, across all bound packs, the strongest effect wins:
allow < tokenize < mask < redact < abstain < escalate < block| Effect | What happens when enforced |
|---|---|
allow | Nothing. A rule that allows cannot cancel a stronger rule elsewhere. |
tokenize, mask, redact | The matched spans are replaced and the call goes on with the rewritten content. |
abstain | The answer is withheld without treating the user as an attacker. |
escalate | An approval is created and the call waits for a person (ApprovalRequired, HTTP 202). |
block | Refused (PolicyViolation, agentfox.Blocked, HTTP 403). |
In observe a pack still evaluates and records what it would have done: the decision's verdict is allow and its effective_verdict is the strongest effect that fired. That difference is what agentfox findings reports as "would have been blocked".
Conditions (when)
| Key | Type | Matches when |
|---|---|---|
surface | list | The content is on one of these surfaces: input, output, tool_args, tool_result, retrieved, memory_write, agent_message, completion, reasoning. See surfaces. |
environment | list | The call's environment is listed. |
agent | glob | The agent slug matches, e.g. support-*. |
risk_tier | list | The agent's risk tier is listed: minimal, limited, high, prohibited. |
tool | glob | The tool key matches, e.g. billing.* or mcp:helpdesk/*. |
tool_impact | list | The tool's declared impact is listed: read, write, high_impact, irreversible. |
tool_known | bool | false matches a tool the registry has never seen. |
detection | object | At least min_count (default 1) detections scored at or above min_score (default 0.5) whose entity equals entity (e.g. PII.CREDIT_CARD) or starts with entity_prefix (e.g. INJECTION). Entity types are listed in Detectors. |
argument | object | The tool argument at path (dotted, with [0] for list items) compared with value by op: eq, ne, gt, gte, lt, lte, in, not_in, contains (case-insensitive substring), matches (regular-expression search). A missing argument never matches. |
taint_exceeds | level | The worst provenance of the call's content or arguments is above this level, in the order none < user < retrieved < tool_result < subagent < memory. |
capability | state | The grant check came out denied (no grant), constraint_violated (a grant, but an argument outside its limit), requires_approval or granted. |
action_operation | list | What an SQL, shell or HTTP argument does: read, write, destructive, admin, unknown. |
blast_radius_at_least | level | The estimated blast radius is at least this, in the order none < bounded < unknown < unbounded < catastrophic. |
action_reversible | bool | The analysed action is (or is not) reversible. |
action_risk | glob | Any risk code found in the call matches, e.g. sql.*, shell.destructive, remote-code-execution, unscoped-table, control_flow.*, crescendo.trajectory_drift. Codes appear in a decision's explanation. |
budget_exceeded | bool | The agent's call, token, spend or depth budget is exhausted. |
loop_detected | bool | The loop governor saw a repeating call with no progress. |
intent_declared | bool | The caller did (or did not) declare a task intent. |
detector_degraded | bool | A detector timed out, errored or was skipped for budget on this call. |
completion_requires | list | On the completion surface: any named fact was not reported as true by the caller. Scope the rule with surface: [completion]. |
expr | text | An escape hatch: a Python expression over agent, surface, tool, impact, risk_tier, environment, n_detections, max_score, prior_tool_count, taint, with no builtins. Lint cannot reason about it and it does not compile to Rego meaningfully; avoid it. |
Refusals that are not policy rules
Some refusals happen whatever mode your packs are in, because they are facts about the call rather than opinions: a capability check with no grant (default deny once the agent holds any grant, and always in the SDK and gateway), a critical action risk such as shell.destructive or sql.unbounded_mutation, an SQL argument that cannot be analysed because the [sql] extra is missing (analysis.unavailable, which fails closed), composed privilege escalation, and MCP schema drift. With tool-containment demoted to observe, both of these still block:
db.query PolicyViolation [('analysis.unavailable', 'enforce')]
shell.run PolicyViolation [('shell.destructive', 'enforce')]With sqlglot installed (pip install "agentfox[sql]") the same DELETE FROM tickets is analysed and refused as sql.unbounded_mutation instead.
The shipped packs
agentfox init loads baseline, tool-containment and eu-ai-act-high-risk. coding-agent is loaded only for agents with coding-agent hooks installed (agentfox admin hooks install, see Coding agents).
agentfox policy packs
agentfox policy listpack origin mode rules file
baseline shipped observe 13 …/agentfox/packs/baseline/policies/baseline.yaml
coding-agent shipped observe 5 …/agentfox/packs/coding-agent/policies/coding-agent.yaml
eu-ai-act-high-risk shipped observe 7 …/agentfox/packs/eu-ai-act/policies/eu-ai-act-high-risk.yaml
tool-containment shipped enforce 25 …/agentfox/packs/tool-containment/policies/tool-containment.yaml
policy version mode rules
baseline v1 observe 13
eu-ai-act-high-risk v1 observe 7
tool-containment v1 enforce 25Capability packs
Each shipped policy file lives in a capability pack: a directory with a pack.yaml (id, version, maturity, owners, compliance mappings, vocabulary) and the policies, controls, business-ladder templates, red-team probes, golden cases and fixtures one use case needs. Besides these four, AgentFox ships eu-ai-act (which carries eu-ai-act-high-risk and the risk classes), compliance/catalog (the control catalog), payments/refunds and customer-support. Your own go in .agentfox/packs/; only stable packs load unless pack_maturity says otherwise.
agentfox policy packs list
agentfox policy packs show payments/refunds
agentfox policy packs test
agentfox policy packs new payments/chargebacks
agentfox policy packs validate .agentfox/packs/payments/chargebacksbaseline: content guardrails (observe)
Detection-driven rules on every surface. Ships in observe: read what it would have done, then promote it.
| Rule | Effect | Severity | What it does |
|---|---|---|---|
injection.direct | block | high | Injection or jailbreak in the user's input, score 0.7 or more. |
injection.indirect | block | critical | Injection in retrieved content or a tool result, at the lower bar of 0.6: an instruction has no business being in data. |
injection.system_prompt_leak | block | medium | An attempt to extract the system prompt, on any surface. |
injection.memory_and_agent_message | block | critical | Injection in a memory write or a message from another agent. |
injection.adopted_in_reasoning | block | critical | Instruction-like text in the model's own reasoning, at 0.4: evidence the model took the payload up. |
secrets.block | block | critical | A credential anywhere, in either direction, at 0.9. |
pii.outbound_redact | redact (mask) | medium | Personal data in a response is masked before it is delivered. |
pii.inbound_tokenize | tokenize | medium | Personal data in a prompt is replaced with tokens before it reaches the model. |
pii.high_sensitivity | block | critical | A US Social Security number, anywhere. |
pii.memory_and_agent_message | redact (mask) | medium | Personal data in a memory write or an inter-agent message is masked. |
safety.harm | block | high | Unsafe content (harm, self-harm, illicit, harassment, extremism). |
schema.violation | block | medium | A response that does not match the output schema it was declared with. |
pipeline.degraded_high_risk | escalate | medium | A high-risk or prohibited-tier agent served while detectors were degraded. |
tool-containment: what an agent may do (enforce)
Rules about the action and where its arguments came from, which hold when a detector misses. Enforces from agentfox init, and the pack cannot be loaded with control_plane.tamper removed or disabled.
| Rule | Effect | Severity | What it does |
|---|---|---|---|
taint.irreversible_tool | escalate | critical | An irreversible tool whose arguments came from anything other than the user. |
taint.high_impact_tool | escalate | high | The same for a high-impact tool. |
taint.write_from_tool_result | escalate | medium | A write whose arguments were copied out of a tool result, a sub-agent or memory. |
capability.denied | block | high | No grant for this tool: default deny. The reason names the command that fixes it. |
capability.constraint_violated | block | high | A grant exists, but an argument is outside a limit it declares. |
capability.approval_required | escalate | medium | The grant says a person signs off, or an argument's provenance is worse than the grant allows. |
intent.undeclared_irreversible | escalate | medium | An irreversible tool call with no declared task. |
budget.exceeded | block | medium | The agent ran out of calls, tokens, spend or recursion depth. |
loop.runaway | block | medium | The agent is repeating the same tool call without progress. |
completion.unverified_claim | escalate | high | The agent says it is finished without reporting that the work was verified. |
completion.irreversible_unconfirmed | block | critical | A run with a destructive or admin action ends without confirmation that it succeeded. |
tool.not_declared | escalate | high | A tool the registry has never seen: invented by the model, or real and undeclared. |
action.remote_code_execution | block | critical | A download piped straight into a shell or interpreter. |
secrets.credential_file | block | high | A command touching .env, an SSH key, cloud credentials, a kubeconfig or a service-account file. |
action.supply_chain_publish | escalate | high | Publishing a package or image. |
action.infrastructure_mutation | escalate | high | terraform, kubectl, helm or a cloud CLI changing live infrastructure. |
action.history_rewrite | escalate | medium | A hard reset, force clean or dropped stash. |
control_plane.tamper | block | critical | A command that would switch AgentFox's own enforcement off. The pack refuses to load without it. |
cascade.reaches_destructive | block | critical | A harmless-looking call that reaches a destructive tool through its declared triggers. |
cascade.reaches_notification | escalate | high | A call whose declared triggers reach only a message that cannot be recalled (a tool declared effect: communication), and nothing more destructive. |
cascade.cycle | block | critical | The declared trigger graph loops. |
cascade.blast_radius | escalate | high | The declared trigger graph is unusually deep or wide. |
access.unscoped_table | block | critical | A query on a table with per-customer rows and no predicate binding it to the caller. |
access.undeclared_table | escalate | high | A query on a table with no scope declaration. |
injection.in_tool_arguments | block | critical | Injection inside tool arguments (see the note under the table). |
secrets.in_tool_arguments | block | critical | A credential inside tool arguments. |
eu-ai-act-high-risk (observe)
Measures for agents classified high (and a hard stop for prohibited). They only fire for agents whose risk tier says so. The article mappings are drafts, not legal advice.
| Rule | Effect | Severity | What it does |
|---|---|---|---|
eu.art14.human_oversight | escalate | high | Any irreversible tool call by a high-risk agent goes to a person. |
eu.art14.no_covert_action | block | critical | Content telling a high-risk agent to act without informing the user. |
eu.art15.no_degraded_enforcement | block | high | A high-risk agent served while detectors were degraded. |
eu.art15.injection_resistance | block | critical | Injection against a high-risk agent, at 0.5. |
eu.art10.special_category_data | block | critical | Personal data in a high-risk agent's prompt. |
eu.art50.impersonation | escalate | medium | Review of an answer that claims to be a person ("I'm a real person", "I am not a bot"): the disclosure.claims_human risk. Lexical, so it escalates rather than blocks. |
eu.art5.prohibited_tier | block | critical | An agent classified prohibited is refused on every call. |
coding-agent (observe)
Lower thresholds for an agent that reads diffs and runs shell commands on a developer's machine.
| Rule | Effect | Severity | What it does |
|---|---|---|---|
code.injection_in_fetched_content | block | critical | Injection in a tool result or fetched content, at 0.5 rather than 0.6. |
code.secret_in_fetched_content | block | high | A credential coming back from a tool, at 0.7: once it is in context it can leak later. |
code.injection_adopted | escalate | critical | A directive in the model's reasoning, at 0.3. |
code.injection_in_operator_turn | escalate | medium | Injection in the operator's own turn, usually a pasted log or issue. |
code.pii_out_of_a_developer_machine | escalate | high | Personal data in a tool call's arguments from a coding session. |
Write and load your own
Put a YAML file in .agentfox/policies/ at the root of your repository. agentfox init (safe to run again) loads it alongside the shipped packs, and it travels with the code through review like anything else. A file whose key equals a shipped pack's replaces that pack.
key: support-desk
name: Support desk rules
description: Rules for the agents that answer customer tickets.
version: 1
mode: enforce
fail_mode: open
scope:
agents: ["support-*"]
rules:
- id: email.outside_domain
description: Email to an address outside example.com needs a person.
when:
tool: email.send
argument: {path: to, op: matches, value: "@(?!example\\.com$)"}
effect: escalate
severity: high
reason: "Email to a recipient outside example.com; a person must approve it."
- id: billing.large_export
description: Exports over 10,000 rows are refused.
when:
tool: "billing.*"
argument: {path: rows, op: gt, value: 10000}
effect: block
severity: high
reason: "Billing exports are capped at 10,000 rows."
- id: crm.write_from_untrusted
description: A CRM write whose arguments came from a web page or a tool result.
when:
tool: "crm.*"
tool_impact: [write, high_impact, irreversible]
taint_exceeds: user
effect: escalate
severity: high
reason: "CRM write carries data the user did not type."
- id: output.card_number
description: A card number never goes back to a customer.
when:
surface: [output]
detection: {entity: PII.CREDIT_CARD, min_score: 0.9}
effect: block
severity: critical
reason: "Card number in a reply to a customer."Check it, load it
agentfox policy validate .agentfox/policies/support-desk.yaml
agentfox init
agentfox policy lintvalid — support-desk v1, 4 rules, mode=enforce
controls: []
compiles to 73 lines of Rego
…
✓ 4 policy pack(s) loaded
baseline observe recorded, nothing blocked
eu-ai-act-high-risk observe recorded, nothing blocked
support-desk enforce violations are blocked now
tool-containment enforce violations are blocked now
…
no policy issuespolicy validate FILE checks one file offline: the schema, the effects and operators, that it compiles, and the full lint. policy lint FILE… lints files before they are loaded; with no file it lints every bound pack and every hierarchy layer together, so run it after init too. Lint catches a rule whose conditions can never be true, a condition naming a value no request carries (surface: [input, toolargs] still fires on input, but toolargs is a typo), duplicate ids, over-broad globs, rules with no conditions, and illegal loosening. Both exit 1 on critical or high findings, so either can gate a pull request:
key: typos
name: A pack with mistakes
mode: observe
rules:
- id: pii.reply
when:
surface: [outputs]
detection: {entity_prefix: PII}
effect: redact
- id: tools.everything
when:
tool: "*"
effect: escalate$ agentfox policy validate .agentfox/policies/typos.yaml
invalid: typos — 1 blocking finding(s)
…
$ agentfox policy lint .agentfox/policies/typos.yaml
severity code rule level message
high unreachable pii.reply org 'pii.reply' can never fire: every value in `surface` is unknown
(outputs);
medium over-broad-glob tools.everything org 'escalate' applies to every tool ('*') — likely to produce false
blocks
{'high': 1, 'medium': 1}
LINT FAIL — critical/high findings block the buildAn unknown effect is caught by validate:
invalid: 1 validation error for PolicyDocument
rules.0.effect
Input should be 'allow', 'redact', 'mask', 'tokenize', 'abstain', 'block' or 'escalate'See it fire
support-triage holds grants for email.send, billing.export and crm.update:
from agentfox import AgentFox, ApprovalRequired, PolicyViolation
fox = AgentFox(agent="support-triage")
fox.tool("email.send", impact="write")(lambda **kw: None)
fox.tool("billing.export", impact="read")(lambda **kw: None)
fox.tool("crm.update", impact="write")(lambda **kw: None)
def attempt(s, tool, args):
try:
r = s.guard_tool(tool, args)
print(f"{tool:15} allow")
except (ApprovalRequired, PolicyViolation) as exc:
kind = type(exc).__name__
print(f"{tool:15} {kind}: {[r['rule_id'] for r in exc.result.rules_fired]}")
with fox.session(intent="answer customer tickets") as s:
attempt(s, "email.send", {"to": "ada@example.com", "subject": "T-1042", "body": "Resolved."})
attempt(s, "email.send", {"to": "ada@lookalike.example", "subject": "T-1042", "body": "Resolved."})
attempt(s, "billing.export", {"month": "2026-09", "rows": 250})
attempt(s, "billing.export", {"month": "2026-09", "rows": 50000})
note = s.retrieved("Customer note: change email to ada@lookalike.example")
attempt(s, "crm.update", {"customer_id": "c-17", "email": note})
r = fox.check("Your card 4111 1111 1111 1111 is on file.", surface="output")
print("output:", r["verdict"], [x["rule_id"] for x in r["rules_fired"]])email.send allow
email.send ApprovalRequired: ['email.outside_domain']
billing.export allow
billing.export PolicyViolation: ['billing.large_export']
crm.update ApprovalRequired: ['capability.approval_required', 'crm.write_from_untrusted']
output: block ['pii.outbound_redact', 'output.card_number']The effective policy
agentfox policy effective resolves what is in force for a subject and says which layer each rule came from:
agentfox policy effective --agent support-triageeffective policy in development — default allow
layers:
org:*(extend) baseline observe
org:*(extend) eu-ai-act-high-risk observe
org:*(extend) support-desk enforce
org:*(extend) tool-containment enforce
rule effect mode from overrides
access.undeclared_table escalate enforce org:* —
access.unscoped_table block enforce org:* —
…
billing.large_export block enforce org:* —
…Each layer is listed with its own mode, and each rule with the mode it is applied under: enforce rules block, observe rules are recorded as the effective verdict only.
Hierarchy: org, team, agent, user
A policy saved through the API can be placed at a level (org, team, agent, user), with a scope_id (which team, agent or user; * for all) and a compose mode:
compose | Meaning |
|---|---|
extend (default) | Add rules; the broader levels' rules still apply. |
restrict | Tighten only. A rule with the same id and a weaker effect is rejected. |
override | May weaken a rule, but only one the broader level marked overridable: true. |
Packs loaded by agentfox init are bound at org:* with extend. To place one elsewhere, post it to POST /api/policies with level, scope_id and compose. Here a finance team tightens PII redaction to a block, and an agent layer tries to switch off injection.direct:
import httpx
B = "http://127.0.0.1:18732"
for f, level, scope, compose in [("finance-team.yaml", "team", "finance", "restrict"),
("loosen.yaml", "agent", "payments-ops", "override")]:
r = httpx.post(f"{B}/api/policies", json={"body": open(f).read(), "level": level,
"scope_id": scope, "compose": compose, "notes": "docs example"})
print(r.status_code, r.json())201 {'key': 'finance-team', 'version': 1, 'version_id': 'pvr_01m469r2pmpz4qds25'}
201 {'key': 'payments-ops-exceptions', 'version': 1, 'version_id': 'pvr_01m469r2q1s0kh1bq6'}agentfox policy effective --agent payments-ops --team finance
agentfox policy linteffective policy in development — default allow
layers:
org:*(extend) baseline observe
…
team:finance(restrict) finance-team observe
agent:payments-ops(override) payments-ops-exceptions observe
…
injection.direct block observe org:* —
pii.outbound_redact block observe team:finance org:*
…
rejected layer rules
injection.direct at agent:payments-ops — cannot loosen 'block' (from org) to 'allow' — the upstream rule is not marked
overridabl
severity code rule level message
critical illegal-loosening injection.direct agent weakens 'block' from org:* to 'allow' without an 'overridable:
true' grant
{'critical': 1}
LINT FAIL — critical/high findings block the buildWhat the runtime enforces
The enforcer resolves the hierarchy the same way policy effective does, for each request:
- A layer applies only to the subject its level and
scope_idname. An agent's team is itsowner_team(set withPATCH /api/agents/{slug}or when registering it); an agent with no team gets onlyteamlayers scoped to*. In the example above,team:financedoes not apply tosupport-triage. - A rule rejected as an illegal loosening is not enforced; the broader rule stands. A granted
overridereplaces the broader rule, so anallowthere really loosens it. - A rule that a narrower layer tightened stays in force beside the tighter one, each under its own pack's mode. A team that trials a stricter rule in observe does not switch off the org's enforced rule.
userlayers scoped to a specific user apply only where the caller identifies the user, which the runtime does not do today; auserlayer scoped to*applies to everyone.
fail_mode and the enforcement budget
What happens when a detector times out or errors is decided by two settings, and the stricter wins: the deployment-wide fail_mode (AGENTFOX_FAIL_MODE, or fail_mode in agentfox.toml) and each pack's own fail_mode. With open (the default) the call proceeds and the gap is recorded. A degraded call is blocked as pipeline.fail_closed when the deployment says closed and the decision is enforcing, or when a pack that is bound in enforce says closed and has an enabled detection rule on the surface being checked (its coverage depended on the detectors that did not finish). tool-containment and eu-ai-act-high-risk ship with closed. Details and an example are in Detectors: budget and failure.
Roll out a change
Simulate against recorded traffic
agentfox policy simulate --file replays recorded decisions against a candidate and exits 1 if anything would newly block. Here the export cap is lowered from 10,000 rows to 100:
agentfox policy simulate --file candidate.yamlsupport-desk simulated against 6 decisions
unchanged 5
newly blocked 1
newly escalated 0
newly allowed 0
would block support-triage tool_args billing.export — Billing exports are capped at 100 rows.
This change would block production traffic. Review before promoting to enforce.Options: --agent, --since-days (default 30), --limit (default 1000).
Promote and demote
agentfox policy enforce baseline
agentfox policy observe baselinebaseline → enforce
baseline → observeBoth are audited. agentfox.auto() in its default mode follows the change with no code change.
Canary
A new version can take a percentage of traffic before it takes all of it. Canaries are run over the API (there is no CLI command): POST /api/policies/{key}/canary/start with optional steps (default [10, 25, 50, 100]), max_block_rate_delta (0.15), max_block_rate_drop, min_dwell_seconds and min_sample (20); /canary/advance checks health and advances, holds, or rolls back when the candidate blocks more, or less, than stable by more than the threshold; /canary/rollback stops it.
201 {
"id": "cny_01m469sp29fkvhqj65",
"status": "rolling",
"percent": 10,
"step_index": 0,
"steps": [
10,
50,
100
],
"stable_version": 1,
"candidate_version": 2,
"max_block_rate_delta": 0.15,
"max_block_rate_drop": 0.15,
"min_dwell_seconds": 3600,
…
"gate": {
"action": "hold",
"reason": "waiting for 20 decisions in each cohort (stable 0, candidate 0)"
}
}Rego export and the OPA engine
Every pack compiles to a Rego module: GET /api/policies/{key}/rego, and policy validate reports the line count. The native engine is the default. Setting policy_engine = "opa" with opa_url evaluates through an OPA sidecar instead, and falls back to the native engine if the sidecar is unreachable. The OPA path was not exercised for this page.
# Generated by AgentFox from policy 'support-desk' v1.
# Do not edit — regenerate from the declarative source.
package agentfox.platform.policy.support_desk
import rego.v1
default verdict := "allow"
# Exports over 10,000 rows are refused.
fired contains out if {
glob.match("billing.*", [], input.tool)
input.arguments.rows > 10000
out := {
"rule_id": "billing.large_export",
"effect": "block",
…Commands
| You want to | Run |
|---|---|
| See which packs exist and where they came from | agentfox policy packs |
| See each bound pack's mode | agentfox policy list |
| Check one file | agentfox policy validate FILE |
| Lint everything bound (CI gate) | agentfox policy lint |
| What is in force for an agent | agentfox policy effective --agent support-triage |
| Replay traffic against a candidate | agentfox policy simulate --file candidate.yaml |
| Start or stop enforcing | agentfox policy enforce baseline |
| Draft grants from recorded calls | agentfox policy proposals from-traffic |
Troubleshooting
- My pack is not in
policy list - Files in
.agentfox/policies/are loaded byagentfox init, run from the repository root.policy packsshows what is on disk;policy listshows what is bound. - A rule never fires
- Run
agentfox policy lintfor unreachable conditions. Check the pack'sscope, the surface (each detector runs only on some surfaces), and thatmin_scoreis not above what the detector produces: the decision's explanation shows each match's score. - It fires but nothing is blocked
- The pack is in observe. Look at
effective_verdict, thenagentfox policy enforce KEY. tool-containmentwill not load- A replacement pack removed or disabled
control_plane.tamper. That is refused by design.
Limits
- Rules see what the detectors and the registry give them. A wrong impact declaration or a missed detection is not fixed by a better rule.
- The runtime does not know the end user, so
userlayers scoped to one user never apply at runtime (above). - The
completionsurface needs the caller to report facts; over HTTP there is no field for them, socompletion_requiresrules always see them as unmet there.