Destructive SQL never runs.
An ordered deny list, checked against every statement your agent sends. DROP TABLE, DELETE without a WHERE, TRUNCATE: the sidecar refuses them before your database sees them.
Alerts come after the damage.
Monitoring tells you a table was dropped. It tells you after, at 3:12 AM, once the rows are gone.
An agent does not wait for that window to close. It issues the next statement in milliseconds, then the one after.
- 2:47 AMAlert fires
- 3:12 AMOn-call sees alert
- 3:45 AMEscalation
- 4:15 AMAccess revoked
88 minutes to revoke access.
You write what is forbidden.
A guardrail is a rule in your config file. The sidecar checks every statement against the list before passing it upstream. A statement that matches never reaches your database.
An ordered list
Rules run in the order you wrote them, against every statement. The first match decides. No scoring, no ambiguity, same answer every time.
Operations, not string matching
A rule names the operations it refuses: drop, delete, truncate. It reads the parsed statement, so it catches a variant a regex would miss.
Your message, on the wire
You write the refusal text. The client receives it as a real protocol error, not a dropped connection.
Named rules
Every rule carries a name, and the name travels with the refusal. The log and the client cite the same rule.
Nothing reaches the database.
Pick a statement your agent sends and read what comes back on the wire.
Parsed as drop, listed as drop. The first match decides.
policy.rules
- name: no-destructive-sql
type: operation
operations: [drop, delete, truncate]
message: destructive statements are not permittedagent-07 → prod-db:5432
● postgres(DROP TABLE customers)
⎿ FATAL: destructive statements are not permitted
hoop.dev · guardrail: no-destructive-sql
agent-07 reads the refusal. No retry, no stall.> clean up the old test rows in customers
● postgres(DELETE FROM customers WHERE id = 1)
⎿ FATAL: destructive statements are not permitted
hoop.dev · guardrail: no-destructive-sql
Refused, and the agent kept going.
A blocked statement comes back as a real pgwire error carrying the message you wrote. The agent reads why the sidecar refused it instead of guessing at a dropped connection.
So it does not retry the same statement, and it does not stall. It finds a compliant path on its own.
Four controls, one sidecar.
Data Masking
Rewrites sensitive values in the response, in memory, before they reach the client. The request itself is never touched.Guardrails
An ordered deny list checked against every statement. Destructive ones never reach the database, and the client reads the reason you wrote.Session Analyzer
An agent scores every action’s intent and syntax for risk before it executes.Human in the Loop
Risky operations pause for a person’s one-off approval, then the agent carries on.
One rule is enough to start.
Name the operations you never want an agent to run, write the message it should read instead, and start the sidecar. Free and open source under MIT.
Ask an assistant how Guardrails works: Claude, ChatGPT, Grok, Gemini, Perplexity, Copilot.
# config.yaml
policy:
enforce: true
rules:
- name: no-destructive-sql
type: operation
operations: [drop, delete, truncate]
message: destructive statements are not permitted
$ hoop start sidecar --config config.yaml
sidecar running