Skip to content

1.1.0

Latest

Choose a tag to compare

@cevheri cevheri released this 24 Oct 17:00
· 114 commits to main since this release
Immutable release. Only release title and notes can be modified.

LibreDB Studio 1.1.0 is a minor release.
Its headline is native clients: Studio now accepts an access token from the identity provider on its API, so a mobile app that signs in to Keycloak, Auth0 or Zitadel itself can call Studio without a browser session (#1914, #1922).
OIDC roles can now come from the role claim too, including the namespaced claim Auth0 writes and the object claim Zitadel writes (#1869, #1932).
The result grid selects and copies cells the way a spreadsheet does, and the release carries hardening an operator should take: the multi-query route checks its paging options, a file database path can no longer stall the server, and the confirmation gate asks before three writes it used to miss.
One sign-in behaviour changes for OIDC installs; read Upgrading before you roll it out.

Native clients call the API with an access token

A native client signs in to the identity provider itself as a public PKCE client and sends Authorization: Bearer <access token> to Studio's API.
The channel is opt-in: it exists only while OIDC_BEARER_AUDIENCE is set under NEXT_PUBLIC_AUTH_PROVIDER=oidc, and OIDC_BEARER_CLIENT_IDS narrows it to the clients you list.
Without it the header is ignored outside /api/mcp, as before.

Studio accepts an asymmetric JWT from the issuer's key set, for the audience, unexpired, not an ID token, with a subject, a username and a role.
A refused token answers 401 with a WWW-Authenticate challenge and is audited, and an identity provider outage answers 503 IDENTITY_PROVIDER_UNAVAILABLE, so a client can tell the two apart.
A token that verifies but maps to no role, or carries no usable username, answers 403 BEARER_NO_ROLE or BEARER_NO_IDENTITY, so the client stops refreshing a token that will never be accepted.
A bearer session writes no cookie and cannot mint an MCP token, and under governance it meets the same sign-in rule as a browser session.
For a bearer session the managed connection list leaves out the secret fields of an editable seed, and for every session governed records now carry access: "read" or "write".
A native POST without an Origin header must send Content-Type: application/json to pass the cross-site check.
docs/OIDC.md describes the setup, and docs/SECURITY.md asserts it as control 1.11.

OIDC roles from the role claim

OIDC_ROLE_PREFIX maps the role claim's values that start with the prefix to custom roles, and a seed connection's roles can name them, so the identity provider decides who sees which seeded connection at browser sign-in (#1869, closes #1773).
At most 20 roles of at most 64 bytes (UTF-8) each are carried; an account over either limit is refused at sign-in with a message that says why.
The role claim is now read the way the providers write it: a claim whose name contains dots, such as Auth0's https://example.com/roles, is read whole before it is read as a path, and an object claim, such as Zitadel's urn:zitadel:iam:org:project:roles, gives its keys (#1932).
The Auth0 recipe in docs/OIDC.md now also writes the role into the access token, and the role mapping steps show Zitadel's object claim.

Hardening

  • POST /api/db/multi-query checks its options as the query route does and answers 400 with the same sentences before it splits the text, and a page bound is a non-negative safe integer everywhere it is read, including the bounds SQL Server, Oracle and Db2 write themselves (#1881).
  • A SQLite, DuckDB or LibreDB path the server cannot use now fails the connect at once (#1879).
  • The confirmation gate asks before a PostgreSQL WITH list that deletes, merges or inserts, as it already did for one that updates (#1872), before EXPLAIN ANALYZE of a write, which runs the write (#1873), and before a statement whose -- comment holds a bare carriage return; the agent and MCP guard refuse that text with DIALECT_AMBIGUOUS_TEXT (#1936).
  • The row limiter no longer pages a statement whose WITH list writes, since PostgreSQL runs a data-modifying WITH exactly once whatever the outer query reads (#1933).
  • The db routes answer 400 for a body that is not a JSON object, a non-string sql and a few other malformed fields, where they used to answer 500 with the runtime's own text (#1880, #1885).

Result grid

Click, drag, Shift+Click and Ctrl+Click select cell ranges, and a column name, a row number and the corner select a column, a row or everything (#1855).
Ctrl+C copies the selection as tab-separated rows that paste into a spreadsheet cell for cell, a masked cell copies its mask, and a range carries the CSV export's formula guard.
Arrows, Home, End, Page Up and Page Down move the selection, and Enter or F2 edits.
Columns open wide enough for their longest value in the first 200 rows, up to 500 px, and a double-click on the resize handle fits the loaded rows, up to 2000 px.
The row detail parses JSON text that has whitespace around it (#1943).

Also in this release

  • PostgreSQL: the explorer shows EXCLUDE constraints (#1820, closes #1778) and the declared column type beside the base name (#1868, closes #1860); primary and foreign keys are read from pg_catalog, so a SELECT-only role sees them (#1937); a view or another relation without storage of its own carries no size (#1945); and an EXPLAIN or SHOW answer reports the rows it returned rather than 0 (#1854, closes #1839).
  • Paste URL percent-decodes the database name (#1871, closes #1866) and reads libpq keywords from a PostgreSQL URL's query string, so postgresql:///mydb?host=db.example.com&port=5433 connects where psql does (#1905, closes #1902).
  • The sidebar tree and the Keys panel pin the ancestors of the first visible row, so a deep scroll still says which database and folder the rows belong to (#1896, closes #1895).
  • On MySQL the Diff tab compares the session's database by default, with a Scope picker that keeps Whole connection as a choice (#1838, closes #1791).
  • Create Table keeps table and column names as typed, quoted in the engine's dialect, and refuses a name the engine cannot store (#1766, closes #1435).
  • Agent mode runs OpenAI models through the Responses API with store: false, LLM_MODEL_EFFORT sets the reasoning effort for LLM_PROVIDER=openai, and temperature and top-p are sent only where the model takes them (#1837, closes #1835; #1846, closes #1842; #1843, closes #1841).
  • The MongoDB agent sweep fixed eleven findings, among them a tool schema that reached some model servers as an object with no allowed key (#1819).
  • LIBREDB_ENABLED_PROVIDERS keeps a provider outside the list from loading at all (#1861).
  • Oracle DATE and TIMESTAMP edits bind through TO_DATE, TO_TIMESTAMP or FROM_TZ (#1818, closes #1807); SQL Server reports error 4060 for a database that does not exist (#1805, closes #1451) and answers a cancelled statement with 499 like every other engine (#1877).
  • ClickHouse keeps the object tree for a user without grants on system.* (#1771, closes #1768); Couchbase runs collection maintenance from the tree on a named collection (#1650, closes #1412); the Redis key browser opens a key whose name ends in :* (#1946).
  • The plan analyzer stops warning about estimate mismatches below a Limit that stops a node early (#1911, closes #1858), and MTConnect reads a current or sample answer in one pass (#1808, closes #1787).
  • Object search answers 400 with the code INVENTORY_PAIR_LIMIT and the limit when a request names too many pairs (#1944).
  • The VS Code extension, built on Studio's providers, is on the Marketplace as 0.2.0 and is released from its own vscode-extension-<version> tags (#1882, #1893, #1894, #1897 to #1900, #1904, #1908, #1920).
  • The Milvus and Qdrant seed images move to certifi 2026.10.19 (#1913).

Known issues

  • The editor's Monaco 0.57.0, its latest release, pins DOMPurify to exactly 3.4.15 and bundles that copy, so 1.1.0 ships the version GHSA-cg2g-xh64-v4q7 names (fixed in 3.4.17). The advisory is a bypass of DOMPurify's SAFE_FOR_TEMPLATES option, which Monaco never sets, and Studio does not use DOMPurify itself. The dependency scan suppresses it until 2027-01-22, and Studio takes the fix with the first Monaco release past 3.4.15.

Upgrading from 1.0.0

  • OIDC usernames. Without governance the username keys a person's saved work. It is now the email unless the provider marks it unverified (email_verified: false), and otherwise a preferred_username without @, then the subject. An account whose email the provider marks unverified is therefore named by its preferred_username and starts with an empty workspace under that name, and an account with no usable email whose preferred_username contains @ is now named by its subject, and refused with BEARER_NO_IDENTITY on the bearer channel (#1914).
  • With the Auth0 or Zitadel recipe, accounts holding an OIDC_ADMIN_ROLES value now become admins, and with OIDC_USER_ROLES set, accounts that used to be refused can sign in (#1932).
  • In the result grid, pressing a column's name selects the column; sorting moved to the arrow beside it, which keeps the same accessible name (#1855).
  • Create Table quotes the names it writes, so on Oracle a table typed as orders is created as the lowercase "orders" rather than folded to ORDERS (#1766).
  • A client of the HTTP API sees 400 where a malformed request used to answer 500 (#1880, #1885), 400 for multi-query options the query route refuses (#1881), 403 ADMIN_REQUIRED on every admin-only refusal, and 401 AUTH_REQUIRED from /api/admin/audit without a session (#1922).
  • A cancelled SQL Server statement answers 499 instead of 500 (#1877); a PostgreSQL view or partitioned table carries no sizeBytes instead of 0 (#1945); EXPLAIN and SHOW report the rows they returned as rowCount (#1854); and a statement whose WITH list writes is no longer bounded and answers no hasMore (#1933).
  • LLM_PROVIDER=openai agent and plan runs now call the Responses API (/responses) at LLM_API_URL, so a gateway that serves only Chat Completions needs LLM_PROVIDER=custom. An LLM_MODEL_EFFORT the model does not take refuses agent starts with 503 LLM_CONFIG where the SDK knows the model's efforts (GPT-6 and later, and non-reasoning models); otherwise the first turn fails with OpenAI's own error. On ollama and custom the agent's tool schemas no longer carry propertyNames (#1837).

Helm chart: 0.1.103

Tracks app release 1.1.0; no template, value or default moves in this version.
Chart 0.1.101 added config.llmModelEffort and 0.1.102 added config.oidcRolePrefix; their default image, 1.0.0, reads neither, so they take effect from this chart or an image of 1.1.0 or later.
artifacthub.io/containsSecurityUpdates is true for 0.1.103, for the hardening above.

helm repo add libredb https://libredb.org/libredb-studio/
helm upgrade --install libredb-studio libredb/libredb-studio --version 0.1.103

Contributors

The bearer channel, the role claim fixes, the result grid selection, the VS Code extension, the OpenAI Responses path and the hardening of #1879, #1881 and #1936 are by @cevheri.

Full changelog: 1.0.0...1.1.0