New
Apps 1,895Makers 1,890This week 1,690
Promote your app−30%
QueryHub screenshot 1
1views in
the last 30 days

QueryHub

Run SQL on production databases with approval and a full audit. Users submit from Slack or a web...

Built with Claude Code

by ahmetrende · Unclaimed · added Oct 29, 2026
no ratings0 comments

QueryHub

QueryHub

Per-query approval for production databases. Developers never hold a production credential. They submit a statement, and a DBA approves that statement. QueryHub then executes it under a role that matches its classification: RO, RW or DDL. It masks the result, delivers it, and audits every decision. Apache-2.0, no enterprise tier.

Python 3.11+ Engines Changelog

Two surfaces over one core: /sql in Slack, or the web UI. Engines: PostgreSQL, SQL Server, Amazon Athena and ClickHouse. Athena and ClickHouse are read-only.

A pending RW request in the approval queue: the SQL, its classification, the reason, and Approve / Reject / Request changes
The product in one screen: a developer's write is pending. The DBA sees the exact statement, its classification, what it will touch, and why the developer asked for it. Then the DBA approves that statement, not a session.

Status: experimental / early. This project runs in production for its author, but the public release is young. Interfaces, schema, and defaults may change, and hardening continues. Run it behind your own network controls. Do not expose it to the internet. See docs/KNOWN_LIMITATIONS.md for an honest inventory and ROADMAP.md for what's next.

The problem this solves. In a typical engineering org, the DBA team is the bottleneck for ad-hoc data questions. Examples: "why is this user missing?", "how many of X happened yesterday?", "I need a CSV of all Y for the report". Each question is a Slack DM or a ticket, and each one costs a context switch. The data path is informal and unaudited. Over time, access escalates as developers accumulate long-lived read-only roles "just for this one report".

This bot is the paved path. Developers submit, and an admin approves. The bot executes against the right credential tier, and the results return as a CSV in a DM. Every step is in audit_log. Production access stays narrow, and ad-hoc work no longer blocks the DBA's calendar.

How this compares

Two products solve adjacent problems. If you know either one, you should be able to tell in twenty seconds whether this one is redundant.

QueryHub Bytebase Teleport
What it governs one statement at a time schema changes (migration review is the product) access to a session — it brokers a connection and records the transcript
Reads your SQL? yes — classifies each statement RO/RW/DDL and picks the credential to match yes, for migration review no — it cannot tell a SELECT from a DROP
Developer holds a credential? never for ad-hoc query, usually yes yes — a short-lived one, but it is theirs
Approve one query the whole model secondary to change management not a concept
Where approval happens Slack DM or the web panel, and the result arrives as a CSV web console web console / CLI
Cost of the approval workflow Apache-2.0, all of it custom approval flows are Enterprise access requests are Enterprise

In short: the alternatives charge for the two features that this project is entirely about — per-query approval and a custom approval workflow.

Where you will lose by choosing this

Here are the losses, stated plainly. The wins above are only worth reading if the losses are here too:

  • No schema-migration pipeline, no GitOps. If reviewing and shipping migrations is your problem, Bytebase is built for it, and this project is not.
  • Four engines. PostgreSQL, SQL Server, and Athena and ClickHouse (both read-only). Bytebase supports more than a dozen.
  • No HA. One process, one host. See docs/KNOWN_LIMITATIONS.md.
  • No infrastructure access. Teleport governs SSH, Kubernetes and more. This project governs SQL statements and nothing else.
  • One maintainer, against funded companies. The status banner is not false modesty.

Why this shape is about to matter more

People give agents database credentials right now, because that is the easiest way to let an agent answer a question. The problem is not new, and the fix is not new either:

  1. A caller that should not hold a credential submits a statement.
  2. A human approves that statement.
  3. The statement runs under a role that matches what it actually does.

QueryHub already does that, for humans.

Extending it to agents is a small addition, not a new product. The addition is a principal kind for non-human callers, a hard read-only ceiling for them, and their prompt recorded next to the SQL. It is on the roadmap (P3, agent access), but it is not built yet. This section is positioning, not a feature claim. Note what it is not: QueryHub would govern the SQL that an agent writes. It would not write SQL from natural language.

Not this project's job

QueryHub is not an ORM, a BI tool, or a general query IDE. It has no dashboards, no charts, and no scheduled reports beyond "run this statement later". If you want to explore data, use Metabase. QueryHub is the gate in front of the database. It is not a tool to explore the data.

Engines

An engine is in one of three states. Today, every engine that QueryHub names is in the first state. The second state is the one that a new engine passes through.

