Skip to content

pg_policy

Agentic policy language for PostgreSQL with guardrails, guidance, and session-aware controls

Overview

PackageVersionCategoryLicenseLanguage
pg_policy0.1.0SECPostgreSQLSQL
IDExtensionBinLibLoadCreateTrustRelocSchema
7440pg_policyNoNoNoYesNoNopolicy

PIGSTY patches the reserved upstream schema pg_policy to policy and quotes the reserved check function, so the packaged API is policy.check() rather than pg_policy.check(); pure SQL and PL/pgSQL, no preload.

Version

TypeRepoVersionPG VerPackageDeps
EXTPIGSTY0.1.01817161514pg_policy-
RPMPIGSTY0.1.01817161514pg_policy_$v-
DEBPIGSTY0.1.01817161514postgresql-$v-pg-policy-
OS / PGPG18PG17PG16PG15PG14
el8.x86_64
el8.aarch64
el9.x86_64
el9.aarch64
el10.x86_64
el10.aarch64
d12.x86_64
PIGSTY 0.1.0
PIGSTY 0.1.0
PIGSTY 0.1.0
PIGSTY 0.1.0
PIGSTY 0.1.0
d12.aarch64
PIGSTY 0.1.0
PIGSTY 0.1.0
PIGSTY 0.1.0
PIGSTY 0.1.0
PIGSTY 0.1.0
d13.x86_64
PIGSTY 0.1.0
PIGSTY 0.1.0
PIGSTY 0.1.0
PIGSTY 0.1.0
PIGSTY 0.1.0
d13.aarch64
PIGSTY 0.1.0
PIGSTY 0.1.0
PIGSTY 0.1.0
PIGSTY 0.1.0
PIGSTY 0.1.0
u22.x86_64
PIGSTY 0.1.0
PIGSTY 0.1.0
PIGSTY 0.1.0
PIGSTY 0.1.0
PIGSTY 0.1.0
u22.aarch64
PIGSTY 0.1.0
PIGSTY 0.1.0
PIGSTY 0.1.0
PIGSTY 0.1.0
PIGSTY 0.1.0
u24.x86_64
PIGSTY 0.1.0
PIGSTY 0.1.0
PIGSTY 0.1.0
PIGSTY 0.1.0
PIGSTY 0.1.0
u24.aarch64
PIGSTY 0.1.0
PIGSTY 0.1.0
PIGSTY 0.1.0
PIGSTY 0.1.0
PIGSTY 0.1.0
u26.x86_64
PIGSTY 0.1.0
PIGSTY 0.1.0
PIGSTY 0.1.0
PIGSTY 0.1.0
PIGSTY 0.1.0
u26.aarch64
PIGSTY 0.1.0
PIGSTY 0.1.0
PIGSTY 0.1.0
PIGSTY 0.1.0
PIGSTY 0.1.0

Build

You can build the RPM / DEB packages for pg_policy using pig build:

pig build pkg pg_policy         # build RPM / DEB packages

Install

You can install pg_policy directly. First, make sure the PGDG and PIGSTY repositories are added and enabled:

pig repo add pgsql -u          # Add repo and update cache

Install the extension using pig or apt/yum/dnf:

Install
pig install pg_policy;          # Install for current active PG version
pig
pig ext install -y pg_policy -v 18  # PG 18
pig ext install -y pg_policy -v 17  # PG 17
pig ext install -y pg_policy -v 16  # PG 16
pig ext install -y pg_policy -v 15  # PG 15
pig ext install -y pg_policy -v 14  # PG 14
dnf
dnf install -y pg_policy_18       # PG 18
dnf install -y pg_policy_17       # PG 17
dnf install -y pg_policy_16       # PG 16
dnf install -y pg_policy_15       # PG 15
dnf install -y pg_policy_14       # PG 14
apt
apt install -y postgresql-18-pg-policy   # PG 18
apt install -y postgresql-17-pg-policy   # PG 17
apt install -y postgresql-16-pg-policy   # PG 16
apt install -y postgresql-15-pg-policy   # PG 15
apt install -y postgresql-14-pg-policy   # PG 14

