> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pikarc.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Guardrails Engine

> How Pikarc evaluates rules and makes ALLOW/DENY decisions at every boundary.

The decision engine evaluates guardrail rules at two boundaries: **run start** and **step creation**. Evaluation short-circuits on the first DENY — if the kill switch is active, no other rules are checked.

## Evaluation Priority

Rules are evaluated in this exact order:

| Priority | Rule                   | Deny Reason                       | Run Start | Step |
| -------- | ---------------------- | --------------------------------- | :-------: | :--: |
| 1        | Kill switch            | `KILL_SWITCH_ACTIVE`              |    Yes    |  Yes |
| 2        | User blocked           | `USER_BLOCKED`                    |    Yes    |  Yes |
| 3        | Workspace daily budget | `WORKSPACE_DAILY_BUDGET_EXCEEDED` |    Yes    |  Yes |
| 4        | User daily budget      | `USER_DAILY_BUDGET_EXCEEDED`      |    Yes    |  Yes |
| 5        | Monthly run limit      | `MONTHLY_RUN_LIMIT_EXCEEDED`      |    Yes    |  No  |
| 6        | Max concurrent runs    | `MAX_CONCURRENT_RUNS_EXCEEDED`    |    Yes    |  No  |

Rules 5 and 6 only apply at run start — they don't make sense for individual steps within an already-running run.

## Run Start vs Step Checks

### Run Start (all 6 rules)

When `guard.run()` is entered, the SDK calls `POST /v1/runs/`. The engine evaluates all 6 rules:

1. Is the kill switch active?
2. Is this user blocked?
3. Has the workspace exceeded its daily budget?
4. Has this user exceeded their daily budget?
5. Has the workspace hit its monthly run limit?
6. Are there too many concurrent runs?

If allowed: a new run is created with status `RUNNING`, the concurrent run counter is incremented, and the monthly run counter is incremented.

### Step Checks (rules 1-4)

When `run.model_call()` or `run.tool_call()` is called, the SDK calls `POST /v1/runs/{id}/steps`. The engine evaluates rules 1-4:

1. Is the kill switch active?
2. Is this user blocked?
3. Has the workspace exceeded its daily budget?
4. Has this user exceeded their daily budget?

This catches cases where budgets are exceeded mid-run, or where the kill switch is activated while a run is in progress.

## Two-Tier Evaluation

Pikarc uses a two-tier evaluation strategy for performance:

```
Request → Try Redis cache → cache hit? → evaluate from cache → return
                          → cache miss? → warm from Postgres → retry
                          → still miss? → fall back to full DB query
```

### Fast Path (Redis)

All guardrail data is cached in Redis:

* Kill switch state
* Workspace config (budgets, concurrent limit, plan, monthly run limit)
* User blocked status
* Daily spend counters (workspace and per-user)
* Concurrent run count
* Monthly run count

Spend counters use **microdollars** (1 USD = 1,000,000 microdollars) for atomic `INCRBY` without floating-point drift.

### Fallback Path (Postgres)

If any Redis value is missing, the engine attempts to warm the cache from Postgres and retry. If the cache is still incomplete, it falls back to a full database evaluation.

In practice, the Redis fast path handles the vast majority of requests. The Postgres fallback is a safety net for cold starts and Redis failures.

## Decision Record

Every evaluation produces a Decision record stored alongside the step:

```json theme={null}
{
  "outcome": "DENY",
  "reason": "WORKSPACE_DAILY_BUDGET_EXCEEDED",
  "evaluated_rules": {
    "kill_switch": "PASS",
    "user_blocked": "PASS",
    "workspace_daily_budget": "DENY"
  }
}
```

The `evaluated_rules` field shows exactly which rules were checked and their results, which is useful for debugging why a request was denied.

## Kill Switch

The kill switch is the highest-priority guardrail. When active, **every** run start and step creation is immediately denied with `KILL_SWITCH_ACTIVE`. No other rules are evaluated.

Toggle it via the API:

```bash theme={null}
# Enable
curl -X POST http://localhost:8000/v1/workspace/kill-switch \
  -H "Authorization: Bearer lg_..." \
  -H "Content-Type: application/json" \
  -d '{"active": true}'

# Disable
curl -X POST http://localhost:8000/v1/workspace/kill-switch \
  -H "Authorization: Bearer lg_..." \
  -H "Content-Type: application/json" \
  -d '{"active": false}'
```

Or use the toggle in the [Configuration page](https://app.pikarc.dev/configuration) of the dashboard.

<Warning>
  The kill switch requires the **Scale** plan or higher.
</Warning>