Engine State What that means
PostgreSQL Executes Full three-tier model: RO / RW / DDL, per-tier credentials, streamed results, EXPLAIN pre-flight, PII lineage from the planner. It is the reference engine.
SQL Server Executes Same model. T-SQL safety dialect. QueryHub refuses cross-catalog and linked-server references. AG read-routing for RO.
Amazon Athena Executes, read-only For data that is now in object storage, not in the database. No host and no stored credential: the gateway assumes a role. QueryHub accepts only SELECT / WITH. On this engine, that rule is what refuses the statements that would WRITE (CREATE TABLE AS, INSERT, UNLOAD, MSCK REPAIR). The schema comes from the data catalog. Every query records what it scanned, because on a pay-per-byte engine the scan is the risk.
ClickHouse Executes, read-only Native protocol over TLS, as a login that the server holds at readonly=1. readonly=1 refuses every setting, so the gateway sends none. The gateway enforces the time limit itself: it closes the connection at the deadline, which cancels the query. QueryHub accepts only SELECT / WITH. Table functions are default-deny (benign generators only). QueryHub refuses dictionary, remote and file reads. system.* and information_schema are readable. The schema catalog never wakes a service that idles to save compute.
A new engine Parses, refuses to run A safety spec, but no execution path yet. QueryHub rejects a target tagged with it at execution time, and does not silently run it through the PostgreSQL driver.
Anything else Not started No spec, no driver. Nothing to configure.

That middle row is deliberate, not an unfinished corner. Connecting psycopg to a non-PostgreSQL server would "work" often enough to be dangerous. It would also skip the safety dialect of that engine. So a statement that PostgreSQL rules classify as read-only could be a write under the rules of the target. QueryHub refuses an engine that it can parse but cannot execute. This is the same discipline as refusing SQL whose meaning is ambiguous: if QueryHub cannot make the guarantee, the query does not run.

Adding an engine is a spec in engines.py — sqlglot dialect, banned leading keywords, driver — plus a resolver in the lineage and statement-count seams. It is not a fork.

Screenshots

All of these are the shipped UI, rendered from the design prototype's mock data. No real connection, person or query appears in any of them.

Submit a query, see it auto-approved and masked, then approve a write as the DBA
The whole loop: you run a read-only query. QueryHub auto-approves it within your grant and masks PII in the result. Then you switch to the DBA side, review a pending write, and approve it. The audit log records the decision.

QueryHub web — workspace
The workspace: your connections by tier (RO/RW/DDL), recent history, saved queries, and open sessions — everything in one place.

QueryHub web — SQL editor with masked results
You write SQL against a tier-matched connection and run it. QueryHub masks PII columns in the result and audits every run.

QueryHub web — grants, per subject and connection
The three-tier model, made visible: who holds RO / RW / DDL, on which connection, scoped to which databases. A team grant and a user grant read the same way. The user grant wins.

QueryHub web — auto-approve grants, time-limited and row-capped
The controlled bypass: a trusted subject can skip review for reads that stay inside a row cap and an expiry. Both bounds are deliberate. An exemption with no time limit is a permanent grant with extra steps.

QueryHub web — audit log
Every decision, attributed and recorded — approvals, rejections, auto-approvals, grant changes, and queries that the safety pass refused.

QueryHub web — light theme, SQL Server schema browser
Light theme, with a SQL Server schema in the schema browser. QueryHub supports PostgreSQL and SQL Server behind one pluggable engine layer.

The /sql modal in Slack: server, database, SQL, justification The approval card in Slack: the statement, its tier, the reason, and Approve / Reject / Request changes
The same two moments in Slack, for teams that prefer not to open a web app. They are /sql to submit, and the card that a DBA acts on. The card shows the statement, its classification, and why the developer asked for it. When the DBA approves, the statement runs. The result returns as a file in a DM.

QueryHub web — sign in
Sign in with Slack, or with a local username/password in the vanilla (no-Slack) profile.

What it does

  • Per-statement approval. The DBA approves the exact SQL that runs. QueryHub executes nothing on a credential that the developer could have used themselves.
  • Tier-matched execution. QueryHub classifies every statement as RO / RW / DDL and runs it under the credential for that tier. It re-derives the tier at execution time, so an edited or superseded query cannot execute at a stale tier.
  • PII masked on output. QueryHub masks values as they stream into the result file. It masks by content (IBAN, card, email, phone, national ids — checksum-validated, so false positives stay near zero). It also masks by column-name catalog, for free-text PII with no detectable shape. This is accidental-exposure mitigation, not a data boundary: the boundary is column privileges and RLS on the database itself. COMPLIANCE.md says exactly where the limits are.
  • Two surfaces, one core. /sql in Slack and the web UI drive the same submit → approve → execute → audit path. Approve wherever the interrupt arrives. The result returns as CSV/XLSX either way. A third, optional door lets a trusted portal call the same API (below).
  • Auto-approve, deliberately narrow. Read-only requests can run instantly under a per-user, time-bounded, tier- and target-scoped grant. Everything else waits for a human.
  • An audit row for every state change, written in the same transaction as the change. So there is no "it happened but nobody logged it" state.

