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):

  1. Zero code changes: Point your existing client (psycopg2, pgx, SQLAlchemy, etc.) to :5433 instead of :5432 without changing a line of database code.
  2. 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.yaml file so runaway agent loops get throttled locally.
  3. The Guillotine: Safe SELECT queries are forwarded over TLS to your database (e.g., Neon Postgres or RDS). If Aegis spots a DROP TABLE or a DELETE without a WHERE clause, 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!