# Gateway overview

> Put AgentGate in front of any HTTP app. Routes map requests to actions; GET /v1/gateway/check decides each request before it reaches your origin.

The gateway decides every protected request with the same engine as the
browser widget: tokens, clearances, verified agent signatures, rate policy
and the site's rules. Your proxy or middleware asks
`GET /v1/gateway/check` before forwarding, and forwards only on a `204`.

Use it when you want protection without touching each handler, for routes
that browsers never call (APIs, webhooks, agents), or when the same routes
serve people and agents.

## 1. Describe the site's routes

A site's **routes** map a request to an **action**, and the action says
what evidence it needs. Set them in the console (site settings) or with the
admin API:

```json
{
  "allowedOrigins": ["https://app.example.com"],
  "actions": {
    "signup":       {"browserRequired": true},
    "checkout":     {"browserRequired": true, "clearance": true},
    "catalog_read": {"browserRequired": false},
    "agent_orders": {"browserRequired": false, "requireContentDigest": true}
  },
  "routes": [
    {"method": "POST", "path": "/api/signup", "action": "signup"},
    {"method": "POST", "path": "/checkout", "action": "checkout"},
    {"method": "GET", "pathPrefix": "/api/catalog/", "action": "catalog_read"},
    {"method": "POST", "path": "/api/orders", "action": "agent_orders"}
  ]
}
```

- **Browser routes** (`browserRequired: true`) need a single-use token from
  the browser SDK in `X-AgentGate-Token`, or, for `clearance: true`
  actions, a valid clearance (`X-AgentGate-Clearance` or `AgentGate-Clearance` header, or the
  `agentgate_clearance` cookie). Without one the answer is a challenge
  (`401`).
- **Machine routes** (`browserRequired: false`) never ask for browser
  evidence: rate policy, agent identity and rules only. Requests no route
  matches are evaluated the same way.
- The action always comes from this configuration, never from the client.
  Paths are normalised like nginx: `/api/./signup`, `/api//signup` and
  `/api/%73ignup` are all `/api/signup`.
- `requireContentDigest: true` makes a signed agent's signature cover the
  request body's `Content-Digest` (see [Signed agents](#signed-agents)).

The page sends the token as a header:

```js
const { token } = await AgentGate.execute({ action: "signup" });
await fetch("/api/signup", { method: "POST", headers: { "X-AgentGate-Token": token }, body });
```

> [!NOTE]
> nginx `auth_request` never sees the request body, so a hidden form field
> cannot reach the gateway. For classic form posts, validate the token in
> your backend with [`/v1/siteverify`](/docs/siteverify) instead.

## 2. The check API

`GET /v1/gateway/check`, called by your gateway only (never by browsers):

| Request header | Meaning |
| --- | --- |
| `Authorization: Bearer ags_…` | the site's backend secret (required) |
| `X-AgentGate-Site-Key` | optional; must name the secret's site |
| `X-Original-Method`, `X-Original-Host`, `X-Original-URI`, `X-Original-IP` | the original request (required) |
| `X-Original-Proto` | `http` or `https` (default `https`) |
| `X-Original-Body-Digest` | `match` or `mismatch` if the adapter checked `Content-Digest` against the body; omit when it never saw the body |
| `X-AgentGate-Token`, `X-AgentGate-Clearance`, `Cookie` | the client's token and clearance |
| `Signature`, `Signature-Input`, `Signature-Agent`, `Content-Digest`, `User-Agent`, … | the client's own headers, forwarded |

`X-Original-*` headers are believed only from a caller with a valid secret
for the site; without one the answer is `403 invalid_credential` and
nothing is evaluated.

| Answer | When | Headers |
| --- | --- | --- |
| `204` | allow; always in monitor mode | `X-AgentGate-Decision: allow`, `X-AgentGate-Reason`, `X-AgentGate-Decision-ID`, `X-AgentGate-Agent` (verified agent), `X-AgentGate-Would-Decision` (monitor), `X-AgentGate-Digest-Check: required` |
| `401` | challenge: get a (new) token | `X-AgentGate-Reason`, for example `receipt_required`, `token_used`, `expired_clearance` |
| `403` | block, or a bad secret or metadata (every mode) | `X-AgentGate-Reason`, for example `rate_limited`, `agent_signature_invalid`, `policy`, `invalid_credential` |
| `503` | dependency failure in enforce mode | `X-AgentGate-Reason: service_unavailable`, `Retry-After: 5` |

Every reason is listed on [Error codes](/docs/error-codes#gateway). The
endpoint never answers `429`; the nginx configuration maps
`403 rate_limited` to `429` for the client and `403 payment_required` to
`402`. In monitor mode a failing token verifier is recorded and the
request allowed with reason `service_unavailable`, never silently.

Rate budgets per site: `GATEWAY_IP_PER_MINUTE` per client address and
`GATEWAY_AGENT_PER_MINUTE` per verified agent without its own quota (both
default 600). Exceeding one labels the request; the rules decide.

## When a token is spent

A token is spent only by the request it admits:

- the check first validates it without spending it (signature, site,
  action and origin, expiry, not yet used), then evaluates the request;
- only an allow spends it, in one transaction with the decision event;
- a block or challenge after a valid token (a rate limit, a rule, a bad
  signature) leaves it unspent;
- if the transaction fails, enforce mode answers `503` and the token stays
  unspent, so retrying the same operation with it is safe;
- of several concurrent requests carrying one token, exactly one is
  allowed; the others get `401 token_used`.

This is exactly-once admission at AgentGate, not exactly-once execution at
your origin: if your origin fails after a `204`, the token is spent and the
client needs a new one.

## Signed agents

Web Bot Auth signatures are verified at the gateway over `@authority`,
`@method`, `@path` and the covered `Signature-Agent` member. Because
`auth_request` never sees the body, `content-digest` is required only on
actions with `requireContentDigest: true`. There the gateway checks that the
signature covers it and answers `X-AgentGate-Digest-Check: required`, and
your [middleware](/docs/gateway-middleware) verifies the digest against the
body. A middleware that checked the body itself sends
`X-Original-Body-Digest` instead.

## 3. Pick an adapter

- [nginx](/docs/gateway-nginx): `auth_request` in front of any app, tested
  against a real nginx.
- [Go and Node middleware](/docs/gateway-middleware): inside your app, with
  body digest checks.

## 4. Go live

1. Start in monitor mode and watch the would-have decisions for false
   positives.
2. Switch the site to enforce.
3. Rotate the backend secret with an overlap window: update your gateway's
   secret and reload before the old one expires.

Limits: rate budgets are process-local (one AgentGate instance per state
directory), and content classifiers do not run on gateway checks because
the gateway sees no body.
