Inspiration

AI agents are becoming capable of querying and changing business data, but giving an agent a database connection is still too much power. A prompt can say “only access this customer’s records,” yet the database connection may still allow another tenant’s rows, unauthorized fields, unsafe SQL, or an oversized mutation.

We wanted to build a boundary that is enforced by software rather than by instructions. That became SentiQL: a semantic firewall for AI-agent access to PostgreSQL.

What it does

SentiQL gives agents governed business capabilities instead of unrestricted SQL:

  • data.read for bounded, tenant-scoped reads
  • data.aggregate for approved metrics and grouping
  • data.mutate for controlled updates with row limits and approval gates
  • schema.discover for safe resource metadata

For every request, SentiQL:

  1. Verifies workload identity through OIDC.
  2. Checks purpose, resource, fields, tenant scope, selectors, and limits against a versioned policy.
  3. Compiles only bounded parameterized SQL.
  4. Uses PostgreSQL Row-Level Security as the final tenant boundary.
  5. Records decisions, policy hash, correlation ID, and database outcome in a local SQLite audit log.
  6. Optionally evaluates generated SQL with a conservative, fail-closed PostgreSQL AST policy.

The offline demo shows the real enforcement path in seconds:

[ALLOW] Authorized tenant-scoped read
[DENY] Unauthorized field request
[APPROVAL_REQUIRED] Sensitive mutation requires approval

How we built it

We built SentiQL as a Node.js MCP server. The MCP tools expose typed capabilities rather than a general-purpose query endpoint. A JSON policy bundle defines resources, readable and writable fields, purposes, tenant scope, mutation actions, and approval rules.

The server authenticates the workload, authorizes the semantic request, compiles a constrained SQL artifact, and executes it using a least-privilege PostgreSQL role. PostgreSQL RLS receives the verified tenant context inside the transaction, so the database remains a defense-in-depth boundary even if an upstream layer fails.

We also built an AST policy experiment using @pgsql/parser. It is intentionally conservative: unknown syntax, unsafe functions, trivial predicates, nested writes, context mutation, parser errors, and unsupported versions fail closed. The differential harness covers 47 cases across PostgreSQL parser versions 13–18 and found zero safety-sensitive ast_allow_heuristic_deny widenings in the captured matrix.

The dashboard is an Express audit console that shows decisions and bounded AST shadow observations without exposing raw SQL, request values, tokens, or result rows.

To run the deterministic offline demo:

npm install
npm run policy:simulate -- --demo --pretty

The demo does not require Docker, PostgreSQL, an OIDC provider, or network access. It uses local stubs only for repeatability; production uses real OIDC verification and PostgreSQL RLS.

Challenges we ran into

The hardest challenge was deciding what an AST can safely prove. It is tempting to treat a parsed SELECT as safe, but syntax alone cannot prove database permissions, search_path, extensions, RLS configuration, schema drift, or function side effects. We therefore made the AST path fail closed and kept the authoritative policy behavior isolated until the evidence was strong enough.

We also had to prevent identity and tenant spoofing. Subject, organization, tenant, and roles are never accepted from tool arguments; they come from the verified workload identity. SQL literals and request values are redacted from audit output, while SQL compatibility calls are represented by digests.

Finally, we had to make the demo useful without requiring a complete cloud identity and database environment. The offline demo exercises the real policy enforcement seam with deterministic local collaborators while clearly separating that from production infrastructure.

Accomplishments that we're proud of

  • Built an MCP-native semantic authorization layer instead of exposing raw SQL to the agent.
  • Added tenant isolation at both the policy layer and PostgreSQL RLS layer.
  • Added field-level permissions, purpose binding, row limits, and approval-required mutations.
  • Implemented fail-closed handling for malformed input, missing identity, unsafe SQL, audit failures, parser errors, and timeouts.
  • Added privacy-safe audit records with policy hashes, correlation IDs, decisions, and database outcomes.
  • Built a read-only AST shadow review path and a differential test harness across parser versions 13–18.
  • Verified the project with 287 automated tests passing and no failures.
  • Created a reproducible demo that shows allow, deny, and approval outcomes without external dependencies.

What we learned

We learned that safe agent-to-database access is not one filter placed in front of SQL. It is a chain of independently useful controls: verified identity, semantic authorization, bounded compilation, database-enforced isolation, approval, and audit.

We also learned to treat “clean” AST results carefully. A parser can describe what the SQL looks like, but it cannot by itself establish that the query is safe in a particular database deployment. Conservative denials and explicit evidence are more valuable than a larger allow list that cannot be explained.

Most importantly, we learned that the best agent tool is not necessarily the most flexible one. A smaller, typed capability surface gives the agent a clearer contract and gives operators something deterministic to review.

What's next for SentiQL

  • Complete a production OIDC integration with managed key rotation and short-lived workload tokens.
  • Expand the typed capability catalog while preserving strict resource and field boundaries.
  • Add a vetted function allowlist and a deliberately designed read-write AST policy path.
  • Run longer shadow-observation pilots with human review gates before considering any enforcement promotion.
  • Add more database-aware validation for RLS, permissions, search_path, extensions, and schema migrations.
  • Package the dashboard and policy review workflow for deployment alongside common MCP hosts.
  • Keep the protocol client-neutral so the same governed boundary can serve Codex and other agent clients.

Built With

Share this project:

Updates