Changelog

Every release, and what it changes for you

Keep a Changelog format, semantic versioning. A patch never breaks a covered surface; a minor release may, and always says so under Breaking. Experimental features sit outside the promise and are labelled. The full record, back to 0.1, is in the repository; the policy is on Support and versions.

{{ relStatus }}

Covered by the versionREST routes and shapes, CLI, env vars, on-disk format, grant fields, saved-query format, MCP tool contract, error codes.
Not coveredConsole internals, exact message and log wording, anything in the package not re-exported from queryapigate.
DeprecationsAnnounced only once the successor ships; Deprecation and Link headers, deprecated: true in OpenAPI, a log warning; removed no sooner than the next minor.
0.34.2
Security

Two fixes for MongoDB connections, both affecting 0.9.0 to 0.34.1.

UpgradingNothing to change. To check whether the first issue affected you, look at the rows of each run of a saved mongo query with an undeclared placeholder: a run that returned far more than usual.
  • SecurityA MongoDB parameter could be query operators. For a saved mongo query whose placeholder had no declared type, {"$ne": null} matched every document. A value is now a string, number, boolean, null or a list of those; a document is refused with param_invalid. Typed placeholders were never affected.
  • SecurityServer-side JavaScript in a projection or sort. $where, $function and $accumulator were refused in a filter but not in projection or sort. They are refused there too, and when a saved query is saved.
0.34.1
Security

allowed_tables is now enforced on MongoDB connections (0.11.0 to 0.34.0).

UpgradingNothing to change. To check whether it happened, list GET /api/v1/history?key=<name>&kind=adhoc for each key with allowed_tables and a mongo connection.
  • SecurityA key narrowed to some tables read every collection. On MongoDB the list is now the collections a key may find(): one outside it is refused with 403 table_not_allowed before the server is asked. Keys without allowed_tables, and every SQL connection, were never affected.
0.34.0
StableSecurity

Live events, and secrets from files and Vault, are stable: under the compatibility promise, each after a security review (ADR 0021). AWS secrets and credentials stay experimental until they are tested against AWS.

Upgradingpsycopg2-binary 2.9.5 or later. If a script reads why a secret was unavailable from error, read detail instead. A ${file:...} naming one of the server's own secret files is now refused.
  • ChangedLive events are stable, with the 0.33 payload unchanged. No longer marked experimental in Settings, /openapi.json or the startup log.
  • ChangedSecrets from files and Vault are stable: ${VAR}, ${file:...}, ${vault:...} and vault-database credentials, tested on every push against Vault 1.21.
  • SecurityA new key with a deleted key's name no longer sees the old key's runs. A key now sees no event from before it was created.
  • Security${file:...} could read the server's own secrets where they were files inside an allowed folder. The server's own secret files, service account tokens and /proc are never readable now.
  • Securitysecret_unavailable no longer tells any caller where secrets live. The path, Vault address and answer are only in the log and in detail.
0.33.0

Live events get a log of their own (ADR 0020). Every run is written to the event log in the same batch as its history row, and kept for a set time.

UpgradingSchema 16 adds an events table on first start, copying the last QUERYAPIGATE_EVENTS_RETENTION_HOURS of runs. Update live-event clients for the new payload: check kind instead of "type", and read query instead of filename.
  • AddedThe event log. Every run is an event, including one sampled out of history. Ids are never handed out twice.
  • AddedSubscription filters on both /events endpoints: ?type=, ?kind=, ?query=, ?connection= and ?status=, combined with AND.
  • AddedGET /metrics on the events server, with streams, delivered, replayed, slow disconnects and lag.
  • ChangedThe live event payload: every run is "type": "query.run", with source, occurred_at, kind, query and version.
  • FixedA resume more than 1,000 events back lost the rest. It now replays every page up to the live stream.
0.32.0
Stable

The audit log's stream to a SIEM is stable: tested on every push against rsyslog, syslog-ng and an OpenTelemetry Collector, and a SIEM's own copy of the log can be checked with the same command that checks the store.

