If you've ever wired an LLM agent or a text-to-SQL script up to a Postgres database, you know the anxiety: one bad hallucination and the model runs a DROP TABLE, wipes rows with an unconstrained DELETE, or gets stuck in a loop that burns through your compute budget.
Most teams try to fix this with regex filters or by asking a second LLM to check the query first. In practice, regex breaks the moment a query has inline comments or nested CTEs, and using an LLM-as-a-judge adds hundreds of milliseconds of latency while still being non-deterministic.
I wanted a deterministic guardrail at the network layer, so I built Aegis—an open-source Layer-4 Postgres proxy written in Go.
Why WebAssembly (wazero) Instead of cgo?
To parse Postgres queries properly, you really want Postgres's actual C parser (libpg_query). The problem is that calling C from Go requires cgo, which makes cross-compiling painful, adds cgo boundary overhead under concurrency, and pushes memory management outside the Go runtime.
To keep Aegis 100% pure Go, it runs libpg_query compiled to WebAssembly via wazero (wasilibs/go-pgquery). You get a single static Go binary with zero C dependencies, while still parsing the exact same Abstract Syntax Tree (AST) that Postgres uses internally:
package guardrail import ( pg_query "github.com/wasilibs/go-pgquery" ) func ValidateQuery(rawSQL string) error { tree, err := pg_query.Parse(rawSQL) if err != nil { return err // Block malformed SQL by default } for _, stmt := range tree.GetStmts() { node := stmt.GetStmt() if node.GetDropStmt() != nil || node.GetTruncateStmt() != nil { return ErrDestructiveQueryBlocked } if deleteStmt := node.GetDeleteStmt(); deleteStmt != nil { if deleteStmt.WhereClause == nil { return ErrUnconstrainedDeleteBlocked } } } return nil } How the "SQL Guillotine" Works on the Wire
Aegis sits between your app and your Postgres database, listening on port :5433 for TCP traffic (and :5434 for admin and metrics):
- Zero code changes: Point your existing client (
psycopg2,pgx, SQLAlchemy, etc.) to:5433instead of:5432without changing a line of database code. - AST inspection and local rate limiting: Aegis parses incoming query packets in memory using the Wasm AST parser and enforces local RPM rate limits from a
policies.yamlfile so runaway agent loops get throttled locally. - The Guillotine: Safe
SELECTqueries are forwarded over TLS to your database (e.g., Neon Postgres or RDS). If Aegis spots aDROP TABLEor aDELETEwithout aWHEREclause, it drops the connection immediately and returns a fatal error packet to the client. The database never even sees the query.
Try It Out
The proxy and companion Python SDK (aegis-sdk on PyPI) are open-source (AGPLv3) and run completely offline in Docker with total data privacy.
GitHub: https://github.com/raymondhani/aegis-proxy
If you work with Go or Postgres internals, I'd love for you to take a look and let me know if you can think of any weird SQL edge cases that might slip past the AST check!