Quack on Demand

The database you can hand an agent the keys to.

Quack On Demand is a governed SQL gateway over autoscaling fleets of DuckDB nodes. Agents connect over MCP with scoped, revocable tokens; humans and BI tools over Arrow FlightSQL. Every statement, from either, goes through the same enforcement path: ACLs, row policies, column masks, budgets, and a kill switch.

View on GitHub
Scoped tokens

The agent never holds a database credential. Tokens are minted narrower than their owner: databases, verbs, row ceilings, timeouts, budgets, expiry. Tokens mint tokens, each a strict reduction.

ACL & masking, one path

Table-level ACLs, row-level policies, and dynamic data masking enforced on every statement, whether it arrives over MCP or FlightSQL. No "agent mode": same code, same executor.

Killable & audited

Every statement is attributed to the acting token, live-killable, and audited. Revoking a token kills its live statements and every child it minted, in one call.

Autoscaling & your IdP

Fleets of DuckDB Quack nodes autoscale on your Kubernetes, or run as one Docker container. Sign-in through your IdP: OIDC SSO (Keycloak, Google, Azure AD, Cognito) with SCIM provisioning.

Quack On Demand admin console - live per-node metrics, statement history, ACL editor

The agent-ready database

Built for the newest kind of database user.

An agent writes arbitrary SQL like an analyst, issues it at machine speed like an application - and nobody reads it before it runs. Quack On Demand is designed for exactly that caller: the policy lives on the credential, not in the prompt, because grants are the only thing an injected instruction cannot argue with.

πŸ€–

MCP and ADBC

Agents connect to POST /mcp with a bearer token and a small tool surface shaped for how agents work: list_tables, describe_table, run_sql, time travel, my_usage. Applications and BI tools connect over ADBC with zero-copy Arrow results. Stateless, so any replica answers any request.

πŸ”‘

The agent never holds a database credential

It holds a personal access token that resolves to a QoD principal - never the object-store keys or catalog credentials. Stored only as a SHA-256 hash, expiring, revocable in one call without disturbing anyone else.

πŸͺ†

Delegation is always a reduction

Tokens mint tokens, each strictly narrower: databases, verb ceilings, row caps, timeouts, budgets, expiry. An orchestrator hands each sub-agent its own credential without a human in the loop - and revoking the parent revokes the whole chain, atomically.

πŸ›€οΈ

One enforcement path - no "agent mode"

A statement arriving over MCP goes through exactly the same validator, per-table grants, row policies, and column masks as one arriving over Arrow FlightSQL. Same code, same executor. A fully compromised agent still cannot exceed the grants on its credential.

πŸ”Ž

Attributable, killable, audited

Every statement carries the owning human and the acting token. Live statements are killable by id, and revocation kills a credential’s in-flight statements too. Writes stamp their author into the DuckLake snapshot, so lineage is answerable from the data itself.

πŸ’Έ

Budgets and structured refusals

Per-credential rate limits, concurrency caps, byte ceilings, and spend budgets - my_usage tells the agent what remains. Every denial is a machine-readable envelope: a stable code, whether retrying helps, what budget is left. Agents adapt in one turn.

↩️

Undo: agents write to branches

Hand an agent a DuckLake snapshot branch, review its work as a diff against the parent, then merge or discard atomically. Pin a session to a snapshot and the world cannot shift under it mid-investigation. Reversible mistakes are what make agent writes possible at all.

🧭

Context, so agents stop guessing

Business descriptions on tables and columns, curated agent-facing views, worked query examples, and a schema digest sized to a context window. Wrong SQL is a governance problem too: it costs money and erodes trust in the answers.

🧯

Blast radius bounded by architecture

Each database is served by its own pool of processes, isolated per tenant. A runaway cartesian join saturates its own pool - scale, suspend, or kill it without touching anything else. Pools scale to zero when idle. The noisy neighbour is not a neighbour.

And underneath

Everything DuckDB Quack is missing in production.

DuckDB ships Quack as a minimal HTTP endpoint on localhost with a random token, and explicitly recommends a reverse proxy in front of it. Quack On Demand is that proxy - with the multi-tenancy, identity, and observability you need to actually expose it.

πŸ›«

Arrow FlightSQL edge

Zero-copy result streaming over Apache Arrow Flight SQL - orders of magnitude faster than JDBC for analytical workloads. TLS is on by default and a self-signed cert is generated on first boot.

