Skip to content

orgoldfus/queryGate

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

QueryGate

QueryGate is a read-only SQL gateway CLI for AI agents. It parses PostgreSQL SELECT queries with a real SQL AST, validates them against a user-level allowlist policy, and executes approved queries through a read-only database session.

Install

cargo install --path .

Configuration

By default, QueryGate loads configuration from:

  1. ~/.queryGate/config.yaml
  2. ~/.queryGate/config.yml
  3. ~/.queryGate/config

Override with --config <path> or the QUERYGATE_CONFIG environment variable.

Generate a config from an existing database

querygate init --url "postgres://user:pass@host:5432/mydb"

This introspects PostgreSQL metadata and writes ~/.queryGate/config.yaml. It never overwrites an existing file. Use --output to choose a different path.

Options:

  • --database <name> — profile name (default: app)
  • --url-env <VAR> — env var for the runtime connection URL (default: QUERYGATE_APP_DATABASE_URL)
  • --default-schema <schema> — default schema for unqualified names (default: public)
  • --include-schema <schema> — repeat to include additional schemas
  • --suggest-safe — opt in to marking columns that do not look sensitive as allowed

Set the connection URL after init:

export QUERYGATE_APP_DATABASE_URL="postgres://querygate_app:...@host:5432/mydb"

For databases that require TLS, include the standard Postgres SSL mode in the connection URL:

export QUERYGATE_APP_DATABASE_URL="postgres://querygate_app:...@host:5432/mydb?sslmode=require"

If you need the Node.js-style rejectUnauthorized=false behavior for a database with an untrusted or mismatched certificate, QueryGate accepts that URL parameter and disables TLS certificate verification for that connection:

export QUERYGATE_APP_DATABASE_URL="postgres://querygate_app:...@host:5432/mydb?sslmode=require&rejectUnauthorized=false"

Only use rejectUnauthorized=false for trusted networks or development databases. It keeps the connection encrypted, but it disables verification that the server certificate belongs to the database host.

Policy format

Example ~/.queryGate/config.yaml:

version: 1

databases:
  app:
    description: Safe production app data for support agents.
    url_env: QUERYGATE_APP_DATABASE_URL
    dialect: postgres
    statement_timeout_ms: 10000
    max_rows: 1000
    policy:
      default_schema: public
      tables:
        public.users:
          access: allowed
          default_column_access: denied
          columns:
            id:
              access: allowed
            created_at:
              access: allowed
            country:
              access: allowed
        public.orders:
          access: allowed
          default_column_access: allowed
      functions:
        - count
        - sum
        - avg
        - min
        - max
        - date_trunc

Access rules:

  • Tables and columns not listed under policy.tables are denied.
  • Missing access defaults to denied.
  • Missing default_column_access defaults to denied.
  • A table is queryable only when access: allowed.
  • A column is queryable when the table is allowed and either the column has access: allowed or default_column_access: allowed.
  • SELECT * is allowed only when the table is allowed and default_column_access: allowed.
  • Functions are denied unless listed under policy.functions.

querygate schema shows agents the effective allowlist only: denied tables and columns are omitted.

See examples/querygate.yaml for a multi-database sample.

Usage

# List configured databases
querygate databases

# Inspect allowlist for a profile
querygate schema --database app

# Validate without executing
querygate validate --database app --sql "select id from public.users"

# Execute and return JSON rows
querygate run --database app --sql "select id, created_at from public.users limit 10"

# Pipe SQL from stdin
querygate run --database app < query.sql

If only one database profile is configured, --database may be omitted.

Output

Successful execution:

{
  "ok": true,
  "columns": ["id", "created_at"],
  "rows": [{ "id": 123, "created_at": "2026-05-19T10:30:00Z" }],
  "row_count": 1,
  "truncated": false,
  "database": "app"
}

Validation failure (stderr):

{
  "ok": false,
  "error": {
    "code": "column_not_allowed",
    "message": "Column public.users.email is not allowed by the policy.",
    "hint": "Use `querygate schema --database app` to see allowed tables and columns."
  }
}

Exit codes

Code Meaning
0 Success
2 Config error
3 SQL parse error
4 Policy validation error
5 Database execution error

PostgreSQL setup

Create a dedicated read-only role per database profile:

CREATE ROLE querygate_app LOGIN PASSWORD '...';
GRANT CONNECT ON DATABASE mydb TO querygate_app;
GRANT USAGE ON SCHEMA public TO querygate_app;
GRANT SELECT ON public.users TO querygate_app;
GRANT SELECT ON public.orders TO querygate_app;
-- Prefer column-level grants or safe views for PII tables

Set the connection URL:

export QUERYGATE_APP_DATABASE_URL="postgres://querygate_app:...@host:5432/mydb"

Security model

QueryGate reduces PII exposure for AI agents, but it is not a substitute for database permissions.

  • Use a dedicated read-only PostgreSQL role per profile.
  • Grant only CONNECT, USAGE, and SELECT on approved schemas/tables/views.
  • Mirror the allowlist with PostgreSQL column privileges or safe views where possible.
  • Prefer pre-aggregated views for analytics instead of raw customer tables.
  • Agents can still infer sensitive facts from allowed aggregate columns or low-cardinality filters.
  • Query logs may contain sensitive literals even when selected columns are safe.

Application-side safeguards:

  • AST-based validation (not regex).
  • Single SELECT statement only.
  • Read-only transactions (BEGIN READ ONLY).
  • statement_timeout per profile.
  • Automatic outer LIMIT when the query has no LIMIT.

Development

cargo test
cargo build --release

Optional database integration test (requires a reachable PostgreSQL instance):

export QUERYGATE_TEST_DATABASE_URL="postgres://..."
cargo test -- --ignored

License

MIT

About

Safe read-only SQL gateway for AI agents

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages