---
title: "Policies"
description: "A policy profile is the named bundle of rules PgBeam enforces for an agent credential. Access mode, allowlists, masking, budgets, and timeouts."
canonical: "https://pgbeam.com/docs/policies"
last-updated: "2026-10-30T13:32:17.000Z"
---

# Policies

> A policy profile is the named bundle of rules PgBeam enforces for an agent credential. Access mode, allowlists, masking, budgets, and timeouts.

URL: https://pgbeam.com/docs/policies

A policy profile is a named bundle of rules attached to one or more credentials. It is the single place you decide what a principal can do: read-only or read-write, which tables it can touch, which rows it can see, which columns are masked, and how much it can run. Policies stream to the data planes and hot-reload, so a change takes effect on the next query without restarting anything.

Policies apply to agent credentials and to human credentials alike. Every rule below works the same whether an AI agent or a person runs the statement. See [human and passthrough connections](#human-and-passthrough-connections) for applying policies beyond per-agent credentials.

## What a policy contains

| Rule              | What it controls                                                                  | Page                                           |
| ----------------- | --------------------------------------------------------------------------------- | ---------------------------------------------- |
| Access mode       | Read-only or read-write, plus per-statement-type rules.                           | [Read-only](/docs/read-only)                   |
| Table allowlist   | The schemas and tables the principal may read or write.                           | [Allowlists](/docs/allowlists)                 |
| Row filters       | A `WHERE` predicate that scopes a table to a slice of rows.                       | [Row-level policies](/docs/row-level-policies) |
| Masking rules     | Columns redacted, nulled, or hashed in flight.                                    | [Masking](/docs/masking)                       |
| Query mode        | Raw SQL, or only the project's pre-approved query templates.                      | [Query templates](/docs/query-templates)       |
| Query budgets     | Queries per window, max rows per result, statement timeout.                       | [Budgets](/docs/budgets)                       |
| Write row cap     | Hard cap on rows a single write may affect (over-cap writes roll back and block). | [Budgets](/docs/budgets)                       |
| Approvals         | Hold writes or DDL until a human approves them.                                   | [Approvals](/docs/approvals)                   |
| Migration linting | Warn or block dangerous DDL (locks, rewrites, unsafe drops).                      | [Safe migrations](/docs/safe-migrations)       |
| Write mode        | Commit writes normally or run them as a dry-run that rolls back.                  | [Dry-run writes](/docs/dry-run-writes)         |

## Create and attach a policy

**CLI**

```bash
pgbeam policies create --name analytics-readonly \
  --mode read_only \
  --allow public.orders,public.users \
  --mask users.email=hash \
  --budget-queries-per-day 5000 --max-rows 1000
# → Policy profile created: pol_1a2b3c…

pgbeam agents create --name analytics-bot --policy pol_1a2b3c…
```

**Dashboard**

Go to **Policies**, create a profile, set the rules, then attach it to one or
more agents from the **Credentials** tab.

**API**

```bash
curl -X POST https://api.pgbeam.com/v1/projects/{projectId}/policies \
  -H "Authorization: Bearer pbo_..." \
  -d '{
    "name": "analytics-readonly",
    "access_mode": "read_only",
    "table_allowlist": ["public.orders", "public.users"],
    "masking_rules": [{"table": "public.users", "column": "email", "kind": "hash"}],
    "budget_queries_per_day": 5000,
    "max_rows": 1000
  }'
```

## Enforcement model

PgBeam parses every statement a principal sends and checks it against the attached policy before forwarding it. Enforcement fails closed: unparseable SQL, unknown statement types, `COPY`, and multi-statement batches containing any blocked statement are rejected with an LLM-readable reason.

> **Hot reload**
>
> Policy changes stream to the data planes and apply on the next query. You do
> not need to rotate the credential or reconnect the principal.

## Test a policy before you roll it out

Two preview tools evaluate statements through the same engine the proxy enforces, so their verdicts match production exactly. Neither one connects to your database.

**What-if (single statement).** Paste a statement and see the decision this policy would make: allow, block, mask, or row-filter, with the rule, the reason, and any rewritten SQL. In the dashboard the what-if box sits inside the policy editor and evaluates your unsaved draft. From the CLI:

```bash
pgbeam policies dry-eval --policy pol_xxx --sql "SELECT email FROM users"
```

**Traffic replay (recorded history).** Replay your recent agent traffic from the [audit log](/docs/audit-log) against a draft or saved policy and see what would change before you save it: which queries that ran would now be blocked, which blocked queries would now be permitted, and which results would be masked or row-filtered. Traffic is deduplicated by normalized query shape, newest first, so one noisy query does not drown out the rest.

```bash
# Replay the last 7 days of traffic against a saved policy
pgbeam policies replay --policy pol_xxx

# Replay one credential's traffic against a draft, as JSON
pgbeam policies replay --draft ./policy.json --credential cred_xxx --json
```

The replay summary counts newly blocked and newly allowed queries; those are the changes to review before rollout. Stateful checks a preview cannot model (per-region budgets, approvals, write-mode routing) are reported as informational notes on each result.

**Right-size from traffic (recommend a policy).** Instead of writing a policy by hand, ask PgBeam to derive one from an agent credential's real [audit history](/docs/audit-log). It reads the statements the credential has actually run over a lookback window (30 days by default) and produces the tightest policy that still passes all of them:

* a table allowlist that is the union of the relations actually referenced,
* a statement-kind allow set limited to the kinds observed,
* a downgrade to read-only when no write, sequence-mutating, or lock-taking statement was seen, and
* a `max_rows` ceiling sized from a high percentile (p95) of observed row counts.

The recommendation is derived only from statements that legitimately executed; blocked attempts are excluded, so it never widens access to something that was already refused. The candidate is then proven safe by replaying it against that same history: a good recommendation reports zero newly blocked statements.

In the dashboard the "Right-size from traffic" box sits in the policy editor. Pick a credential and a window, and the candidate loads straight into the draft form for you to review and save. It never saves on its own and never changes an existing policy. From the CLI:

```bash
# Recommend a least-privilege policy from an agent's last 30 days of traffic
pgbeam agents recommend-policy agt_xxx

# Widen the window and print the candidate plus its replay proof as JSON
pgbeam agents recommend-policy agt_xxx --lookback-days 90 --json
```

For testing against a local or dev database before anything reaches PgBeam at all, the `pgbeam-dev` tool evaluates a policy file offline and can run a local enforcing gateway. See [local policy testing](/docs/local-testing).

**Lint a policy for foot-guns.** What-if and replay check a policy against statements; the linter checks the policy's own shape. `pgbeam policies lint` runs offline over a saved policy or a draft file and flags risky combinations with a fix for each: read-write with no table allowlist, writes that commit with no `max_affected_rows` and no approval, no query budget, masking with no read ceiling, masking or row-filter rules on tables the policy makes unreachable, write settings that are inert under read-only, and redundant allow and deny overlaps. Use `--strict` to fail on any warning as a CI gate.

```bash
# Lint a saved policy
pgbeam policies lint --policy pol_xxx

# Lint a draft file and fail the build on any warning
pgbeam policies lint --draft ./policy.json --strict --json
```

## Human and passthrough connections

A policy can bind to more than a per-agent credential. Every credential carries a `principal_type`: `agent` for an AI agent's credential, `human` for a person's. The rules are identical; the field records who is connecting so the [audit log](/docs/audit-log) and [anomaly baselines](/docs/anomaly-detection) can tell agents and people apart.

You can also apply policies to connections that are not per-principal credentials at all:

| Scope                  | What it covers                                                                                                     |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------ |
| Project default policy | A baseline policy applied to every connection on the project, including your application's passthrough connection. |
| Per-database policy    | A policy that overrides the project default for one database.                                                      |
| Per-credential policy  | The policy attached to a single agent or human credential.                                                         |

The most specific scope wins: a per-credential policy overrides a per-database policy, which overrides the project default. Leave the project default unset to keep passthrough connections unrestricted, the original PgBeam behavior, and opt individual credentials into policies one at a time.

> **Turning the gateway into a database firewall**
>
> Set a project default policy to enforce row filters, masking, or read-only
> rules on every connection, not just agents. Your own application can then read
> through the same guardrails an agent does, scoped per database where you need
> a different rule.

## Related

* [Read-only enforcement](/docs/read-only)
* [Table allowlists](/docs/allowlists)
* [Row-level policies](/docs/row-level-policies)
* [PII masking](/docs/masking)
* [Query budgets](/docs/budgets)
* [Approvals](/docs/approvals)
* [Safe migrations](/docs/safe-migrations)
* [Dry-run writes](/docs/dry-run-writes)
* [Kill-switch](/docs/kill-switch)