Release notes¶
What changed in each release, and whether you have to do anything when you upgrade. Newest first. Both editions are released together when a change touches both; a dash means that edition had no release for the change.
For security fixes in full — what was exposed, who was affected, how to check — see
Security & updates. The community edition's full changelog is
CHANGELOG.md.
To see what you are running: GET /v1/version.
Reading the version numbers
A patch (0.20.3) fixes behaviour without changing what a correct caller sees. A minor (0.21.0) adds something, or changes a response or status code a caller could notice — even when the change is a fix. Read the action column for every minor.
November 2026¶
| Date | Enterprise | Community | What changed | Action on upgrade |
|---|---|---|---|---|
| 11-10 | 0.36.1 | — | Security: MCP tools could name tables and columns outside a key's policy (details). The MCP tools preview_source, filter_source, aggregate_source and schema_source take a table or column name in any case (an agent asking Snowflake for customers was told it did not exist), and a "not found" answer lists only the tables and columns the caller's policy shows. It had listed every table and column, withheld ones included (names, never values). Found by running policies, views and approvals against a real Snowflake account. |
None. |
| 11-10 | 0.36.0 | — | Snowflake VARIANT, OBJECT and ARRAY values are JSON values (as PostgreSQL jsonb), not a string of pretty-printed JSON text. A failed connection check on PostgreSQL, MySQL, SQL Server or Snowflake logs its reason (source_unreachable, password masked); the API still answers 503. |
If you read Snowflake VARIANT/OBJECT/ARRAY columns, stop parsing them as strings (Snowflake). |
| 11-10 | 0.35.2 | — | Three Snowflake fixes, found by the first run against a real Snowflake account: a query cut off by TDB_QUERY_TIMEOUT is always 504 query_timeout (Snowflake reports the timeout as either of two error codes, and for one of them TDB answered 500, audited as internal); page_key paging works (every request was 500); the schema of a source whose role cannot use its database or warehouse is 503 "not accessible" (was 500). Other connectors unchanged. |
None. Snowflake service users need TYPE = LEGACY_SERVICE (Snowflake). |
| 11-07 | 0.35.1 | — | The registry runs in SQLite's WAL mode, with a 5-second busy timeout. Requests with a database key wrote to the registry on every call (the rate limiter), and other requests waited behind those writes: at 32 concurrent clients, p99 fell from about 2.2 s to 0.8–0.95 s; throughput is unchanged. | Back up the registry with its -wal and -shm files, or stop TDB first (day-2 operations). Keep it on a local disk, not NFS. |
| 11-07 | 0.35.0 | — | Security: a SQL Server source's driver must be an installed driver's name; a path could make TDB load any library (details). Every ODBC value is escaped. A PostgreSQL query opening with a comment keeps the server-side cursor and the row cap; a credit_limit column no longer stops the cap being pushed down. MCP tools quote column names, so first name or order works. A view run is audited by the source UUID on MCP too. Tenant refusals are counted in metrics and carry CORS headers. |
A SQL Server source with a driver path is refused: re-register it with the driver's name. A filter_source value or float view parameter such as nan, inf or 1_000 is now a caller error. |
| 11-07 | 0.34.0 | 0.18.0 | Fixes from an independent review of community 0.17.1. Both: an MCP source_name (every tool, and views in enterprise) takes the source's name in any case, or its UUID, as REST does; policies still apply to the registered source whichever way it is named. source_id in query and schema responses is the UUID. An unexpected error is not quoted back to the caller, and no error shows a server file path. The schema endpoint audits what it refuses and answers 503, not 500, for a file it cannot read. A missing audit-log directory is created at startup (every request had been a 500 outside Docker). The image no longer carries uv and runs as uid 1000. Community: only one source can be registered, even under simultaneous requests; MCP reads the Authorization header as REST does, so bearer in any case works and a bare key does not. |
Community: an MCP client sending the bare key without Bearer must add it. A client that matched source_id against the name it sent now gets the UUID. |
| 11-06 | 0.33.0 | — | Tenants (Enterprise): one deployment serves several teams or customers, each with its own sources, keys, views, policies, approvals and audit log, chosen by the credential alone. Off unless TDB_TENANTS_DIR is set; the deployment as it was becomes the tenant default, in place. tdb tenants init / list / suspend / resume, operator routes, and a deployment audit chain. Also, with tenants off: two Authorization headers are refused with 400, and a lowercase bearer works on MCP and with policies as it always did elsewhere. |
A client sending two Authorization headers gets 400. Turning tenants on: mint a key for default first (static keys read no tenant), keep default's data directory outside TDB_TENANTS_DIR, and add tenant: to every SSO group. |
| 11-06 | 0.32.0 | — | Query approvals (Mid-Market): a policy rule can make queries that read named tables or columns wait for a person. The caller gets a request id at once; an approver decides with tdb approvals or /v1/approvals; the requester then runs it, by sending it again or with run_approved. Every path holds it, the wire gateway included, and the audit log records the request, the decision and each run. An optional signed webhook announces new requests. |
None. Off unless a rule has an approval block. Approvers need their own database key or SSO login: a static key can neither request nor decide. |
| 11-05 | 0.31.0 | — | SECURITY: SQL injection through filter_source's value on MySQL and Snowflake, in every release since Wave 3. Details. New: paging with page_key and cursor on REST, MCP and views, and streaming a whole result as NDJSON or CSV, both capped at TDB_MAX_RESULT_ROWS. Six new metrics. MySQL's repeated-column error is a 400, not a 500. |
Upgrade if you have MySQL or Snowflake sources. Scripts calling PUT /v1/auth/keys/{id}/tools or .../rate-limit must send the field: a body without it is now a 422. A cut result gains a hint. |
| 11-04 | 0.30.0 | — | With a policy file, a source it does not name refuses every caller, audited policy_source_unlisted. It was open to every key, as documented, so a typo or a new source silently removed every rule. unlisted_sources: allow keeps the old behaviour. This is a changed default, not a vulnerability report: no advisory. Policies |
If you use TDB_POLICIES_FILE: list every source, or set unlisted_sources: allow, or those sources refuse every caller, admins included. |
| 11-04 | 0.29.0 | 0.17.1 | Enterprise: registering a database source checks its required keys, without connecting. A query that fails under a policy records the rule. The gateway refuses a source missing a required key with a clear error. Community: INFO lines reach the console, and TDB_LOG_LEVEL works. |
Enterprise: a registration missing a key now returns 400, not 201. Community: more lines in docker logs, holding IDs and counts, not SQL or keys. |
| 11-04 | 0.28.0 | 0.17.0 | A query that fails is now in the audit log, as event: "denied" with query_failed, source_unavailable or internal_error. It reached only the application log. Enterprise MCP tools and view runs also record what they turn away. Enterprise gateway fix: a statement with a bound parameter failing at Parse or Bind could leave the next query answered with SET, breaking the connection. Audit reasons |
Expect more denied entries. If you alert on denied, filter on reason. |
| 11-04 | 0.27.0 | 0.16.0 | TDB_AUDIT_SQL=redacted keeps values in statements out of the audit and application logs. Off by default. New audit files are created 0600. Keeping values out of the log |
None unless a log shipper reads a new audit file as another user: grant it access. Enterprise: the tdb_startup log line gains audit_sql. |
| 11-04 | 0.26.0 | 0.15.0 | Security: SQL reads only the registered file. It could read anything in the data directory, so in enterprise one source's key could read another source's file. Details | SQL reading another file now returns 403. Enterprise: a single large CSV or Parquet query can be slower. Check your audit log. |
| 11-04 | 0.25.0 | 0.14.0 | Security: no built-in API key. It used to work whenever TDB_API_KEYS was unset, as admin in enterprise. Details |
If you relied on the default, set a real key or TDB will not start. Check whether you were exposed. The tdb_startup log line loses dev_mode. |
| 11-04 | 0.24.0 | — | Single sign-on and SCIM (Mid-Market): people sign in through your IdP, groups decide role, rules and tools, and removal ends access. A user field in audit. Single sign-on and SCIM |
Audit entries from JWTs gain user. Changing TDB_JWT_SECRET now also ends open gateway sessions. |
| 11-04 | 0.23.1 | — | Security: filelock 4.2.7 closes the advisories image scans reported against it. Only the Snowflake connector's local caches use it. | None. |
October 2026¶
| Date | Enterprise | Community | What changed | Action on upgrade |
|---|---|---|---|---|
| 10-30 | 0.23.0 | — | DuckDB connector: a DuckDB database file as a source, the whole schema or one table, with column and row policies. TDB opens the file only while a query runs, so its owner can keep writing; a query while the owner writes is 503 source_busy. DuckDB |
None. |
| 10-30 | 0.22.0 | — | Column and row policies (Mid-Market): per-key rules that withhold, mask and filter rows, enforced in the database on REST, MCP, views and the wire gateway, with the rule in the audit log. Plus key attributes, GET /v1/policies and suggested policies. Policies |
None. Off unless TDB_POLICIES_FILE is set; an SMB licence with it set refuses to start. |
| 10-29 | 0.21.2 | — | Security: a rotated API key keeps its allowed_tools and Lean Mode setting. Rotation used to drop them, so a scoped key came back able to call every tool. |
Check keys you rotated (GET /v1/auth/keys) and re-scope any that lost their tools. Advisory |
| 10-29 | 0.21.1 | — | Licence check hardening: the licence verification key can no longer be changed through configuration. | None. |
| 10-28 | 0.21.0 | 0.13.0 | Lean Mode: smaller MCP results for agents — rows as arrays, marked cell trimming, and in enterprise schema detail levels and a per-key setting. Saving tokens | None. Opt-in; with nothing set, responses are unchanged. |
| 10-27 | 0.20.3 | 0.12.4 | Security (enterprise): API keys were written to the application log; now a six-character hint. Both: a mistake in the caller's SQL is a 400, not a 500. | Enterprise: rotate API keys and purge retained application logs. Advisory |
| 10-27 | 0.20.2 | 0.12.3 | Line breaks in logged values are escaped, so caller input can't forge a log line. | None. |
| 10-26 | 0.20.1 | 0.12.2 | Every query value is plain JSON, the same on REST and MCP: binary as base64, NaN/Infinity as strings. REST used to fail on binary values. | Clients that parsed binary or NaN output get base64 / strings now. |
| 10-26 | 0.20.0 | — | Parquet connector: one Parquet file served as the table data. Parquet |
None. |
| 10-26 | 0.19.1 | 0.12.1 | Security: certifi past GHSA-6q48-6c7w-9gw2. Enterprise was exposed on Splunk, S3 and Snowflake connections. |
Enterprise: upgrade. Community was not exposed. |
| 10-16 | 0.19.0 | — | tools/list shows a scoped key only its allowed_tools; an unknown tool name in PUT /v1/auth/keys/{id}/tools is a 400. |
Scripts that set allowed_tools with a misspelt name now fail instead of storing it. |
| 10-10 | 0.18.0 | 0.12.0 | MCP 2026-07-28 alongside the earlier revisions; audit entries name the AI client (mcp_client, mcp_protocol); enterprise OAuth iss and opt-in client metadata documents. |
None. |
| 10-10 | 0.17.0 | 0.11.0 | MCP 2025-11-25 and 2025-06-18: version negotiation, tool titles, read-only hints, structured output. Enterprise output filter fails closed. |
A client sending an unsupported MCP-Protocol-Version header gets 400. |
| 10-10 | 0.16.0 | — | TDB_MAX_ROWS bounds every path that returns rows (MCP tools, view runs). |
A limit above the ceiling is now refused instead of clamped. |
| 10-10 | 0.15.1 | — | MCP tools and the REST view run report truncated. |
None. |
| 10-10 | 0.15.0 | 0.10.0 | MCP conformance: no 500 on malformed input, notifications get 202, ping, enterprise argument type checks. |
A request from a foreign browser Origin is 403; list browser origins in TDB_CORS_ORIGINS (enterprise). |
| 10-10 | 0.14.2 | 0.9.2 | The row cap reaches the source when a query ends in a comment. | None. |
| 10-09 | 0.14.1 | 0.9.1 | pip removed from the images (six scanner findings, not exposed). Gateway refusals show TDB's error over the extended protocol. | None. |
| 10-09 | 0.14.0 | 0.9.0 | PostgreSQL wire gateway (enterprise, opt-in TDB_PG_PORT). Write keywords used as column aliases are accepted. Wire gateway |
None; the gateway is off until TDB_PG_PORT is set. |
| 10-09 | 0.13.0 | 0.8.0 | The replace() string function is accepted. |
None. |
| 10-09 | 0.12.0 | 0.7.2 | Security: the read-only check could be shown one statement while the engine ran several (GHSA-qmj5-8fv9-h74r). Enterprise also: allowed_tools enforced on REST, privileged database roles refused, Postgres SSL and require_auth options. |
Upgrade. Sources that connect as a privileged role are refused unless TDB_ALLOW_PRIVILEGED_DB_ROLE=true. Advisory |
Earlier releases¶
Earlier security releases, with their advisories, are listed in Security & updates. The community edition's earlier releases are in its CHANGELOG.