UpgradingNothing to change. No schema change, no setting; the stream's messages are exactly as before.
  • ChangedThe SIEM stream is under the compatibility promise. No longer marked experimental in Settings, /openapi.json or the startup log.
  • Changedqueryapigate audit verify --file checks what your SIEM received: syslog lines as a receiver writes them, exact repeats from at-least-once delivery dropped and counted, entries checked in seq order however they arrived. A repeat that differs is reported as conflicting_copies.
  • AddedCI runs the stream against real receivers: rsyslog and syslog-ng over mutual TLS, and an OpenTelemetry Collector over OTLP/HTTP, each read back and verified, including refused certificates and an outage. Guide 53 re-run against them.
0.31.0
Security

The first signed release: the image and the Helm chart, signed keyless with cosign by the release workflow, with SLSA provenance and an SPDX SBOM. What a release is built from is pinned.

UpgradingNothing to change. 0.30 and earlier aren't signed, so a policy that requires a signature refuses them.
  • AddedSigned releases. Every image variant and the chart, signed by digest, in both the Sigstore bundle and the classic .sig form admission controllers read, with a GitHub attestation for each. The workflow verifies them before it ends.
  • Addedimage.digest in the Helm chart, so the image is pulled by the digest you verified.
  • AddedGuide 59: Verify a release before you run it. cosign verify, the attestation, provenance and SBOM, and Sigstore's policy-controller and Kyverno admitting the signed image and refusing an unsigned one.
  • ChangedBuilds are pinned: every GitHub Action by commit SHA, the base images by digest. A test fails on anything unpinned.
0.30.0
StableSecurity

Eight features come under the compatibility promise, each after a security review: descriptions, agent usage, usage quotas, configuration as code, OAuth for the MCP server, Console sign-in through SSO, write tools over MCP and the Helm chart.

UpgradingOn the Helm chart, /metrics now needs a token (metricsAuth, on by default); the ServiceMonitor sends it. In /metrics, administrators are counted as key="admin". No schema change.
  • SecurityA ${VAR} reference could name the server's own cloud credentials. AWS_*, AZURE_*, GOOGLE_*, VAULT_*, KUBERNETES_* and similar are now refused, and QUERYAPIGATE_SECRET_ENV_VARS limits references to what it lists.
  • SecurityThe Helm chart published /metrics through its ingress. QUERYAPIGATE_METRICS_TOKEN now makes /metrics answer only a bearer token or an administrator who may observe.
  • SecurityThe Console showed what a sign-in link said. A refused SSO sign-in now carries only an error code; the Console words it itself.
  • ChangedStable now: descriptions, agent usage, usage quotas, configuration as code, MCP OAuth, Console SSO, MCP write tools and the chart's values. CI tests the lowest allowed version of every core dependency.
  • AddedGuide 58: Keep the server's own secrets, and its metrics, in.
0.29.0
Stable

The answers a security review asks for, written down and kept true: where every kind of data goes, values kept out of the logs, a person's data erased on request, and cryptography that is FIPS-approved throughout.

UpgradingThe image's access log no longer has query strings or referrers; QUERYAPIGATE_ACCESS_LOG_QUERY=1 brings the query string back. No schema change.
  • AddedWhere data goes: a document of every place data is kept or sent, for how long, who reads it, and what is never kept. A test fails if the store, a setting or an outbound path isn't on it.
  • AddedFIPS 140: every algorithm used is FIPS-approved, enforced by a test, and the whole suite runs in Red Hat UBI 9 with OpenSSL's FIPS provider forced.
  • AddedForgetting a person: queryapigate history forget CALLER and DELETE /api/v1/history?caller= erase a caller's runs and usage counts, audited by the caller's hash, not their name.
  • AddedGuide 57: Answer a security questionnaire about data and cryptography.
  • ChangedThe access log leaves query strings out, so parameter values sent in the URL aren't logged.
0.28.0
ExperimentalSecurity

No database password anywhere: a connection's secrets come from a file, Vault or AWS Secrets Manager, and its credentials can be short-lived, made by Vault per lease or as RDS IAM tokens, rotated with no failed call.

