# Managed rule groups

> Every published managed rule group, its rules and default actions, and how to reference, pin and override them.

A managed rule group is a named, versioned list of rules. Reference it in a
rule set instead of copying its rules:

```json
{"group": "agentgate-core", "version": 1, "overrides": {"agent_class": "count"}}
```

- The `version` is required. Unknown groups, versions and override targets
  are rejected.
- Published versions never change (a digest test pins them), so moving to
  a new version is an explicit edit.
- `overrides` changes single rules' actions, for example to `count` while
  you evaluate them. `scope_down` on the reference is ANDed into every rule
  of the group.
- Rule names, and so the `agentgate:rule:<name>` labels, are the same as
  when the rules are written out.
- `GET /v1/console/managed-groups` (operators: `/v1/admin/managed-groups`) lists every
  group with its expanded rules. `GET …/rules` returns the references as
  written.

The built-in default rule set is `agentgate-core@1`, `agentgate-agents@1`
and `agentgate-gateway@1`, in that order. `agentgate-bot-control@1` and
`agentgate-anonymity@1` are opt-in.

## agentgate-core@1

The browser-check rules, evaluated on `/submit` (the form and every SDK
verification), plus the per-agent policy rules, which apply on every route
and stay dormant until a site configures an [agent policy](/docs/agent-policy).

| Rule | Action | Matches |
| --- | --- | --- |
| `honeypot` | drop | a hidden honeypot field was filled |
| `missing_proof` | block 403 | no proof of work |
| `invalid_proof` | block 403 | a proof that does not verify |
| `agent_policy_unverified_block` | block 403 | unverified AI claim, policy `block` |
| `agent_policy_unverified_charge` | block 402 | unverified AI claim, policy `charge` |
| `agent_policy_unverified_challenge` | challenge | unverified AI claim, policy `challenge` |
| `agent_policy_denied` | block 403 | verified agent not permitted this action |
| `agent_policy_quota` | block 429 | over the agent's per-site quota |
| `agent_policy_payment_invalid` | block 400 | malformed, conflicting or unsigned price offer |
| `agent_policy_payment_required` | block 402 | priced action without an acceptable offer |
| `agent_policy_allowed` | allow | verified agent permitted this action |
| `instrument_invalid` | block 403 | a wrong instrumentation answer |
| `instrument_missing` | challenge | no instrumentation answer |
| `instrument_slow` | challenge | the instrumentation answer took too long |
| `known_pattern` | block | the content matches a known spam pattern |
| `ip_soft_limit` | challenge | the address is over its soft rate budget |
| `model_threshold` | block | the content model score is high |
| `sdk_late_init` | challenge | `execute()` ran without `init()` |
| `agent_class` | challenge | the behaviour model classifies the visit as an agent or a bot |
| `env_watch` | count | any browser environment finding (for the audit) |
| `env_automation` | challenge | definitive automation markers, such as `navigator.webdriver` or headless |
| `env_cdp_low_pointer` | challenge | a DevTools-protocol client and almost no pointer movement |
| `env_cdp_stealth` | challenge | a DevTools-protocol client with patched browser functions |
| `env_agent_dom` | challenge | DOM markers left by known browser agents |
| `pat_attested_skip_check` | count | a Private Access Token attested the device |
| `tls_non_browser` | challenge | a browser User-Agent over a non-browser TLS fingerprint |
| `challenge_required` | challenge | the bot-risk score is high |
| `rate_limited` | block 429 | session or global rate budget exceeded |

Challenges in this group are skipped for a session that has passed the
visible check (label `agentgate:session:passed`).

## agentgate-agents@1

Web Bot Auth policy for the agent API, `/agent/submit`.

| Rule | Action | Matches |
| --- | --- | --- |
| `agent_signature_invalid` | block 401 | a bad, expired or replayed signature |
| `agent_authenticated` | allow | a verified agent (`agentgate:agent:verified:*`) or a valid shared key |
| `agent_unknown_key` | block 401 | the key is not known for its origin |
| `agent_unauthenticated` | block 401 | no signature, or a wrong shared key |

## agentgate-gateway@1

Authorization at `/v1/gateway/check`.

| Rule | Action | Gateway reason |
| --- | --- | --- |
| `gateway_agent_signature_invalid` | block | `agent_signature_invalid` (includes a body digest mismatch) |
| `gateway_rate_limited` | block | `rate_limited` |
| `gateway_receipt_rejected` | challenge | the token's code: `invalid_token`, `expired_token`, `token_used`, `action_mismatch`, `origin_mismatch` |
| `gateway_browser_evidence_required` | challenge | `receipt_required` (or the clearance's code) on a browser route with neither a valid token nor clearance |

## agentgate-bot-control@1

User-Agent and crawler identity checks, from the bot catalogue and
[threat intelligence](/docs/threat-intelligence).

| Rule | Action | Matches |
| --- | --- | --- |
| `bot_ua_mismatch` | block 403 | the User-Agent claims one catalogued bot while a signature proves another |
| `bot_ip_mismatch` | block 403 | a crawler User-Agent from outside its operator's published ranges |
| `bot_http_library` | block (`/submit`) | an HTTP library User-Agent (curl, python-requests, …) |
| `bot_headless_browser` | challenge (`/submit`) | a headless browser User-Agent, unless verified |
| `bot_automation_flag` | challenge (`/submit`) | the automation flag signal |
| `bot_unverified_ai` | challenge (`/submit`) | an AI assistant or crawler User-Agent that nothing verifies |

## agentgate-anonymity@1

| Rule | Action | Matches |
| --- | --- | --- |
| `anonymity_tor` | challenge (`/submit`) | a current Tor exit |
| `anonymity_datacenter` | count (`/submit`) | an address in a cloud provider's ranges, unless a verified bot |
| `ip_reputation_high` | challenge (`/submit`) | a high IP reputation score from recent decisions |

Datacenter addresses are normal for agents and crawlers, so
`anonymity_datacenter` counts rather than blocks; override its action if
your site should treat them differently.
