# Monitor and enforce

> Start every site in monitor mode, read the would-have decisions, then switch to enforce.

Every site has a mode. New sites start in **monitor**.

| | `monitor` | `enforce` |
| --- | --- | --- |
| Rule outcome | always allow | the rule set's decision |
| What would have happened | recorded as `wouldDecision` | the actual decision |
| Browser widget | gets a token (interactive widgets still show the check) | may be challenged or declined |
| `/v1/siteverify` | accepts every valid token; adds `wouldDecision` when it was not `allow` | accepts valid tokens |
| Gateway | forwards (`204`) with `X-AgentGate-Would-Decision` | answers `401` / `403` when the rules say so |

## What monitor mode does not relax

Monitor mode changes what the rules decide, not what is authenticated:

- a malformed, expired, spent or mismatched token is still rejected by
  `/v1/siteverify` (`invalid_token`, `expired_token`, `token_used`,
  `action_mismatch`, `origin_mismatch`);
- a missing or wrong backend secret is still `invalid_credential`;
- a page on an origin that is not allowed still gets `origin_denied`;
- an action that needs an authenticated agent is never allowed without one;
- replay protection is never skipped: if the replay store is unreachable
  the answer is `503` in both modes.

## What enforce mode stops today

With the built-in rule set (`agentgate-core@1`, `agentgate-agents@1` and
`agentgate-gateway@1`), enforce mode acts on:

- **Proof of work**: a submission without the browser proof, or with one
  that does not verify, is blocked (`missing_proof`, `invalid_proof`).
- **Instrument**: a submission that solved the proof but never ran the
  page's instrument program gets the visible check (`instrument_missing`);
  a wrong answer is challenged too (`instrument_invalid`; its default is a block, held to a challenge until more browsers are measured). The check has keyboard
  and no-interaction alternatives, so a person whose collector failed is
  not locked out.
- **Content**: a known spam pattern or a high content score is blocked
  (`known_pattern`, `model_threshold`).
- **Rate**: the session and global budgets answer `429` (`rate_limited`);
  an address over its soft budget gets the visible check (`ip_soft_limit`).
- **Agent signatures**: unsigned or wrongly signed agent requests, unknown
  keys and per-agent quotas (`agentgate-agents@1`), the gateway's
  signatures, rate budgets, receipts and required browser evidence
  (`agentgate-gateway@1`), and a site's [agent policy](/docs/agent-policy).
- **Bot risk**: a high bot-risk score gets the visible check
  (`challenge_required`).

Still count-only on the hosted instance (`deploy/rules.count-mode.json`,
`prod-1.1.0-count`), labelling decisions without acting until they are
measured on real people: the behaviour model (`agent_class`), the
environment detectors (`env_automation`, `env_cdp_low_pointer`,
`env_cdp_stealth`, `env_agent_dom`), the TLS fingerprint rule
(`tls_non_browser`), `sdk_late_init` and `instrument_slow`. `env_watch` and
`pat_attested_skip_check` count by design. The hosted rule set overrides the
groups' own default actions for those rules to `count`; the defaults are
listed under [Managed rule groups](/docs/managed-rule-groups).

## Reading the would-have decisions

The site's [analytics](/docs/health-and-analytics#analytics) count the
tokens enforce mode would have challenged (`wouldChallenge`) or blocked
(`wouldBlock`). To see why:

- The dashboard (console **Decisions**) lists every decision with its
  `wouldDecision`, labels and matched rule. Filter for decisions that would
  have been challenged or blocked and look at a sample.
- `/v1/siteverify` includes `"wouldDecision": "challenge"` or `"block"` in
  monitor mode, so your backend can log it next to your own request ID.
- Behind the gateway, `X-AgentGate-Would-Decision` and `X-AgentGate-Reason`
  are on every allowed answer.

Things to look for before you enforce: people wrongly challenged (for
example assistive technology, password managers or dictation), integrations
that skip `AgentGate.init()` (label `agentgate:sdk:late_init`), and origins
you forgot to allow.

## Switching to enforce

Change the mode in the console (site page) or with the admin API
(`PATCH /v1/admin/sites/{siteKey}` with `{"mode": "enforce"}`). The change
applies to the next request and is recorded in the site's change history.
You can switch back at any time.

> [!TIP]
> Single rules can stay in count mode after you enforce. Override a rule's
> action to `count` to keep measuring it without acting on it; see
> [Managed rule groups](/docs/managed-rule-groups).

## When AgentGate fails

In enforce mode AgentGate never decides blind: if a dependency it needs is
down, the answer is `503` with `Retry-After: 5` (fail closed). In monitor
mode a failing rule set or receipt verifier is recorded and the request is
allowed with reason `service_unavailable` (fail open). Two things hold in
every mode: a request whose token, site credential or agent signature is
invalid is refused, and replay protection is never skipped, so the answer
is also `503` when the site cannot be loaded or the replay store cannot be
reached. Nothing is consumed unrecorded: a token, agent ID or gateway
receipt is spent in the same transaction as its decision event, so a `503`
from `/v1/siteverify` is safe to retry with the same `requestId`.

When AgentGate itself is unreachable, your integration decides. The
reference nginx configuration and the middleware adapters fail closed for
enforce-mode sites and open for monitor-mode sites; see
[When AgentGate is unreachable](/docs/siteverify#when-agentgate-is-unreachable).
