# Guardrails

A guardrail is a policy for a whole workspace. It limits which offers can serve a call, caps spending, limits send rates, and checks the text of a call for prompt injections and sensitive data. The router applies it to every call.

A [purchasing rule](https://inference.ai.exchange/docs/rules.md) says what you want to buy. A guardrail says what is never allowed. A request, a preset or a header cannot widen it.

## Set up a guardrail

1. Open **Rules** in the console, then **Guardrails**.
2. Choose the default guardrail, or choose **New guardrail**.
3. Go through the steps, from **Name and keys** to **Review**.
4. In **Prompt injection** and **Sensitive info**, paste a sample under **Try it** to see what a provider would receive. It saves nothing and costs nothing.
5. Save the guardrail.

The next call uses the new rules. Only owners and admins can change guardrails. Members can read them.

## Which keys a guardrail covers

Each workspace has one **default guardrail**. It covers every API key and every console call. You cannot delete it.

- A new guardrail covers **All keys** by default, also keys you make later.
- **Select keys** covers only the keys you pick.
- A key follows the default guardrail, the all-keys guardrails and its own guardrail together.
- A key has at most one guardrail of its own. A workspace has at most 20 guardrails, the default one included.

## What a call sees

A guardrail blocks emails. A call with `jane@example.com` in a message gets this response before the router holds money. You pay nothing.

```json
{
  "error": {
    "message": "a guardrail of this workspace blocks requests that match an email address",
    "type": "liquid_error",
    "code": "content_blocked"
  }
}
```

If the detector is set to **Redact**, the call goes through. The provider receives `Reply to [REDACTED_EMAIL]`.

## What a guardrail can hold

A guardrail has five kinds of rule: limits that remove offers, a budget, rate limits, a prompt injection check and a sensitive info check.

### Your own cloud keys

Some limits decide which other companies can receive a prompt. They do not apply to an offer on a cloud key that your workspace connected (see [Bring your own key (BYOK)](https://inference.ai.exchange/docs/byok.md)), because an admin already chose that cloud. These do not apply:

- the allowed and excluded providers
- the regions
- the retention
- the provider claims

All other rules apply to your own key. A limit that the call or the preset of its key sets also applies. For example, `"privacy": {"regions": ["eu"]}` excludes a key whose region is `global`.

If the call and its preset set no retention, the exchange asks for none, also for your own key. A key that keeps prompts takes only a call that sends `"privacy": {"retention": "allowed"}`. See [Bring your own key (BYOK)](https://inference.ai.exchange/docs/byok.md#control-a-key).

### Budget

A budget is an amount in USD and a period: **Daily**, **Weekly** or **Every 30 days**. The period is a window that ends now: the last 24 hours, 7 days or 30 days.

The router adds the settled charges in the window and the price caps of jobs in flight. It refuses a call with `402 budget_exhausted` when the call's price cap is more than the budget that remains, so a high cap can fail before the budget is spent.

- A key guardrail counts the spend of that key.
- The default guardrail counts the spend of the workspace.
- To limit one key without a guardrail, use the spending limit in [API keys](https://inference.ai.exchange/docs/keys.md).

#### Guardrail of an organization

The default guardrail of an organization bounds every workspace that spends from the organization. It does not bind a workspace with its own balance.

A workspace adds its own guardrails. It can make limits stricter and cannot loosen them. The router uses the stricter value of each limit. The workspace sees the guardrail of the organization and cannot change it.

Only an owner or an admin of the organization can change its guardrail, unless they gave a member the **Guardrail** level of write. See [Workspace members](https://inference.ai.exchange/docs/members.md).

##### Guardrails that cannot merge

The exchange refuses to save a workspace guardrail that cannot merge with the guardrail of the organization, such as an allowlist that shares no provider with the allowlist of the organization. The answer is `422` with the code `guardrail_conflicts_with_organization`. A move of a workspace into an organization is refused the same way.

If the organization tightens its guardrail later, the save is accepted. The router then refuses each call that cannot merge. An owner or an admin sees each open workspace that no longer merges, in the field `conflicts`.

In a workspace under an organization, each step marks the rules that the organization changes:

| Mark                            | Meaning                                                      |
| ------------------------------- | ------------------------------------------------------------ |
| **From organization**           | Your rule is blank and the rule of the organization applies. |
| **Stricter than organization**  | Your rule is tighter.                                        |
| **Overridden by organization**  | Part of your rule has no effect.                             |
| **Conflicts with organization** | No call could pass both. You cannot save the guardrail.      |

##### Budgets of an organization

The guardrail of an organization has three budgets:

- **Budget of the organization.** It counts the jobs of the organization and of every workspace under it, once, through the payer.
- **Budget for each workspace.** Set it in the **Budget for each workspace** card on the Guardrails page of the organization. The router counts each workspace on its own. The lower remaining amount applies.
- **Budget for each end user.** It applies to each end user within each workspace.

A rate limit of the scope `workspace` applies to each workspace on its own.

#### Budget for each end user

A guardrail can hold a budget for each end user, with the same periods. Each end user has its own budget, so one end user that spends all of it does not stop the others. See [End users](#end-users).

The router takes the lowest of these as the price cap of the call: the balance, the key limit, the budget that remains in each guardrail, and the end-user budget that remains. A call over the end-user budget gets `402`: `insufficient_balance` if settled spend used the budget, or `budget_exhausted` if jobs in flight hold the rest.

### Rate limits

A rate limit has two optional numbers. A blank number sets no limit.

| Field                 | Counts                                                                          |
| --------------------- | ------------------------------------------------------------------------------- |
| `requests_per_minute` | Calls that the router admits in a sliding window of 60 seconds.                 |
| `tokens_per_minute`   | The input tokens plus the output tokens that the call asks for, in that window. |

A guardrail sets each limit for three scopes:

| Scope       | Applies to                                                                    |
| ----------- | ----------------------------------------------------------------------------- |
| `key`       | Each key of the workspace, one at a time. One busy key does not slow another. |
| `end_user`  | Each end user that a call names, one at a time. See [End users](#end-users).  |
| `workspace` | One total for all calls of the workspace.                                     |

A call must have room in every limit that applies to it. When two guardrails set the same limit, the lower number wins.

- The router counts a call when it admits it, before it holds money. A refused call counts for nothing.
- A call with `n` choices counts `n` times.
- The token count uses `max_completion_tokens` or `max_tokens`, or else the default output limit. Unused tokens are not given back.

A call over a limit gets `429 rate_limited`. Only a refused call carries these headers:

```http
HTTP/1.1 429 Too Many Requests
Retry-After: 12
X-RateLimit-Limit-Requests: 20
X-RateLimit-Remaining-Requests: 0
X-RateLimit-Scope: end_user
```

- `Retry-After` gives the seconds to wait.
- `X-RateLimit-Limit-Requests` and `-Remaining-Requests` give the limit that refused the call and what is left. The `-Tokens` headers do the same for tokens.
- `X-RateLimit-Scope` is `key`, `end_user` or `workspace`.

A rate limit is not exact. Each router process keeps its counters in memory, so the real limit can be a few times higher. It stops a loop or a leaked key. A budget is exact, because the ledger enforces it.

### End users

An **end user** is a person or a system that calls through your application. The exchange has no account for it. You name it in the call, and the router can then limit its speed and its spend.

The router reads the end-user id from the first of these that is present:

1. The header `X-Liquid-End-User`.
2. The `user` field of an OpenAI-format body.
3. The `metadata.user_id` field of an Anthropic-format body.

For example, add `-H "X-Liquid-End-User: customer-4821"` to a request.

- The id has 1 to 512 printable ASCII characters. Otherwise the call gets `400 invalid_end_user`.
- An empty `user` field in the body is ignored, because some SDKs send one. An empty header is refused.
- A call with no id counts as the end user `anonymous`.
- The router stores only a keyed hash of the id, and the same id in two workspaces gives two hashes. The id never goes to a provider.

The id is not a credential. Any holder of a key can send any id. An end-user limit protects the workspace from its own customers, and a key limit from the holder of a key.

### Prompt injection

The router looks for text that tries to override your instructions, such as "ignore all previous instructions". The action is **Off**, **Flag** (record the match and send the call) or **Block** (refuse the call before a provider sees it).

By default the router reads every message, including tool results, because an injection from a web page or a document arrives there. You can choose to read only the last user message. There is no **Redact** action, because replacing text in the middle of a sentence changes its meaning.

### Sensitive info

Set one action for each detector: **Off**, **Flag** (record the match and send the text unchanged), **Redact** (replace each match with a placeholder such as `[REDACTED_EMAIL]` before any provider sees it) or **Block** (refuse the call). The detectors are in the [reference](#reference). You can add up to 10 custom patterns, each with its own action.

### What the content rules do not do

- The router reads the text of messages and tool-call arguments. It does not read images or the answer.
- Detection uses patterns and checksums, not a model. It cannot find a person's name or an address. Do not treat it as complete.
- The router checks the text before it holds money or contacts a provider. A blocked call is not charged and never gets a cached answer.
- The router records a **finding** for each match: the detector, the action and the number of matches, never the text. The **Guardrails** page shows the findings of the last 7 days.

## When several guardrails apply

For a call, the router merges the default guardrail and the key guardrail into the strictest rules that satisfy both. Guardrails are the outermost layer. The rules of a request, the preset of a key and the headers apply inside them.

- A ceiling or a time limit takes the lower value. A minimum throughput takes the higher value.
- Two allowlists become their intersection. If it is empty, no offer can serve the call. An empty list or a list with `*` does not limit.
- Two exclusion lists join.
- For each detector, the stronger action wins: `flag`, then `redact`, then `block`.
- The injection rule reads at least the messages that either guardrail reads.
- Each budget applies on its own. For each rate limit, the lower number wins.

Two pattern lists, such as `gpt-*` and `*-mini`, have no exact intersection. The router keeps a pattern only if the other list holds it or names a model that it matches.

## Refusals

A refused call gets `guardrail_violation`, `content_blocked`, `budget_exhausted`, `rate_limited`, `invalid_end_user` or `guardrail_unavailable`. See [Errors](https://inference.ai.exchange/docs/errors.md). If the router cannot read the guardrails, it refuses the call, because a call without them could spend what you forbade.

## Reference

### Limits

The limits use the same fields as the [Limits](https://inference.ai.exchange/docs/rules.md) of a purchasing rule:

- `exchange.candidates.providers.allow` and `exchange.candidates.providers.exclude`
- `allowed_models` and `exchange.candidates.models.exclude`. Only a guardrail takes `allowed_models`. A name matches the model or the deployment symbol, and `*` matches any run of characters.
- `exchange.candidates.creators.allow`
- `exchange.privacy.regions`. The console lists every published region, also a region with no offer now.
- `exchange.privacy.retention`, which is **Zero data retention only** in the console
- `exchange.privacy.requirements`, the claims that a provider must hold. See [Provider claims](https://inference.ai.exchange/docs/provider-claims.md).
- `cap_usd`, the price cap for one call
- `exchange.limits.price.input_usd_per_mtok`, `exchange.limits.price.output_usd_per_mtok`, `exchange.limits.price.cache_read_usd_per_mtok` and `exchange.limits.price.cache_write_usd_per_mtok`
- `ttft_ms`, `completion_ms` and `exchange.limits.min_tps`

A guardrail only removes choices. It cannot hold `exchange.candidates.models.allow`, `exchange.rank.strategy`, `exchange.rank.workload_profile` or `exchange.cache`. The console refuses to save these fields.

The rate limits are `limits`, with `key`, `end_user` and `workspace`. A number below 1 is refused. Leave a number out for no limit. The budget for each end user is `end_user_budget`, an amount and a period.

```json
{
  "limits": {
    "key": { "requests_per_minute": 600, "tokens_per_minute": 400000 },
    "end_user": { "requests_per_minute": 20 },
    "workspace": { "requests_per_minute": 5000 }
  }
}
```

### Detectors

| Detector      | What it finds                                                    |
| ------------- | ---------------------------------------------------------------- |
| `email`       | Email addresses.                                                 |
| `phone`       | Phone numbers of 10 to 13 digits.                                |
| `ssn`         | US Social Security numbers.                                      |
| `credit_card` | Card numbers of 13 to 19 digits that pass the Luhn check.        |
| `ip_address`  | IPv4 addresses.                                                  |
| `secret`      | API keys, access tokens, JSON web tokens and private key blocks. |

### Custom patterns

A guardrail can hold up to 10 custom patterns. Each has a name of 1 to 40 letters, digits, `_` or `-`, a regular expression of up to 200 characters, and an action of `flag`, `redact` or `block`. The expression cannot use look-around or back-references. A name cannot repeat a built-in detector. The tester reads at most 10,000 characters.

### Tokens that change guardrails

A token from `liquid login --write` can change guardrails for an owner or an admin. A token from `liquid login` can only read them. See [Liquid CLI](https://inference.ai.exchange/docs/cli.md). An API key cannot read or change guardrails.