UpgradingNo schema change. A PostgreSQL or MySQL connection whose TLS settings ask for more than its server gives now fails instead of connecting without it.
  • SecurityA PostgreSQL or MySQL connection's TLS settings are now given to its driver. sslmode=verify-full previously connected without checking the server's certificate.
  • AddedSecrets from files: ${file:/run/secrets/name}, re-read when the file changes, only inside QUERYAPIGATE_SECRET_FILE_DIRS. Every secret setting has a _FILE form.
  • AddedVault and AWS: ${vault:…} and ${aws-sm:…} references, short-lived database credentials from Vault's database engine, and RDS IAM tokens. Allow-lists say what the server may ask for. Guide 56, run against Vault 1.21: 861 calls across four leases, none failed.
  • Fixedqueryapigate serve exits cleanly on SIGTERM, giving back the audit stream's lease and revoking Vault leases.
0.27.0
Experimental

A server's setup as a file in git: connections, saved queries, roles, keys' grants and the rest, with no secret in it, reviewed as a pull request and applied all or nothing.

UpgradingSchema 15 adds two tables (config_managed, config_applied). Additive; 0.26 instances keep working.
  • AddedConfiguration as code: queryapigate config export, config diff (exit codes as terraform plan -detailed-exitcode) and config apply, in one transaction, every change audited via: config. --prune only deletes what a file manages.
  • AddedApplied at startup with QUERYAPIGATE_CONFIG_FILE, and QUERYAPIGATE_CONFIG_LOCKED keeps what a file manages to the file. The Helm chart's configAsCode rolls the pods when the file changes. Guide 55, run against two real servers.
  • SecurityA connection or destination can't reference the server's own OTEL_* settings.
0.26.0
Experimental

QueryAPIGate on Kubernetes the way most teams install things there: helm install from ghcr, one pod on SQLite or several on PostgreSQL and Redis, locked-down pods, and a chart that refuses setups that can't work.

UpgradingNo schema change. The image needs gunicorn 26 or later and passes --no-control-socket; add the flag if you override the image's command.
  • AddedA Helm chart for Helm 3 and 4, released with the server. Non-root pods on a read-only root filesystem, ingress with /events and the MCP server routed, optional ServiceMonitor, NetworkPolicy and autoscaler. CI installs it on kind and upgrades it under load. Guide 54.
  • ChangedThe image runs on a read-only root filesystem.
  • FixedOn several instances, the example queries answered 404 from every instance but the one that loaded them.
0.25.0
StableExperimental

An audit trail an auditor can rely on: every administrative change chained to the one before and signed, checked in one command, and streamed as it happens to your SIEM without losing an entry while it's down.

UpgradingSchema 14 chains the entries already stored at the upgrade. Upgrade every instance.
  • AddedA tamper-evident audit log: each entry chained to the one before, HMAC-signed with QUERYAPIGATE_AUDIT_KEY. queryapigate audit verify and GET /api/v1/audit/verify name the first entry that doesn't verify; the audit_chain_broken alert says so. Stable.
  • AddedThe audit log streamed to a SIEM: syslog (RFC 5424) over TLS or mutual TLS, or OTLP logs, at least once, one instance delivering at a time. Guide 53. Experimental in 0.25, stable from 0.32.
0.24.0
StableExperimental

Limits a contract can be written in: a key or role can cap the queries it runs at once, how long each may run and how many rows a call returns, and carry a quota of calls and rows per day or month.

UpgradingSchema 13 adds usage_counters. Upgrade every instance before giving a key a quota.
  • AddedPer-key limits: max_concurrent, query_timeout and max_rows on keys and roles, on every front door, counted across instances through Redis. Over the limit: 429 too_many_concurrent. Stable.
  • AddedUsage quotas: "quota": {"calls": 100000, "rows": 10000000, "per": "month"}, with X-Quota-Remaining-* headers, 429 quota_exceeded and the quota_nearly_used alert. Experimental in 0.24, stable from 0.30.
  • AddedX-Row-Count on every paged data response, and the Console's key and role forms take the new limits. Guide 52.
  • FixedA streamed response closed before its first chunk kept its database connection checked out.
0.23.0
StableExperimental

