# [Access control](https://rebilder.com/docs/access-control)

> Allow, deny or rate-limit AI agents on your own site with the gateway’s access policy. Rules that name a platform are enforced with Web Bot Auth verification.

- **Updated:** 2026-09-28
- **Author:** Rebilder
- **Section:** Concepts
- **Description:** Allow, deny or rate-limit AI agents on your own site with the gateway’s access policy. Rules that name a platform are enforced with Web Bot Auth verification.
- **Publisher:** Rebilder

## Set a policy

Access control is off by default: with `access` unset, every agent is served as usual. Add a policy to the same `GatewayConfig` every adapter uses:

gateway-config.ts

```
import type { GatewayConfig } from '@rebilder/gateway'

export const gatewayConfig: GatewayConfig = {
  storeId: 'store_123',
  sources: { /* … */ },
  verification: { keys: myRegistry },      // required for platform-named rules
  access: {
    default: 'allow',
    rules: [
      { subject: 'chatgpt', action: 'allow' },
      { subject: 'unverified', action: 'limit', limit: { requests: 60, windowSeconds: 60 } },
      { subject: '*', action: 'allow' },
    ],
  },
}
```

## Subjects, most specific first

- A platform id (`chatgpt`, `claude`, `gemini`, `perplexity` and the others in [Classification](/docs/classification)) matches a **verified** agent of that platform.
- `unverified` matches any agent that presented no verifiable identity.
- `*` matches every agent.
- When nothing matches, `default` applies. It is `allow` unless you set it.

> **Platform rules need verification** A rule that names a platform is enforced only when `verification` is configured with trusted keys; without it the rule is rejected and reported, not applied. A rule keyed on a `User-Agent` anyone can edit would not be a control. `unverified` and `*` need no verification. See [Protocols](/docs/protocols#checkout-verification) for key setup.

## What a denied agent receives

- A denial answers `403`, and a rate limit answers `429` with `Retry-After`, each with a small JSON body.
- The policy runs before any source, protocol handler or rendering, so refusing a request costs less than serving one.
- Denied requests still emit an event on the `denied` path, so your reports show the effect of the policy. See [Events](/docs/events).
- Search crawlers are not agents: Googlebot receives your canonical HTML even under `default: "deny"`.

## Rate limits and policy updates

The rate limiter counts per process. Separate server or edge instances keep separate counts, so put a shared limiter upstream if you need one global cap.

`access` takes a policy or a synchronous getter; it is never fetched per request. To change the policy at runtime, fetch and verify it on your own schedule and have the getter return the latest one. Counters survive a policy swap.

gateway-config.ts

```
import type { AccessPolicy, GatewayConfig } from '@rebilder/gateway'

let current: AccessPolicy = BOOT_POLICY
setInterval(async () => { current = await fetchAndVerifyPolicy() }, 300_000)

export const gatewayConfig: GatewayConfig = { /* … */, access: () => current }
```

## Validate where you edit the policy

`compileAccessPolicy` is exported so the screen where someone edits a policy can show which rules would be rejected, and why, before it is saved:

policy-editor.ts

```
import { compileAccessPolicy } from '@rebilder/gateway'

const compiled = compileAccessPolicy(draft, { verificationConfigured: true })
// Show compiled.rejected to the person editing the policy before you save it.
```