Repository navigation
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-querychecks itsoptionsas 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
WITHlist that deletes, merges or inserts, as it already did for one that updates (#1872), beforeEXPLAIN ANALYZEof 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 withDIALECT_AMBIGUOUS_TEXT(#1936). - The row limiter no longer pages a statement whose
WITHlist writes, since PostgreSQL runs a data-modifyingWITHexactly once whatever the outer query reads (#1933). - The db routes answer 400 for a body that is not a JSON object, a non-string
sqland 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
EXCLUDEconstraints (#1820, closes #1778) and the declared column type beside the base name (#1868, closes #1860); primary and foreign keys are read frompg_catalog, so a SELECT-only role sees them (#1937); a view or another relation without storage of its own carries no size (#1945); and anEXPLAINorSHOWanswer 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=5433connects 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_EFFORTsets the reasoning effort forLLM_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_PROVIDERSkeeps a provider outside the list from loading at all (#1861).- Oracle
DATEandTIMESTAMPedits bind throughTO_DATE,TO_TIMESTAMPorFROM_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
Limitthat 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_LIMITand 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_TEMPLATESoption, 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
emailunless the provider marks it unverified (email_verified: false), and otherwise apreferred_usernamewithout@, then the subject. An account whose email the provider marks unverified is therefore named by itspreferred_usernameand starts with an empty workspace under that name, and an account with no usable email whosepreferred_usernamecontains@is now named by its subject, and refused withBEARER_NO_IDENTITYon the bearer channel (#1914). - With the Auth0 or Zitadel recipe, accounts holding an
OIDC_ADMIN_ROLESvalue now become admins, and withOIDC_USER_ROLESset, 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
ordersis created as the lowercase"orders"rather than folded toORDERS(#1766). - A client of the HTTP API sees 400 where a malformed request used to answer 500 (#1880, #1885), 400 for multi-query
optionsthe query route refuses (#1881), 403ADMIN_REQUIREDon every admin-only refusal, and 401AUTH_REQUIREDfrom/api/admin/auditwithout a session (#1922). - A cancelled SQL Server statement answers 499 instead of 500 (#1877); a PostgreSQL view or partitioned table carries no
sizeBytesinstead of 0 (#1945);EXPLAINandSHOWreport the rows they returned asrowCount(#1854); and a statement whoseWITHlist writes is no longer bounded and answers nohasMore(#1933). LLM_PROVIDER=openaiagent and plan runs now call the Responses API (/responses) atLLM_API_URL, so a gateway that serves only Chat Completions needsLLM_PROVIDER=custom. AnLLM_MODEL_EFFORTthe model does not take refuses agent starts with 503LLM_CONFIGwhere 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. Onollamaandcustomthe agent's tool schemas no longer carrypropertyNames(#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
- @notlakshitha made the gate ask before a writing
WITHlist andEXPLAIN ANALYZE(#1872, #1873), showed the declared PostgreSQL column type (#1868), percent-decoded the database name (#1871), steadied the etcd test (#1870) and corrected the README's audit claim (#1867). - @Maaz2212 added custom roles from the OIDC role claim (#1869) and kept the ClickHouse tree for a restricted user (#1771).
- @niukanen1 fixed the PostgreSQL row count of a plan (#1854) and read libpq keywords from a URL (#1905).
- @mgr-punith fixed the SQL Server 4060 report (#1805) and the estimate mismatch warnings below a
Limit(#1911). - @kaya-abdullah swept the MongoDB agent end to end (#1819), named the copyright holder and listed the contributors (#1814, #1815), and added two vendor links (#1892, #1910).
- @yusuf-gundogdu ran the six provider live checks in CI (#1844) and kept the deploy pins and AWS listing moving (#1816, #1817, #1852, #1853, #1918).
- @koraysrn filtered sampling by the OpenAI SDK's own decision (#1846), and @Apu-Emdad logged a refused effort as that (#1843).
- @saad-works scoped the Diff tab to one database (#1838), @yizhixiaokong pinned the tree's ancestors (#1896), and @siddxwar04 showed
EXCLUDEconstraints (#1820). - @Bombiccc kept Create Table names as typed (#1766), @Aditya-XR fixed Oracle date edits (#1818), @cestercian fixed Couchbase collection maintenance (#1650), and @itz-puneet sped up MTConnect reads (#1808).
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