The Agentic Data Firewall for PostgreSQL
Identity-aware SQL enforcement for AI agents. Block rogue queries before they hit your database.
Your AI agent has your database password. FaultWall makes sure that's safe.
Deterministic. No LLM in the loop. FaultWall uses the real PostgreSQL C parser (
pg_query_go) for static SQL analysis — no AI, no API keys, no probabilistic guessing. Every decision is auditable, reproducible, and adds under 1ms of latency.
A prompt injection hides a DROP TABLE in a customer feedback comment. Your agent blindly executes it. The WAF sees nothing — it's a legitimate connection with valid credentials. The database sees a normal query from an authorized user.
FaultWall intercepts the query before it reaches PostgreSQL, parses the SQL, checks it against your policy, and blocks it:
🔌 New connection: agent=cursor-ai/summarize-feedback
🟢 [ALLOWED] agent=cursor-ai/summarize-feedback query=SELECT * FROM feedback LIMIT 100;
🔴 [BLOCKED] agent=cursor-ai/summarize-feedback reason=blocked_operation query=DROP TABLE users;
🔴 [BLOCKED] agent=rogue-bot/steal reason=agent_not_in_policy query=SELECT * FROM users;
🔴 [BLOCKED] agent=cursor-ai/summarize-feedback reason=blocked_function:pg_read_file query=SELECT pg_read_file('/etc/passwd');
FaultWall sits between your agent and PostgreSQL as an inline L7 proxy. Every SQL query is parsed and checked before it reaches the database. Blocked queries never execute.
- Intercepts 100% of queries (Simple + Extended Query Protocol)
- Parses SQL using the real PostgreSQL C parser (
pg_query_go) — deterministic, no LLM - Sub-1ms latency overhead per query
- Works with any Postgres client: psql, psycopg2, pgx, SQLAlchemy, JDBC
- Fail-open on internal errors (won't break your app)
./faultwall --proxy --listen :5433 --upstream localhost:5432 --policies ./policies.yamlAgents connect to port 5433 instead of 5432. That's the only change.
FaultWall connects to your database as a read-only sidecar, polls pg_stat_activity, and logs violations. Good for visibility without being in the data path.
- Dashboard with agent activity, violations, cost attribution
- Anomaly detection and alerting
- Slack notifications
- No query blocking (observe only)
DATABASE_URL="postgres://user:pass@localhost:5432/mydb" \
POLICY_FILE=./policies.yaml \
./faultwallgit clone https://github.com/shreyasXV/faultwall && cd faultwall
go build -o faultwall .Or use Docker:
docker pull ghcr.io/shreyasxv/faultwall:latestCreate policies.yaml:
default_policy: deny
# Dangerous PostgreSQL functions blocked for ALL agents
blocked_functions:
- pg_read_file
- pg_read_binary_file
- pg_ls_dir
- pg_execute_server_program
- lo_export
- lo_import
- dblink
- dblink_exec
- pg_terminate_backend
- pg_cancel_backend
- pg_reload_conf
- pg_sleep
- set_config
agents:
cursor-ai:
description: "Cursor IDE agent"
profile: standard # use a security profile
blocked_tables: [public.users, public.payments]
missions:
summarize-feedback:
tables: [public.feedback, public.products]
langchain-agent:
description: "LangChain research agent"
blocked_operations: [DROP, TRUNCATE, ALTER, GRANT] # legacy mode still works
missions:
analyze-trends:
tables: [public.orders, public.products]
unidentified:
policy: deny # deny | monitor | allowPOLICY_ENFORCEMENT=enforce \
./faultwall --proxy --listen :5433 --upstream localhost:5432 --policies ./policies.yamlChange the connection port from 5432 to 5433:
Python (psycopg2):
conn = psycopg2.connect(
host="localhost", port=5433, dbname="mydb", user="myuser",
application_name="agent:cursor-ai:mission:summarize-feedback"
)Node.js (pg):
const client = new Client({
host: "localhost", port: 5433, database: "mydb", user: "myuser",
application_name: "agent:cursor-ai:mission:summarize-feedback"
});Go (pgx):
conn, err := pgx.Connect(ctx, "postgres://myuser@localhost:5433/mydb?application_name=agent:cursor-ai:mission:summarize-feedback")psql:
psql "host=localhost port=5433 user=myuser dbname=mydb application_name=agent:cursor-ai:mission:summarize-feedback"# This should work (agent has access to feedback table):
psql "host=localhost port=5433 ... application_name=agent:cursor-ai:mission:summarize-feedback" \
-c "SELECT * FROM feedback LIMIT 5;"
# This should be BLOCKED:
psql "host=localhost port=5433 ... application_name=agent:cursor-ai:mission:summarize-feedback" \
-c "DROP TABLE users;"
# ERROR: [BLOCKED by FaultWall] blocked_operation (op: DROP)┌─────────────────┐ ┌──────────────┐ ┌──────────────┐
│ AI Agent │────▶│ FaultWall │────▶│ PostgreSQL │
│ (port 5433) │ │ L7 Proxy │ │ (port 5432) │
└─────────────────┘ └──────┬───────┘ └──────────────┘
│
┌──────────┴──────────┐
│ For each query: │
│ 1. Parse SQL (AST) │
│ 2. Check policy │
│ 3. Allow or Block │
└─────────────────────┘
- Agent connects to FaultWall on port 5433
- FaultWall reads the startup message, extracts
application_namefor agent identity - Auth handshake is relayed to upstream PostgreSQL
- Every query (Simple or Extended protocol) is intercepted:
- SQL is parsed into an AST using
pg_query_go/v6(the real PostgreSQL C parser) - AST is checked against the agent's policy: operation type, tables, functions
- Allowed → query is forwarded to PostgreSQL, response relayed back
- Blocked → PostgreSQL never sees it. Client gets
ERROR: [BLOCKED by FaultWall] reason
- SQL is parsed into an AST using
- All other wire protocol messages are forwarded transparently
Agents identify themselves via PostgreSQL's application_name parameter:
agent:<agent_id>:mission:<mission_id>
This is set in the connection string — no code changes beyond the connection config. FaultWall reads it from the startup packet at connect time.
| Check | Example |
|---|---|
| Blocked operations | DROP, TRUNCATE, DELETE |
| Blocked tables | public.users, public.payments |
| Mission scope | Agent can only access feedback and products tables during this mission |
| Blocked functions | pg_read_file, dblink, lo_export (17 dangerous functions blocked by default) |
| Unknown agents | Agents not in the policy file are denied (when default_policy: deny) |
| Unidentified connections | Connections without agent: prefix are denied/monitored per config |
| Protocol | Coverage | Used By |
|---|---|---|
Simple Query (Q message) |
✅ Full inspection | psql, basic clients |
Extended Query (Parse/Bind/Execute) |
✅ Full inspection | psycopg2, pgx, SQLAlchemy, JDBC, all ORMs |
For teams that want visibility without putting a proxy in the data path:
DATABASE_URL="postgres://user:pass@localhost:5432/mydb" \
POLICY_FILE=./policies.yaml \
POLICY_ENFORCEMENT=monitor \
./faultwallFeatures:
- 📊 Real-time dashboard on port 8080
- 🔍 Anomaly detection (genetic algorithm-tuned baselines)
- ⚡ Auto-throttling (kill runaway queries)
- 💰 Cost attribution per tenant/agent
- 🤖 MCP server for AI agent self-monitoring
- 📨 Slack alerting
Limitation: Monitor mode polls pg_stat_activity every 10 seconds. Queries that complete faster than 10 seconds may not be detected. Use Proxy Mode for guaranteed enforcement.
Available in both modes at http://localhost:8080:
| Panel | What you see |
|---|---|
| Agent Connections | Active agents, missions, connection status |
| Violations | Blocked queries with agent, table, reason |
| Tenant Leaderboard | Ranked by queries, latency, cost |
| Cost Attribution | Per-agent/tenant cost breakdowns |
| Anomalies | Statistical deviations from baseline |
| Predictions | Trend forecasts and breach warnings |
version: "3.8"
services:
postgres:
image: postgres:16
environment:
POSTGRES_PASSWORD: postgres
ports:
- "5432:5432"
faultwall:
image: ghcr.io/shreyasxv/faultwall:latest
command: ["./faultwall", "--proxy", "--listen", ":5433", "--upstream", "postgres:5432", "--policies", "/etc/faultwall/policies.yaml"]
environment:
POLICY_ENFORCEMENT: enforce
volumes:
- ./policies.yaml:/etc/faultwall/policies.yaml:ro
ports:
- "5433:5433"
- "8080:8080"
depends_on:
- postgresdocker compose up -d
# Agents connect to localhost:5433Security profiles let you pick a security posture instead of manually listing operations. Three built-in profiles are available:
| Profile | Posture | Blocked | Use Case |
|---|---|---|---|
permissive |
Log everything, block nothing | — | Visibility into agent behavior |
standard |
Block dangerous ops | DCL, ADMIN, EXTENSION, FUNCTION categories + COPY | Agents read/write data safely |
strict |
Allowlist only | Everything except SELECT, INSERT, UPDATE, DELETE, EXPLAIN, TRANSACTION | Basic CRUD only |
standard and strict also enforce "DELETE must include WHERE" and "UPDATE must include WHERE" conditions.
agents:
my-agent:
profile: standard # pick a built-in profile
profile_overrides: # optional per-agent tweaks
allow: [COPY] # allow COPY even though standard blocks it
block: [DELETE] # block DELETE even though standard allows it
blocked_tables: [public.secrets] # table rules still apply on topDefine custom profiles in the profiles section:
profiles:
readonly:
extends: strict
allowed_operations: [SELECT, EXPLAIN]The existing blocked_operations field still works. If an agent has no profile, behavior is identical to before. If both profile and blocked_operations are set, the profile takes precedence.
| Flag | Default | Description |
|---|---|---|
--proxy |
— | Enable proxy mode |
--listen |
:5433 |
Proxy listen address |
--upstream |
localhost:5432 |
Upstream PostgreSQL address |
--policies |
./policies.yaml |
Policy file path |
| Env Var | Default | Description |
|---|---|---|
POLICY_ENFORCEMENT |
monitor |
enforce (block) or monitor (log only) |
| Env Var | Default | Description |
|---|---|---|
DATABASE_URL |
(required) | PostgreSQL connection string |
PORT |
8080 |
Dashboard port |
POLICY_FILE |
./policies.yaml |
Policy file path |
POLICY_ENFORCEMENT |
monitor |
enforce or monitor |
SLACK_WEBHOOK_URL |
— | Slack webhook for alerts |
THROTTLE_ENABLED |
false |
Enable auto-throttling |
RDS_HOURLY_COST |
0.50 |
Hourly DB cost for attribution |
FaultWall exposes an MCP server so AI agents can self-monitor:
{
"mcpServers": {
"faultwall": {
"command": "./faultwall",
"args": ["--mcp"],
"env": { "DATABASE_URL": "postgres://..." }
}
}
}10 tools: list_tenants, get_tenant, get_noisy_tenants, get_costs, throttle_tenant, get_health, get_anomalies, get_predictions, and more.
- SSL/TLS: Proxy mode currently denies SSL negotiation (client retries plaintext). For production with remote databases requiring TLS, use a TLS-terminating proxy in front of FaultWall.
- Identity spoofing:
application_namecan be set by anyone. JWT-based identity attestation is on the roadmap. - Fail-open: If FaultWall's policy engine crashes, the query is forwarded (fail-open for availability). Configurable fail-closed mode is planned.
- Inline L7 proxy mode
- Simple Query Protocol interception
- Extended Query Protocol interception (Parse/Bind/Execute)
- Per-agent, per-mission YAML policies
- 17 blocked PostgreSQL functions by default
- Real-time dashboard
- Monitor mode (sidecar)
- Security profiles (permissive, standard, strict) with custom profile support
- Full SQL parser coverage (115 pg_query_go statement types)
- TLS/SSL passthrough
- JWT-based agent identity
- Connection pooling
- Health check endpoint for proxy
- eBPF kernel-level identity attestation (enterprise)
- MySQL support
- Kubernetes operator
git clone https://github.com/shreyasXV/faultwall
cd faultwall
go build -o faultwall .
go test ./...MIT — see LICENSE.
Built by Shreyas Shubham
faultwall.com · @FaultWall