| type | reference | |
|---|---|---|
| title | Query Runner | |
| description | Customer-installed endpoint that executes compiled SQL against a local warehouse, under a key, a kill switch, and an audit log. | |
| tags |
|
|
| timestamp | 2026-08-15 |
The Query Runner is a small process the customer installs next to their warehouse. The hosted semantic layer never opens the warehouse. The engine calls this endpoint with a key, sends the SQL it compiled, and reads rows back.
The customer owns four things the hosted semantic layer must not own:
- The warehouse password — it stays in this process, as local config.
- The kill switch — they pause the runner and the engine stops executing, with no credential rotation required.
- The access policy — allow or deny tables, fields, and values on this box, per key.
- The audit log — every SQL statement this process ran, on disk they control.
It is a proxy for execution, not a second semantic layer. It does not compile, does not decide catalog access, and does not speak MCP.
Hosted semantic layer Customer network
───────────────────── ────────────────
console / MCP
│
▼
control plane ──publish──► engine
│
│ HTTPS + runner key
│ POST /v1/sql { sql, args }
▼
query-runner ← you install this
│
│ local DSN
▼
Source (Postgres)
Today the engine fetches a decrypted DSN from the control plane and dials the warehouse itself. That requires the warehouse to accept connections from the hosted service, and it requires the control plane to store the warehouse password. Neither holds up once the semantic layer is hosted and the Source is a customer warehouse the vendor cannot see.
The engine already reaches a warehouse only through execute.Runner. This component is that port, moved into the customer's network.
- Run the binary (or container) where it can reach the warehouse.
- Point it at the warehouse DSN and at least one runner key.
- Expose
https://runner.example.comto the engine (IP allowlist or PrivateLink). - In the control plane, create a Connection of kind
runnerwith that URL, the same key, and the warehouse backend (postgresql). - Turn it off, rotate the key, or read
logs/queries.jsonlwithout talking to the hosted service.
The engine then queries with the key. The runner never receives a Semantic Query — only parameterized SQL.
| Not | Why |
|---|---|
| The semantic Policy Decision Point | Catalog, Policy Roles, and RLS stay in the engine (ADR-0021). The runner's policy is physical: table / field / value, same for every caller of that key. |
| A relocated engine | No compile, grants, MCP, or DuckDB. Smaller blast radius, no CGo. |
| An arbitrary SQL proxy | Only the engine, only with a valid key, only SELECT/WITH. |
| A replacement for local/dev | Direct DSN Connections remain for testdata and a fully self-hosted stack. |
| File | What it is |
|---|---|
| DESIGN.md | Topology, modules, Connection change, phases |
| POLICY.md | Allow/deny language: table, field, value — rules and examples |
| SCALE.md | Files, not a DB; how N replicas share config |
| PROTOCOL.md | HTTP contract the engine and the runner both implement |
| config.example.yaml | Ops + access policy, keys, warehouse, logs |
Options considered (including "keep direct DSN only" and an outbound tunnel) are in docs/research/query-runner.md.
cd query_runner
go test ./...
go build -o bin/query-runner ./cmd/query-runner
./bin/query-runner -config config.example.yaml
QUERY_RUNNER_WAREHOUSE_DSN must be set unless the config uses an inline dsn. Reload with SIGHUP. Pause without a reload: POST /admin/v1/pause on the admin listener.
A folder-local stack (runner + Postgres) lives in this directory only:
docker compose up -d --build --wait
python3 compose/smoke.py
docker compose down -v
The smoke key is smoke-runner-key. The public port is 8443.
Phase 1 binary is implemented in this module: /healthz, /v1/readyz, /v1/sql, /v1/schema, /v1/ping, key auth, kill switch, SELECT-only gate, table/field/value policy, JSONL audit, Postgres. See docs/features/platform/query-runner/feature.md.