docs/FEATURES.md has the full feature list. It covers batch submission, CSV bulk import, scheduled execution, schema browser, inline EXPLAIN plans and DDL escalation. It also covers admin scopes, per-team grants, product metrics, ratings, and the full slash-command list.

Try it in two minutes

git clone https://github.com/ahmetrende/database-queryhub.git
cd database-queryhub
docker compose up

Then open http://localhost:8080 in a browser. Sign in as demo-admin / queryhub-demo, or as demo-dev for the developer's view.

You get three containers: the app, its metadata database, and a throwaway "production" Postgres. That Postgres holds a seeded shop schema (10k users, 24k orders, 40k events) plus the three per-tier login roles. The demo developer holds an RO grant. Try this:

  1. Submit SELECT id, email, phone FROM users LIMIT 10;.
  2. Approve it as demo-admin.
  3. Watch the result arrive with the email and phone masked.
  4. Try an UPDATE. Watch QueryHub refuse it, because it exceeds the grant.

Demo only. It has fixed passwords, a generated master key with no custody plan, and plain HTTP on localhost. It also relaxes target_ssl_mode to prefer, because the demo target has no TLS. Use it only to try QueryHub. Do not run anything real on it. The install below is for real use.

Prerequisites

Before you install, you need:

(Optional) Slack workspace Only for the Slack surface (/sql + Slack approvals/DMs). You need admin access to create / install a custom Slack app with Socket Mode + Bot Token + App Token. Without it, QueryHub runs web-only with built-in local accounts — the vanilla profile (see below)
PostgreSQL — bot metadata DB A Postgres instance that can give the bot one dedicated DB (queryhub by default). Hosting agnostic: RDS, self-managed, or local for dev
At least one target DB The cluster(s) that developers query — PostgreSQL or SQL Server. Per-tier login roles: queryhub_ro / _rw / _ddl on Postgres, matching logins on SQL Server
Linux host A small VM or container that can run Python 3.11+ continuously and reach Slack + your DBs. The docs describe the install path for systemd. A different supervisor works fine
Python 3.11+ Plus python3.11-venv, libpq-dev, git
(Optional) Web UI To expose the web surface, run python -m queryhub.web (FastAPI/uvicorn) behind TLS. Serve the QueryHubWeb/ bundle. Web login is either Slack OIDC (a Slack app's client id/secret) or built-in local accounts (username/password, no Slack)
(Optional) SQL Server driver For SQL Server targets: Microsoft ODBC driver (msodbcsql18) + the mssql extra — pip install '.[mssql]' (pulls pyodbc)
(Optional) ClickHouse driver For ClickHouse targets: the clickhouse extra — pip install '.[clickhouse]' (pulls clickhouse-driver). Native protocol over TLS, port 9440
A Postgres superuser (or rds_superuser) for bootstrap deploy/setup_db.sql uses it once, to create the bot's metadata DB and login role

docs/PREREQUISITES.md has the full pre-install checklist: Slack app scopes, per-target role provisioning, network rules, and optional integrations.

Install

Run two commands. Then do everything else in the UI.

cp .env.example .env && $EDITOR .env     # metadata DB + first admin
docker compose -f docker-compose.install.yml up -d

The first up builds the image from this checkout. That takes a few minutes, once. This default is deliberate: an approval gateway should not change what it runs because a shared tag moved. To skip the build, pull a released image. Then name it in the up command:

docker pull ghcr.io/ahmetrende/database-queryhub:1.0.38
QH_IMAGE=ghcr.io/ahmetrende/database-queryhub:1.0.38 docker compose -f docker-compose.install.yml up -d

Every release publishes its own immutable tag alongside :latest. Reference the version, not latest, in anything that you deploy.

Four values in .env matter. BOT_DB_HOST / BOT_DB_PASSWORD are for the metadata database. QH_ADMIN_USER names the

Description taken from the repository README.

Ratings

–
0 ratings
5 0
4 0
3 0
2 0
1 0

Rate this app

Sign in to rate

Comments 0

Sign in to comment

No comments yet.

Related apps

#sql · #database · #audit