
Execution-Layer Security now covers one of the riskiest things an agent can touch: your database.
We built AgentSH on a stubborn premise: rules are not enforcement. A CLAUDE.md that says "don't touch production" is a suggestion. A system prompt that says "only run read-only queries" is a hope. Once an agent has a connection string and a reason, that text is no longer load-bearing. Enforcement has to live somewhere the agent can't talk its way past.
AgentSH already provides that boundary for files, processes, and network calls. The database proxy brings it to the data tier: AgentSH now classifies each statement an agent sends to PostgreSQL, evaluates it against policy, and decides — before it reaches your data. This is not another prompt, policy file, or advisory scanner. It's an inline enforcement point on the database wire.
Two failure modes, one connection
When an agent and a database share a blast radius, there are two distinct ways it goes wrong, and a useful control has to handle both.
The accident. The agent is doing exactly what you asked. It's iterating, exploring the schema, "cleaning up test data" — and somewhere in that loop it issues a statement that's correct in syntax and catastrophic in scope. A TRUNCATE meant for staging that hits prod. An UPDATE that lost its WHERE. No malice, just an autonomous process moving faster than your ability to review each step.
The attack. The agent is being steered by someone who isn't you. A prompt-injected instruction in a fetched page, a poisoned document, a malicious tool description — and now the agent is dutifully running a DROP because the text in its context told it to. The agent isn't compromised in the classical sense. It's working perfectly. It's just working for the attacker.
The uncomfortable part is that these look identical at the connection level. Both are a legitimate session sending a legitimate-looking query. You can't separate them by authenticating the connection harder, because the connection was never the problem. The statement is. Firewalls can see the path. IAM and access brokers can see identity. Database GRANTs can see roles and objects. But none of them reliably answer the agent-specific question: should this exact statement, from this exact agent session, run right now?
Why the tools you already run don't close this
You'll reach for controls you have. They each cover part of the gap:
- Network rules and firewalls decide whether the agent reaches the database host and port. They cannot see whether the query inside that stream is
SELECT 1orDROP TABLE customers. - Database GRANTs and row-level security are real enforcement, and you should keep using them. They're excellent at defining what a role may do over time. They're less good at deciding whether this specific statement, in this specific agent session, should run right now.
- "Don't touch the database" in the prompt is, again, not enforcement — it fails the instant an attacker controls the prompt.
There's a deeper distinction underneath. Human database-access tools — Teleport, Boundary, IAM DB auth — answer an identity question: who is connecting, from where, under what role? An agent is a delegated, probabilistic actor that may be operating under prompt injection or task drift, so identity isn't enough. The path needs to answer a second question: what is this agent trying to do right now? The proxy exists to answer that second question, and it's designed to sit on top of your GRANTs and roles, not replace them.
What shipped
The agent issues an ordinary PostgreSQL connection — same DSN, same drivers, same client libraries, no SDK to adopt and nothing to point at a different host. AgentSH transparently steers the connect through its proxy using the same connect_redirect mechanism it already uses to enforce outbound network policy: the TCP connect to the declared database upstream is rewritten to a per-session Unix socket where the proxy listens, and the proxy verifies the peer's process identity via SO_PEERCRED against AgentSH's session registry. Direct TCP egress to the database from anywhere outside the proxy's session is denied at the network layer, so the proxy isn't an opt-in step in the path — it is the path, for any process inside the governed tree.
From the database side, that path is an inline decision point: the proxy speaks PostgreSQL wire protocol v3, parses each statement into a list of effects, evaluates every effect against policy, and decides before anything reaches the database. The postgres and aurora_postgres dialects are fully supported, with redshift and cockroachdb in beta.
Each rule resolves to one of these decisions:
allow— forwarded untouched.deny— rejected before it reaches the database; the agent gets a clean protocol-level error, not a crash.approve— held pending human sign-off, then released or rejected. AgentSH's approval manager supports TOTP and WebAuthn; the default approval timeout is 60 seconds, sized to be safe inside an open transaction.audit— allowed, logged as a structured event.redirect— read-only steering metadata (see the note below on its current status).
An important detail for writing policy: the database evaluator is order-independent. Unlike AgentSH's file and command rules, DB rules are evaluated collect-all — every covering rule is considered, and the most-restrictive verdict wins. Any matching deny wins, regardless of rule order. And coverage is strict: every object an effect touches must be covered by some rule, or the statement is implicitly denied. You don't sequence rules to stay safe; you just write them.
The controls, concretely
Because the proxy classifies statements into effects, policy can decide on the dimensions that matter for database safety:
Operation class. Reads, writes, and schema changes aren't the same risk. Policy can allow reads, approve writes, and deny schema-destroying operations like DROP and TRUNCATE — using operation tokens (READ, UPDATE, DELETE, DROP, the DANGEROUS bundle, and others) rather than three separate database roles.
Object and schema scope. Rules target specific schemas, syntactic object names, or catalog-resolved relations and functions. A READ on public.users can be allowed while writes elsewhere require approval.
Whereless mutations. require_where. An UPDATE or DELETE with no WHERE is the most common way agents (and humans) destroy data by accident. A rule with require_where: true covers a mutation only when it carries a syntactic WHERE clause — so you allow the bounded form and let strict coverage's implicit deny catch the whereless one. It's a syntactic guard, not a proof of selectivity: WHERE true satisfies it, so pair it with database-native constraints for real predicate enforcement.
Bulk export. A COPY ... TO that pulls a table out is how a "read-only" agent becomes a data breach. The classifier flags bulk-export effects as critical-tier, so policy can deny or gate them distinctly from ordinary reads.
Audit. Every decision is emitted as a structured event tied to the agent session and process context. By design, connection strings are parsed and their embedded credentials discarded at classification time — secrets never land in the event payload. Statement text logging is configurable (none, parameters_redacted, or full), defaulting to redacted. These events flow into AgentSH's telemetry pipeline and on to Watchtower, our commercial control plane. (A dedicated OCSF projection for database events is on the roadmap; today they use AgentSH's native DB event schema.)
A note on redirect
redirect is the most interesting capability: rather than only blocking a risky read, policy can express that it should be steered somewhere safe — a sensitive query pointed at a redacted view instead of denied outright. It is read-only by design: it will not reroute writes. Today the policy engine accepts and validates redirect rules, while runtime enforcement is next on the roadmap. We'd rather tell you that than imply your queries are already being transparently rewritten.
What it looks like in policy
These rules use the same fields and tokens as the proxy's own sample policy. A service is declared, then statement rules govern it:
db_services:
appdb:
family: postgres
dialect: postgres
upstream: db.internal:5432
tls_mode: terminate_reissue
database_rules:
# Reads are fine on appdb
- name: app-read
db_service: appdb
operations: [READ]
decision: allow
# Bounded updates are fine; they must carry a WHERE to be covered at all
- name: app-bounded-updates
db_service: appdb
operations: [UPDATE]
require_where: true
decision: allow
# Deletes are riskier — cover only bounded deletes, and route them past a human
- name: app-bounded-deletes
db_service: appdb
operations: [DELETE]
require_where: true
decision: approve
message: "Agent wants to DELETE on appdb: "
# Destructive schema changes and bulk export are off the table
- name: app-deny-dangerous
db_service: appdb
operations: [CREATE, DROP, ALTER, TRUNCATE, EXPORT]
decision: deny
deny_mode_in_tx: terminate
message: "Agent is not allowed to perform on appdb"
# Catch-all: anything the classifier can't read fails closed
- name: catch-all-unknown
operations: [unknown]
decision: deny
message: "Statement could not be classified. Failing closed."
You can check how a given statement would be judged without sending it anywhere:
agentsh policy db explain ./policy.yaml --service appdb --sql 'SELECT * FROM users'
Because evaluation is collect-all with any-deny-wins and strict coverage, the recommended posture is default-deny: allow the safe paths, approve the risky ones, and let everything uncovered fall to implicit deny.
What it governs today, and what's next
We'll be straight about the boundary, because the people who adopt execution-layer security are the people who distrust a pitch with no edges.
- PostgreSQL and its family. The proxy speaks PostgreSQL wire protocol v3, with
postgresandaurora_postgresfully supported andredshiftandcockroachdbin beta. More databases are on the way; the architecture — classify into effects, evaluate, decide — is protocol-agnostic by design. - A boundary, layered on your existing controls. This is a policy decision point on the wire, not a replacement for database GRANTs and row-level security. Keep those — the proxy is strongest as the runtime, statement-level layer on top of them, answering the question they can't: should this statement, from this agent, run right now? Its enforcement holds for any session that goes through the proxy, which AgentSH's process, network, and file primitives are built to guarantee for the governed process tree.
Two capabilities are coming that make the boundary even sharper. Runtime redirect will transparently steer a flagged read to a redacted view, turning "deny and let the agent improvise" into "succeed safely." And a credential broker will hold the upstream credential so the agent authenticates to the proxy, never to the database — taking the production secret out of the agent's hands entirely. Today the proxy governs what an agent can do, statement by statement; these extend it to govern what it can reach and what it ever holds.
Get it
AgentSH is open source (Apache-2.0). The database proxy is available now.
- Code, config reference, and changelog: github.com/canyonroad/agentsh
- Project site: agentsh.org
Agents need database access. Prompts aren't enforcement, and GRANTs are necessary but weren't designed to make agent-aware, per-statement decisions at runtime. AgentSH now puts a policy decision in the database path — so an agent can do the data work you asked for, statement by statement, under rules it can't argue with.
Pull the release, run an agent under AgentSH against your normal database DSN, and try to make it do something it shouldn't. Then tell us where it bites. That's how the last several releases got good.
← All postsBuilt by Canyon Road
We build Beacon and AgentSH to give security teams runtime control over AI tools and agents, whether supervised on endpoints or running unsupervised at scale. Policy enforced at the point of execution, not the prompt.
Contact Us →