Seeing inside, and hardened at the edge: QueryAPIGate's spans in your traces, without a parameter value, row or secret ever leaving in one, and security headers on every response.

  • AddedTracing with OpenTelemetry: install the otel extra and set OTEL_EXPORTER_OTLP_ENDPOINT. Spans for each request, saved query, database call, MCP tool call and export, continuing a caller's traceparent. About 1 ms a request fully sampled. Guide 51. Experimental.
  • AddedSecurity headers on every response: nosniff, a default-src 'none' CSP and no-referrer on API responses, HSTS when configured, on all three servers. Stable.
  • Changed/docs loads one exact Swagger UI release with Subresource Integrity; responses are Cache-Control: no-store unless they say otherwise; no version banner.
0.22.1
Security

A cached saved query's response could be kept by a shared cache in front of the server.

  • SecurityFrom 0.4.0 to 0.22.0 a saved query with cache_ttl answered Cache-Control: max-age. Callers authenticate with X-API-Key, so a CDN or caching proxy could store one caller's masked rows and serve them to the next request for the same URL. Now Cache-Control: private, on hits and 304s too. The server's own cache was never affected.
0.22.0
Stable

Rotating secrets without downtime: the key stored passwords are encrypted with, and an API key's secret, can each be replaced while everything keeps working. Both stable from the start, so 1.0 freezes them.

UpgradingSchema 12 adds two nullable columns to api_keys and an index. A 0.21 instance keeps working against the upgraded store but does not know a rotated key's old secret, so upgrade every instance before rotating a key.
  • AddedReissuing an API key's secret. POST /api/v1/api-keys/{name}/rotate gives a key a new secret while the old one keeps working for grace_minutes (default 60, at most a week, 0 for a leaked secret). Same name, grants and history. In the Console: API keys → Edit → Secret.
  • AddedRotating the encryption key. QUERYAPIGATE_SECRET_KEY takes several keys, comma-separated: the first encrypts, every one decrypts. queryapigate rekey re-encrypts every stored password under the first, in one transaction; --check only reports.
  • FixedA destination password saved before the secret key was set stayed in plaintext; startup now encrypts those too.
0.21.0
Experimental

Agents that act, governed: an AI agent may write through a saved query its author marked for agents, either at once or only after a named person has approved that exact change.

UpgradingSchema 11 adds one table. Upgrade every instance.
  • AddedWrite tools over MCP, each write approved by a person. A saved query marked agent_writes (one INSERT, UPDATE or DELETE, never DDL) becomes a write tool. With "approval" the first call asks, an administrator approves it at /api/v1/write-requests, the second call runs it once with exactly the approved arguments. Every agent write runs in a transaction rolled back past max_affected_rows. The credential that asked can never approve. New Approvals screen in the Console.
0.20.1
Security

A saved query that writes could be run over MCP by calling it by name.

  • SecurityFrom 0.11.0 to 0.20.0 the MCP server listed only read-only queries as tools, but a call naming one that writes was not refused. Now refused before it runs (read_only), whatever the version and the grant. Check run history for "transport": "mcp" on a saved query that writes.
0.20.0
Stable

A consolidation release: alerts, remote files through DuckDB, exports to object storage and column masking leave the experimental list and come under the compatibility promise, on terms now written into the versioning policy.

Upgradingqueryapigate[flow] now needs sqlglot 25.2 or later. No schema change.
  • ChangedColumn masking needs sqlglot 25.2 or later, the first its tracing works on. An older sqlglot pinned by something else now gets a clear masking_unavailable, never an unmasked result.
  • AddedCI tests against real S3 (SeaweedFS) and a real identity provider (Keycloak), and runs the DuckDB, masking and table-check tests on the lowest dependency versions allowed.
  • FixedA table function run by a table-restricted key answered 500 instead of table_not_allowed on older sqlglot releases.
0.19.0
Experimental

Administrators sign in to the Console through the organisation's identity provider, with its MFA and policies; their role comes from the groups they are in there.

UpgradingSchema 10 adds two tables. Upgrade every instance.
  • AddedConsole sign-in through SSO. OpenID Connect, authorization code flow with PKCE, run by the server. Roles from groups at every sign-in (QUERYAPIGATE_SSO_ROLES); a SameSite=Strict, HttpOnly session cookie bound to a CSRF token; QUERYAPIGATE_SSO_ONLY retires personal tokens for people who sign in through SSO.
  • ChangedAn identity provider's metadata listing any plain-HTTP endpoint is refused whole. Fetched again every hour, last good copy kept while the provider is unreachable.