Create Extension:

CREATE EXTENSION pg_policy;

Usage

Sources:

pg_policy 0.1.0 is an experimental SQL and PL/pgSQL policy evaluator for agent and tool actions. It stores Agent Policy Language rules, evaluates context and session history, records every decision, and returns obligations for a gateway to enforce. It complements PostgreSQL roles and row-level security; it does not intercept SQL or tool calls by itself.

Pigsty Schema Compatibility

Upstream 0.1.0 declares the reserved schema name pg_policy and defines an unquoted function named check. Pigsty packages patch the installed schema to policy, quote the reserved function name as policy."check"(), and fix function search paths. The upstream examples therefore cannot be copied verbatim into a Pigsty installation.

CREATE EXTENSION pg_policy;

SELECT policy.set_setting('enforcement_mode', 'log_only');

The extension is not relocatable, requires PostgreSQL 14 or later, and does not require shared_preload_libraries or a PostgreSQL restart. Current Pigsty packages cover PostgreSQL 14–18.

Define and Evaluate a Guardrail

SELECT policy.upsert_policy('block_ddl', $apl$
forbid
  principal agent "research_bot"
  action tool "execute_sql"
  when { context.statement_type in ["DROP", "TRUNCATE", "ALTER", "CREATE"] }
  reason "Research agents may not run DDL"
$apl$);

SELECT policy.set_setting('enforcement_mode', 'enforce');

SELECT policy.evaluate(
  'agent', 'research_bot',
  'tool', 'execute_sql',
  '*', '*',
  '{"statement_type":"DROP"}'::jsonb,
  NULL
);

SELECT policy."check"(
  'research_bot',
  'execute_sql',
  '{"statement_type":"DROP"}'::jsonb
);

policy.evaluate(...) returns JSON containing decision, allowed, matched_policies, obligations, reasons, and mode. The convenience wrapper policy."check"() returns only a boolean. policy.enforce() requests exception-on-deny behavior when the mode is enforce.

APL Surface

An APL document begins with one effect: permit, forbid, or guide. It can match principal, action, and resource types and identifiers. In 0.1.0, context conditions support only ==, in [...], and and. A temporal clause can count matching session events inside an interval when evaluation receives a session identifier.

forbid overrides matching permit rules. guide allows the action and can return advice, prefer_tool, or max_rows obligations. The caller—not the extension—must interpret and apply those obligations.

Sessions, Temporal Limits, and Audit

SELECT policy.open_session(
  'sess-1',
  'agent',
  'research_bot'
);

SELECT policy.upsert_policy('export_budget', $apl$
forbid
  principal agent "research_bot"
  action tool "export_csv"
  when temporal {
    count(action == "export_csv") within interval '1 hour' >= 3
  }
  reason "Export budget exceeded"
$apl$);

SELECT policy.evaluate(
  'agent', 'research_bot',
  'tool', 'export_csv',
  '*', '*',
  '{}'::jsonb,
  'sess-1'
);

policy.open_session() creates or updates a session. Evaluations with a session identifier append an event and can satisfy temporal predicates. Every evaluation writes policy.decision_log; other important relations are policy.policies, policy.sessions, policy.events, and policy.settings.

Enforcement and Security Boundaries

  • The default enforcement_mode is log_only and the default decision is permit. A matched deny becomes an allow with a shadow_deny obligation.
  • In guide mode, a matched deny becomes an allow with would_deny. Only enforce preserves a deny and allows policy.enforce() to raise an error.
  • A gateway must call the evaluator before the protected action and hard-fail on deny. Calling policy.evaluate(...) after executing a tool is only auditing.
  • Keep PostgreSQL GRANT and REVOKE, row-level security, network controls, and least-privilege credentials as the authoritative data-plane controls. Superusers and roles with BYPASSRLS can bypass row-level controls.
  • The 0.1 line is explicitly an experimental MVP, not a hardened production security boundary. Shadow-test policies, restrict who can change policy.settings or policy.policies, and monitor policy.decision_log before switching to enforce.

Was this page helpful?