Universal onboarding (works for every stack)
Time: ~30 minutes to shadow mode · 1–7 days to enforce
Requires: PostgreSQL 14+, ability to CREATE EXTENSION (or load the SQL in a lab)
This path is the same whether the client is psql, MCP, LangGraph, CrewAI, Cursor, or a homegrown tool gateway.
Install → Identity → Baseline pack → Shadow → Domain pack → Honor obligations → Enforce
Day 0 — Install the kernel
CREATE EXTENSION pg_policy;
SELECT pg_policy.set_setting('enforcement_mode', 'log_only'); -- never start at enforce
From a checkout:
psql "$DATABASE_URL" -f examples/packs/00-baseline.sql
You now have DDL/admin forbids + SQL guidance for every agent id "*".
Day 0 — Identity convention (do not skip)
Pick stable strings. Changing them later orphans logs.
| Field | Convention | Example |
|---|---|---|
principal_type |
agent |
agent |
principal_id |
{framework}:{name} |
langgraph:analytics, cursor:compose, mcp:postgres |
context.acting_for |
human user id (opaque) | user:42 or HMAC of email |
context.tenant_id |
tenant key also used by RLS | acme |
session_id |
one per user-visible chat/run | LangGraph thread_id, MCP session, gen_random_uuid() |
Open a session when the agent run starts:
SELECT pg_policy.open_session(
'thread-abc',
'agent',
'langgraph:analytics',
'{"acting_for":"user:42","tenant_id":"acme"}'::jsonb
);
Day 0 — Wire the one hook
Every tool call (not every SQL string inside a transaction) must hit evaluate before side effects.
Pseudo-contract (any language):
decision = evaluate(agent, tool, context, session_id)
if not decision.allowed: return error(decision.reasons)
apply(decision.obligations) # LIMIT, prefer other tool, show advice
execute_tool()
Copy-paste adapters: integrations.md and examples/integrations/.
If you cannot wrap tools yet, you are not ready for enforce. Stay in log_only and sample pg_policy.decision_log.
Day 1 — Prove shadow mode
SELECT at, principal_id, action_id, decision, reasons, obligations
FROM pg_policy.decision_log
ORDER BY at DESC
LIMIT 50;
Look for:
shadow_denyobligations → policies that would have blocked- agents with empty
acting_for - tools you forgot to name (
action_idjunk)
Fix names and context before loading a stricter pack.
Day 2 — Load a domain pack
| If you are… | Load |
|---|---|
| BI / text-to-SQL | examples/packs/analytics.sql |
| Support / CX | examples/packs/support.sql |
| Payments / ledger | examples/packs/fintech.sql |
| PHI / clinical | examples/packs/healthcare.sql |
| DBA / migrations | examples/packs/devops.sql |
| Multi-agent crew | examples/packs/multi-agent.sql |
Packs are idempotent upsert_policy scripts. They do not change enforcement_mode.
Also enable RLS on tenant tables (examples/04-rls-complement.sql pattern). Packs do not replace RLS.
Day 3–7 — Honor obligations in the PEP
guide never blocks. Your gateway must implement:
Obligation type |
PEP behavior |
|---|---|
max_rows |
Inject LIMIT / truncate result |
prefer_tool |
Hint the model or auto-route |
advice |
Put in tool error/warning channel |
shadow_deny / would_deny |
Metric + optional user-visible warning |
Until max_rows is enforced in the MCP, the policy is theater.
Promote to enforce
-- only after: no surprise shadow_denies you disagree with
SELECT pg_policy.set_setting('enforcement_mode', 'enforce');
Rollback is one row:
SELECT pg_policy.set_setting('enforcement_mode', 'log_only');
Least-privilege Postgres role (required for “works for all”)
Never connect the agent as table owner or superuser.
CREATE ROLE agent_runtime NOINHERIT LOGIN PASSWORD '...';
GRANT USAGE ON SCHEMA public TO agent_runtime;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO agent_runtime; -- tighten per pack
GRANT USAGE ON SCHEMA pg_policy TO agent_runtime;
GRANT SELECT, INSERT ON ALL TABLES IN SCHEMA pg_policy TO agent_runtime;
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA pg_policy TO agent_runtime;
-- Do NOT grant BYPASSRLS
ALTER ROLE agent_runtime SET default_transaction_read_only = on; -- analytics/support
Writes: dedicated agent_writer with default_transaction_read_only = off and a narrower pack.
Definition of done
- [ ] Extension installed; mode started
log_only - [ ] Stable
principal_id/session_id/acting_for - [ ] All tools go through
evaluate - [ ] Baseline + one domain pack loaded
- [ ] RLS enabled on tenant tables
- [ ] Obligations implemented in the gateway
- [ ] Week of shadow review
- [ ]
enforcewith a documented rollback
That onboarding is identical for every framework. Only the adapter file changes.