0.18.0
Experimental

Letting AI agents near your data, governed: hosted AI clients sign in through your identity provider, agents are told what tables and columns mean, and you can see what each agent did.

UpgradingSchema 9 adds one table. The MCP server now answers a missing credential with HTTP 401, a rate-limited client with 429.
  • AddedOAuth for the MCP server. /mcp becomes an OAuth 2.1 resource server with Protected Resource Metadata; tokens must name its URL as audience. REST and MCP tokens are kept apart.
  • AddedDescriptions. What each table and column means, written in the Console or imported from the database's own comments, served only for the tables a caller can reach.
  • AddedAgent usage. GET /api/v1/usage/agents and an Agents screen; an agent_unusual_volume alert when an agent reads ten times its usual hourly rows.
0.17.0
Experimental

Delivering data to partners and pipelines: exports to a bucket or folder as Parquet, CSV or NDJSON, incrementally, and column masking traced through the SQL.

UpgradingSchema 8 adds three tables.
  • AddedDestinations (s3://, gs://, r2:// or a local folder), queryapigate export, saved exports with watermark-based incremental runs, and a mask grant with hash, mask, null and drop rules.
  • FixedParquet output runs in steady memory (about 150 MB at 2 or 6 million rows, was up to 1.4 GB). A stream closed before its first row now ends its query.
  • SecurityExports, connections and the store are kept apart from an administrator's reach: local-folder destinations need an operator allow-list, a connection cannot open the server's own store, and ${VAR} cannot name a server setting.
0.16.0
Stable

Named administrators replace the one shared admin key. Any query can come back as Parquet. A folder or bucket of files becomes a set of tables by itself.

UpgradingSchema 7 adds two tables. The shared key keeps working, as an owner.
  • AddedAdministrators with roles (owner, admin, developer, auditor) and their own tokens; every /api/v1 operation needs a capability; the shared key becomes break-glass with a break_glass_used alert.
  • Added?format=parquet on any query and any database type. On the example export: 513 KB of Parquet against 2.2 MB of CSV.
  • SecurityWrites hidden behind WITH, EXPLAIN ANALYZE or SELECT … INTO are refused on every database type, including DuckDB, H2 and JDBC.
0.15.0
Breaking

Several instances become a supported deployment shape; files in buckets and on the web can be published through DuckDB; what 0.14 deprecated is removed.

UpgradingSchema 6. Give every instance the same QUERYAPIGATE_REDIS_URL.
  • Breaking/execute_sql_from_file and /execute_sql_with_parameters_from_file are removed; use /q/{name}. The pre-SQLite JSON files are no longer imported. A DuckDB connection reads only the files its allowed_paths lists.
  • AddedRate limits shared through Redis, an instance list with alerts, rolling-upgrade rule and CI test, reference Compose and Kubernetes deployments, and S3 / GCS / R2 / HTTP files as sources.
0.14.0
Breaking

The groundwork for 1.0's promise: a deprecation policy, an experimental label, a database support matrix with tiers, tested backups, Python 3.11 or newer, and all 65 how-to guides.

  • BreakingPython 3.11 or newer is required. pip on 3.9 or 3.10 keeps installing 0.13.0.
  • DeprecatedThe two *_from_file routes and the import of pre-SQLite JSON files.
  • FixedA table-restricted key sees only its tables in the schema. An empty allowed_ips list allows no address. The Docker image includes every optional feature.
0.13.0
Breaking

The Console replaces /ui, the Management API (/api/v1) replaces the unversioned management routes, and every front door shares one set of rules, one error format and one run history.

  • BreakingUnversioned management routes removed; /ui redirects to /console. Error bodies gain code and request_id.
  • AddedThe QueryAPIGate Console: React + TypeScript, a client of the public JSON API only, shipped in the wheel and the Docker image.

Releases 0.1 through 0.12 are in the full changelog.