🏒

Multi-tenant pools

Spin up tenants and pools of DuckDB Quack nodes on demand. Each node is READONLY, WRITEONLY, or DUAL - the router classifies every statement and picks a compatible target.

πŸ”

Pluggable authentication

Database (bcrypt-hashed JDBC), external JWT (HS256/RS256/PEM), and OIDC providers - Keycloak (with ROPC), Google, Azure AD, AWS Cognito. Mix and match per deployment.

πŸͺͺ

Enterprise identity - SSO & SCIM

Per-tenant OIDC single sign-on, and a SCIM 2.0 endpoint so Okta, Entra or Google provision, update and deprovision users and groups automatically - no manual account admin, and deactivation cuts query access, not just the console.

πŸ›‘οΈ

Postgres-relational RBAC

Roles, groups and per-table permissions live in qodstate_* tables next to the DuckLake metadata, with column masking and row-level filters rewritten at the edge. Principals expand to user / group / role at validation time so grants match whichever level of identity is stable.

πŸ“Š

Live admin console

React dashboard at /ui/ - tenant + pool CRUD, per-tenant ACL editor, live node metrics (inFlight, totalServed, EWMA latency), admin-role gated.

🦺

Self-healing on restart

Dead Quack child processes are detected (PID + port probe) and respawned automatically before the edge accepts traffic. Manager restarts no longer strand the fleet.

Deployment

Single binary

REST + React UI + FlightSQL edge in one process. State lives in Postgres next to DuckLake - no extra moving parts.

Configuration

Every key is overridable

Every scalar in application.conf accepts a matching QOD_* env-var. Build the image once, flip behavior per environment.

Runtime

Single container or Kubernetes

Single-node mode runs everything in one Docker container - ideal for single tenants, with Quack child processes on a port range. Kubernetes mode runs them as pods. Same control plane, same admin UI.

Query federation

One SQL surface over every source.

Quack On Demand turns DuckDB's federation into a governed, multi-tenant service. Point a query at your lake, your Iceberg warehouse, and your operational databases at once - and join them in place, without moving a byte.

1

Query data where it lives

Object storage, Iceberg tables, and operational databases are read in place. No copies, no nightly ETL, no second engine.

2

Join across sources in one statement

A single SQL query spans Parquet on S3, an Iceberg warehouse, and a Postgres table - DuckDB pushes down filters and streams results back over Arrow.

3

Governed like everything else

Every federated table reference passes the same per-statement RBAC and table-ref policy checks - federation never bypasses your ACLs.

Parquet & CSVApache IcebergDuckLake catalogsPostgreSQLMySQLS3 / GCS / Azure Blob
-- One statement, three sources, zero copies
SELECT o.region,
       c.segment,
       SUM(o.amount) AS revenue
FROM   read_parquet('s3://lake/orders/*.parquet')  o
JOIN   postgres_scan('crm','public','customers')   c
         ON c.id = o.customer_id
JOIN   iceberg_scan('s3://warehouse/products')     p
         ON p.sku = o.sku
GROUP BY 1, 2;

Bring your own pipeline

Works with any ETL, however you built your data.

QoD is a serving layer. It sits downstream of whatever produced your data. It does not care which tool wrote it, and it never routes your users anywhere.

dbt

Materialize models to DuckDB/DuckLake, serve them with QoD.

QoD + dbt recipe β†’

Dagster

Land assets in DuckLake; QoD exposes them to clients.

QoD + Dagster recipe β†’

dlt

Pipe extracted data into DuckDB, query it through QoD.

QoD + dlt recipe β†’

SQLMesh

Build versioned models; serve the outputs over FlightSQL.

QoD + SQLMesh recipe β†’

Spark

Write Parquet/Iceberg; QoD federates and serves it.

QoD + Spark recipe β†’

Plain SQL

Point QoD at any DuckDB file or DuckLake catalog.

QoD + Plain SQL recipe β†’
Open source Β· Apache 2.0

Your agents are going to query production. Decide the terms.

The organisations that let agents in are the ones that can answer, without hedging: what can this agent see, what can it spend, what did it do, and how do I stop it. QoD answers all four - on the same enforcement path as every human caller. Start with one command: uvx qod start --demo, then point your agent at /mcp with a token and ask it something.