Skip to content

0.18.0

Choose a tag to compare

@cevheri cevheri released this 05 Oct 13:55
· 484 commits to main since this release
Immutable release. Only release title and notes can be modified.
83ce126

LibreDB Studio 0.18.0 adds eight connection types for seven engines: etcd, IBM Db2, Neo4j with a Graph tab for Cypher results, Qdrant, Milvus, InfluxDB as two connection types, and Oxia.
It now connects on its own to the databases CapRover runs and to the managed database DigitalOcean creates with the 1-Click Droplet, and on sqlite or postgres storage local sign-in gains passkeys and any number of accounts.
It also fixes a session bypass in OIDC sign-in that affects every deployment from 0.7.0 through 0.17.0 with OIDC_ISSUER, OIDC_CLIENT_ID and OIDC_CLIENT_SECRET set, whatever NEXT_PUBLIC_AUTH_PROVIDER says, so operators of such a deployment should upgrade.
Two more fixes narrow what standard users reach on the server: their DuckDB connections run with file and network access off, and SQLite connections open only for admins (see Upgrading).
The rest is a long run of correctness fixes, most of them measured live against the engine they touch: dates, numbers and binary values shown as the engine stores them, SQL INSERT exports that replay, and maintenance, cancel and SANDBOX that say only what actually happened.

Key-value and coordination stores: etcd and Oxia

etcd connects read-write over etcd's gRPC API (#1228). The editor runs one etcdctl command at a time: get, put, del, txn, leases, a bounded watch, member list, endpoint status and health, alarm list, auth status, users and roles. The tree lists key-prefix groups, members, leases, users and roles. A key's value is edited from the Source tab as one Txn guarded by its mod_revision, so a concurrent change makes the edit fail instead of overwriting it. Compaction, defragmentation and alarm disarm are admin-only cards on Admin > Operations, confirmed by typing the connection's name.

It is Kubernetes-aware. Every write to a Kubernetes key prefix or to kube-apiserver's compact_rev_key is refused on the server, and Kubernetes protobuf, encrypted values and secrets are withheld everywhere. A seed connection can be declared readOnly: true. etcd has no agent execution mode and no MCP. Measured on etcd 3.7.2 as a single member, a cluster, and with client certificate and password auth; 3.6.0 and 3.6.6 for the auth behaviour.

Oxia connects read-only over its gRPC client API (#1310): look up, list and range-scan keys with oxia client commands, browse shards, and walk every key in the Keys panel. The namespace is the connection's Database field, Token is a bearer token, and a cluster lists its data servers in a new Data servers field. Studio dials a shard leader only when it is the address it connected to or a listed data server, sends the token nowhere else, refuses a token without TLS to a non-local host unless allowInsecureAuth is set, and sends no write call at all. Measured on Oxia 0.16.10 and 0.17.1.

IBM Db2 LUW

Db2 for Linux, UNIX and Windows connects as db2 over the db2-node driver, a native client that needs no IBM client install (#1238, closes #786; moved to db2-node 1.0.24 in #1303 and 1.0.25 in #1353). It takes host, port, database, user and password or a pasted db2:// URL, supports TLS verify-full and verify-ca, and refuses to send a password without TLS unless the connection sets allowInsecureAuth. The tree shows schemas with tables, views, MQTs, aliases, sequences, modules, procedures, functions and triggers; queries page on the server; inline row edit and import into existing tables work; and the operations page offers RUNSTATS and REORG on tables. Agent and MCP take the Oracle posture: plan mode and inspect_schema only. Measured on Db2 12.1.0.0 and 11.5.9.0, with and without TLS.

Limits in this version: no Db2 for z/OS or IBM i, no EXPLAIN, transactions, cancel, Create Table or object edit. Because db2-node 1.0.25 silently writes nothing to a CLOB, DBCLOB or BLOB column declared 32768 bytes or more, and a result does not carry the declared length, the grid opens no inline editor on any Db2 CLOB, DBCLOB or BLOB cell and says why (#1353).

Neo4j and the Graph tab

Neo4j 5.26 LTS connects read-only as neo4j over bolt, bolt+s and bolt+ssc, with SSH tunnel support (#1239). One database per connection: the tree shows node labels and relationship types with their properties, indexes and constraints, and the editor runs read-only Cypher with its own language and completion, plan mode and the MCP metadata tools. Every write is refused before it is sent, by a token-level read policy, a server EXPLAIN gate and a READ session. LOAD CSV, APOC, GDS and parameters are refused, and agent execution and MCP run_read_query do not serve Neo4j. There is no neo4j:// routing and no Aura.

A Cypher result that holds nodes, relationships or paths gets a Graph tab next to Results, drawn with Cytoscape.js from only what the statement returned, with no extra query (#1289). Nodes are deduplicated by elementId and coloured per label with a legend; clicking one opens an inspector, and the tab has fit, zoom, re-layout, PNG and JSON export and keyboard controls. At most 300 nodes are drawn, with a notice when a result holds more. Masking follows the grid in captions, the inspector and both exports.

Vector databases: Qdrant and Milvus

Both are new in a vector family that shares a console grammar, an in-flight limiter and a per-type statement size bound (#1247), and the results grid shows a declared vector column as its first 8 elements and its size, such as 768 dims, with the whole value in row detail and Copy Cell (#1248).

Qdrant connects read-only as qdrant over its REST API (#1270). Browse collections with their vectors, payload indexes and a sampled view of payload keys, and run Qdrant's own METHOD /path read requests with one JSON body from a closed console: point reads, scroll, count, facet, queries, batches and grouped queries over dense, sparse and multivector data. Studio refuses every inference input except the local BM25 model, sends an API key without TLS only to a loopback address or through an SSH tunnel, warns about a JWT with no expiry or with manage access, and shows MCP clients only schema and payload-index names. An http(s):// address pasted into the Host box fills Host, Port and SSL Mode (#1244, #1281). Measured on Qdrant 1.19.1.

Milvus connects as milvus, carried over gRPC by Studio's own client (#1283). Browse databases and collections with fields, partitions, indexes and load state, and run reads, exact counts and searches over every vector type, BM25 and hybrid included, from a console that takes Milvus's own POST /v2/vectordb/<route> requests. Studio writes no data; an admin can Load or Release a collection after a preview and a typed name, and the action is audited. Requests that would make the server call another service are refused, a password goes without TLS only to loopback or through an SSH tunnel, and the default root password is warned about. An empty list result says it holds only what this Milvus user has a privilege on (#1287). Measured on Milvus 3.0.2.

Columns inferred from sampled data are marked sampled in the tree and withheld from MCP inspect_schema, agent grounding and the AI panels (#1247).

InfluxDB, as two connection types

InfluxDB reads as two read-only connection types (#1309). influxdb, "InfluxDB (InfluxQL)", reads 1.x, 2.x through its v1 API, and 3.x over POST /query. influxdb3, "InfluxDB 3 (SQL)", reads one 3.x database over POST /api/v3/query_sql. Flux is not offered. Each type refuses anything but a read before any request is sent, one answer is capped at 16 MiB, and each type runs two calls at a time. Measured on InfluxDB 1.13.1, 2.9.1 and 3.12.0.

Platform integrations: CapRover and DigitalOcean

CapRover. A new opt-in one-click template, libredb-studio-autoconnect, adds a discovery companion that reads the Docker socket and exports the databases CapRover runs (#1502). Studio lists them as connections for admins only, never returns their passwords, follows databases added or removed later, and shows the discovery status on an admin Overview card. Studio reads the export through SEED_DISCOVERY_PATH and withdraws the discovered connections once the last scan is older than SEED_DISCOVERY_MAX_AGE_MS; the companion's DISCOVERY_* settings are in .env.example and docs/SEED_CONNECTIONS.md. The plain CapRover template stays socket-free. A standard user cannot read the export file through a DuckDB connection (#1519, see Security fixes). Measured on CapRover 1.15.4 with five official database apps. The template states its app-name limit, 39 characters starting with a letter, and has CapRover create the app that holds the Docker socket last, so an install that stops on a name CapRover refuses never leaves that app running on its own (#1528). The plain template's install text was also corrected: it no longer tells operators to set STORAGE_ENCRYPTION_KEY before rotating JWT_SECRET, which loses saved connection passwords (#1203).

DigitalOcean. When a managed PostgreSQL or MySQL database is added while creating the LibreDB Studio 1-Click Droplet, first boot turns its credentials into one managed seed connection, admin role only, with TLS required. The login message says whether the database was added, and why not when it was not (#1378).

Launch sign-in for platforms that host Studio

A platform that runs Studio for its users can now sign them in with one click: it signs a token valid for at most a minute, opens /launch#token=<token>, and Studio exchanges it for its ordinary session cookie, on a seeded connection when the token names one (#1516). With sqlite or postgres storage the first launch creates an account bound to the person's platform identity, with no password and the role the token names, and a later launch reaches only that account. A launch never signs in to an account that has a password, an authenticator or a passkey, never to the ADMIN_EMAIL address, and never replaces another person's session; in a browser with no Studio session it names the account and waits for a Continue as click. It stays off until LAUNCH_TOKEN_SECRET is set with LAUNCH_TOKEN_AUDIENCE and LAUNCH_TOKEN_ISSUER, and it is not available under OIDC. The guide, with the token format and every answer of POST /api/auth/launch, is docs/LAUNCH.md.

Three settings serve the same platforms and work on their own as well. ALLOW_CUSTOM_CONNECTIONS=false limits every user to the seed connections: routes that build a provider refuse any other connection with 403 CUSTOM_CONNECTIONS_DISABLED, and the editor hides New connection, Edit and Duplicate; it is on when unset, and a value it does not recognise switches it off and logs an error. SEED_LITERAL_VALUES=true reads every seed value as written, with no ${...} or Vault resolution, for a seed file written from data the platform's users control. And a /?connection=<id> link opens the editor on that connection, also after signing in.

Sign-in: passkeys and more than two local accounts

Any number of local accounts. With STORAGE_PROVIDER=sqlite or postgres, local email and password accounts live in an accounts table, so a team can have any number of admins and users, managed under Admin > Accounts: create, change role, disable, delete and set a password (#1122, closes #784). The last enabled admin cannot be removed. Sessions and MCP tokens end at the next request when an account is disabled, deleted, demoted or has its password reset, and every account can set up its own TOTP authenticator. STORAGE_PROVIDER=local and OIDC mode are unchanged. See Upgrading for how ADMIN_* and USER_* behave now.

Passkeys. On the same storage, local accounts can register passkeys and sign in with one, with no email, password or code (#1195, closes #785). User verification is required, so a passkey sign-in counts as a full multi-factor sign-in. Passkeys stay off until PASSKEY_ORIGIN names the address users open Studio at (chart: config.passkeyOrigin); browsers offer them only over https or on http://localhost. Failed passkey sign-ins have a rate-limit budget of their own, RATE_LIMIT_PASSKEY_MAX and RATE_LIMIT_PASSKEY_WINDOW_SEC, so passkey retries never lock password sign-in. Admins see a passkey count per account and can remove them behind a typed confirmation. The authenticator page is renamed Sign-in security.

An ended session. When the session cookie is missing or no longer verifies, API routes answer 401 JSON with code AUTH_REQUIRED instead of a redirect, so the editor no longer shows Unexpected token '<' ... is not valid JSON. The standalone app sends the tab to /login?next=<page> and returns there after sign-in by password, passkey or OIDC (#1484, closes #1420).

Dates, numbers and binary values as the engine stores them

Several engines handed the grid a JavaScript Date or a rounded number, so values shifted with the Studio server's time zone or lost digits. They now arrive as the engine's own text:

  • PostgreSQL date and timestamp and their arrays no longer shift with the server's time zone; date '2026-09-01' showed as the previous day under Europe/Istanbul. timestamptz stays an instant (#1134). interval, point and circle show as psql does, and a top-level json null shows as null text (#1503, closes #1432).
  • MySQL-wire DATE, DATETIME and TIMESTAMP show as the server's text with microseconds and zero dates kept, and inline edits of date cells are accepted (#1505, closes #1388).
  • SQL Server time, date and datetime2 keep all seven fraction digits instead of invented UTC instants (#1178, closes #1132).
  • Oracle DATE and TIMESTAMP show the stored wall clock, and the SQL INSERT export writes them back through TO_DATE and TO_TIMESTAMP (#1225, closes #1131).
  • Integers past 2^53 and wide decimals keep their exact digits on ClickHouse, Redis, Couchbase, Elasticsearch, OpenSearch and LibreDB (#1327, closes #1321), and numeric columns sort as numbers in the grid (#1482, closes #1384).
  • NaN, Infinity and -Infinity reach the grid, exports, agent and MCP as words instead of NULL (#1377, closes #1371).
  • SQLite and libSQL BLOBs show as \x hex (#1350, closes #1331), and the JSON exports and copies write a binary cell as the same hex instead of a {"type":"Buffer"} object (#1472, closes #1381).

Exports that replay

Export as SQL INSERT now writes values each engine reads back: PostgreSQL arrays, intervals, points and circles, SQL Server BIT, and ClickHouse Array, Map and Tuple (#1483); Trino, DuckDB and Cassandra typed literals (#1490, closes #1386). A cell with no valid literal turns its row into a -- Row N skipped comment, and the INSERT and DDL target the table the SELECT read instead of a generic name. The DDL export keeps types such as integer[] and DateTime64(3, 'Europe/Istanbul') instead of TEXT.

Maintenance, cancel and transactions say what happened

  • PostgreSQL and MySQL connections probe the server at connect and offer only the maintenance it runs, so CockroachDB, RisingWave, YugabyteDB, TiDB and Vitess no longer show operations they refuse; a statement's own fault answers 400 QUERY_ERROR with the engine's sentence instead of 500; and every whole-database card in Admin > Operations asks for confirmation (#1487 closes #1387, #1427, #1438; #1499).
  • Maintenance from the monitoring page works on tables outside the public schema, and the table's schema is passed on SQL Server, DuckDB, MySQL, ClickHouse, Oracle and Couchbase too (#1091, closes #772).
  • Cancel says "Query Cancelled" only when the engine confirms it, else "Cancel Not Confirmed". ClickHouse can now cancel, CockroachDB, Materialize and RisingWave fall back to the PostgreSQL wire CancelRequest, and on engines that cannot cancel the control reads STOP WAITING (#1383, closes #1364).
  • SANDBOX claims "auto-rolled back" only when a rollback succeeded, refuses statements that commit implicitly, and refuses to start where BEGIN opens no transaction (#1324, closes #1317).
  • The editor's background plan on PostgreSQL-wire servers no longer executes the statement a second time; it uses EXPLAIN (FORMAT JSON) without ANALYZE, and multi-statement text is never planned (#1318, closes #1311).
  • A query stopped by PostgreSQL statement_timeout or lock_timeout is reported as a timeout, not a cancel (#1149, closes #1145).

Security fixes

OIDC: the state cookie was accepted as a session (GHSA-8gc9-2gm6-5c7f). On a deployment with OIDC_ISSUER, OIDC_CLIENT_ID and OIDC_CLIENT_SECRET set, the signed oidc-state cookie that GET /api/auth/oidc/login hands any visitor once the identity provider answers discovery was also accepted as the session cookie, whatever NEXT_PUBLIC_AUTH_PROVIDER was set to, because the login route never checked it. An unauthenticated visitor could pass the session check on every path outside /admin for that cookie's five-minute life, and could request a new one at will. Versions 0.7.0 through 0.17.0 are affected; a deployment that never set those three variables never issued the cookie, so local sign-in on its own is not exposed. The state is now signed with a key derived from JWT_SECRET for that purpose alone, so the session check no longer accepts it, and it carries a token type of its own that the OIDC callback checks (#1380). If you run Studio with OIDC, or have the OIDC_* variables set, upgrade. A sign-in in progress during the rollout fails its state check once; signing in again works.

  • DuckDB file access for standard users. A non-admin user's DuckDB connection could read, and in some cases write, any file the Studio process can reach, with read_text and the other file-table functions or COPY, and could make the server request URLs; the discovery export of the new CapRover auto-connect template, which holds every discovered database's password, would have been within that reach, so this fix ships with the template. A DuckDB editor handle now opens with DuckDB's enable_external_access off for every non-admin role, and for every role on a DuckDB seed a non-admin role can use, so no statement reaches the network or a file other than the database's own files and the handle's private temp directory, while that database stays writable. The database path itself is not sandboxed: a standard user's DuckDB connection still opens, reads and writes any DuckDB-format file the Studio process can reach, and creates a missing one (docs/providers/duckdb.md section 3.16). The posture is derived on the server from the session's role and the resolved connection, never from the request, and a full-reach handle and a denied one are never shared. An admin keeps full reach on an inline connection and on an admin-only seed (#1519).
  • Server files behind file-based connections. SQLite, DuckDB and LibreDB connections refuse, for every role and before a handle opens, a path that names Studio's storage database, its -wal and -shm files, or the auth-bootstrap.json credentials file with its backup and temporary copies, also when the path reaches one through a symbolic or hard link; other database files in the same directory stay usable. SQLite connections also need trusted file access now, since SQLite's drivers cannot confine what a statement reads and writes the way DuckDB's posture does: a SQLite connection opens only for an admin, on an inline connection or an admin-only seed, and agent runs, the agent hand-over and MCP follow the same rule (#1523, #1529).
  • HTTP database destinations. A new opt-in setting, DB_HTTP_BLOCK_PRIVATE_HOSTS, refuses connections from the HTTP-based providers (ClickHouse, Couchbase, Druid, Elasticsearch, OpenSearch, libSQL, Prometheus, Trino, and in this release Qdrant and both InfluxDB types) to private and loopback addresses, checking literal IPs and DNS answers at socket connection time, custom TLS paths included. It is off by default, so local database connections keep working; when it is on, HTTP connections through an SSH tunnel are refused too, since their target is a local tunnel endpoint (#1114, closes #1108; #1242).
  • Next.js moves from 16.3.5 to 16.3.8: 16.3.6 for a critical Next.js vulnerability reported by the dependency scan (#1288, #1172), and 16.3.8 for the security fixes in that release (#1512).
  • DuckDB no longer fetches extensions at open. Test Connection on a SQLite file used to make the server download the sqlite_scanner extension and rewrite the SQLite file. Autoinstall, autoload and community extensions are now off, and a file that is not a DuckDB database is refused unopened (#1476, closes #1404).
  • Redis commands that moved the shared connection. SELECT, RESET, AUTH and HELLO, inside MULTI too, moved every later request on that connection to another database, user or protocol; the editor now refuses them (#1121, closes #1107).
  • Data masking. The Data Profiler masks min, max and sample values with the same configuration and role as the grid, in its exports and in the AI summary request, which used to send min and max in clear (#1478, closes #1421). Sorting a masked column orders rows by the masked text, so the order no longer reveals the hidden values (#1506, closes #1491), and the column filter matches the masked text (#1492).
  • Connection dialog. Closing the dialog after a new connection resets TLS, SSH tunnel, environment and Advanced fields, so the next new connection no longer opens and tests with the previous one's certificates, client key and tunnel (#1150, closes #1125).
  • The Db2 driver's linked rustls crates are past the RustSec advisories (#1353).
  • The provider cache key. Two connections that differ only in their Elasticsearch API key pair, or only in their MongoDB authSource, no longer share one cached provider, so a rotated or revoked API key takes effect at once instead of only after the cached provider has sat unused for 30 minutes, and a corrected auth database is not served by a provider opened as the other user (#1510).

Also in this release

MySQL family

  • Columns report their declared type, such as varchar(20) or int unsigned, so Schema Diff migrations for a MySQL or MariaDB target are accepted, and column defaults are written as SQL read from SHOW CREATE TABLE (#1073 closes #1033, #1119 closes #1031).
  • Databend, StarRocks and Apache Doris: characters outside the BMP display correctly, statements that return no rows no longer fail, and manual BEGIN, COMMIT and ROLLBACK work with toasts that say Studio cannot verify the transaction there (#1357 closes #1352, #1367 closes #1366, #1473 closes #1470).
  • Databend, which cannot prepare statements, gets its object tree, column reads, statistics and inline edits through client-side binding, and moves from query-only to partial support (#1479, closes #1474).
  • A pasted MySQL connection string gets the same timezone Z default as the host and port form (#1134).
  • Vitess lists its keyspace instead of the sidecar and shard databases, and folders count (#1361, closes #1354). Analyze Table on TiDB, OceanBase and Databend succeeds when the server answers with no report (#1471, closes #1382).
  • Explain draws a plan tree for MySQL, MariaDB and Percona Server, and a script run inside BEGIN or SANDBOX runs statement by statement (#1492 closes #1389, #1390, #1396, #1477).

PostgreSQL family

  • Server WARNING and NOTICE messages are shown with the result, capped at 100 per statement, and a startup notice shows as a caution at Test Connection (#1488, closes #1401).
  • Schema tools: columns whose names are substrings of the key are no longer marked primary key, Schema Diff no longer re-creates the key's own index, Create Table from a schema folder creates the table there, and autocomplete qualifies tables outside the default schema (#1493 closes #1391, #1394, #1395, #1397).
  • On Materialize the object tree folders count and open, except Functions and Procedures (#1358, closes #1351).

Oracle, SQL Server, SQLite and DuckDB

  • PL/SQL bodies, / separator lines and T-SQL GO batches split correctly; Oracle used to report success for objects stored INVALID (#1332, closes #1312).
  • Oracle columns report their declared type, UROWID(n) and VECTOR(n, format) keep their size, and table statistics read the user's tables instead of always "no tables" (#1187 closes #1139, #1237 closes #1209, #1365 closes #1355).
  • Pasting an Azure portal ADO.NET string with Server=tcp: fills the host correctly (#1212, closes #1211).
  • SQLite returns the rows of WITH, VALUES and RETURNING statements (#1330, closes #1323).
  • A DuckDB file the process cannot write opens read-only and says why a write is refused (#1476; #1486, closes #1405; #1494).
  • libSQL: a connection that names a user signs in to a self-hosted sqld started with SQLD_HTTP_AUTH over HTTP Basic (#1516).

MongoDB, Redis and other non-SQL engines

  • MongoDB reads Extended JSON such as $oid and $date, so a document can be found by its ObjectId (#1368, closes #1363); results show the union of every document's keys (#1376, closes #1372); and Generate Find and the profiler no longer fail with "Path collision" (#1374, closes #1373).
  • FerretDB lists databases (#1302, closes #1299), and a pasted string with tlsCAFile or an unescaped @ in the password is handled (#1210).
  • The Keys panel's Scan all no longer freezes the tab after Load more filled the tree, on Redis too (#1228).
  • Redis: each editor line is its own command and SET k "" stores an empty string (#1379, closes #1370); a failed connect names its reason (#1362, closes #1356); and blocking commands the provider refuses no longer ask for confirmation first (#1174, closes #1164).
  • Cassandra and ScyllaDB Generate Query and imports write CQL that parses, Elasticsearch reports a closed index correctly, Qdrant and InfluxDB no longer hit ECONNRESET after about 5 s idle, and the MongoDB grid shows Long, Timestamp, RegExp and UUID readably (#1495 closes #1410, #1413, #1416, #1419, #1423).
  • A Neo4j disconnect during a connect closes that attempt's driver (#1308, closes #1296).
  • Couchbase takes the bucket from the connection string's path, as in couchbases://host/bucket, when the Database field is empty; a Database value still wins (#1517, closes #1457).
  • A malformed Kafka read request is a query error, not a config error (#1137, closes #1129). On VictoriaMetrics the Tables tab lists up to fifty metrics (#1162).

Results, editor and charts

  • A failed query replaces the previous result with an inline error, also in the embedded workspace, and an object's open tab is focused instead of duplicated (#1136). A multi-statement failure keeps a banner naming the statement (#1475, closes #1385), and a new run clears the previous error (#1316, closes #1294).
  • A write shows "N rows affected", Load More is not offered after a last page that ends exactly full, Explain runs go to Query History with a Plan badge, the export menu names masked columns, and deleting a connection removes its saved tabs (#1485 closes #1425, #1433, #1440, #1447, #1448, #1460). CSV import into a new table quotes its names and keeps the dialog open with the error on failure (#1492). The "limited" badge no longer shows on short pages (#1142, closes #1128).
  • Profile Table works on every SQL engine with plain aggregates and shows the engine's message for a refused measure (#1349, closes #1320).
  • Charts: a NULL category no longer shifts every later value (#1375, closes #1369), and rotated x-axis labels stay out of the legend (#1175, closes #1130).
  • A grid column filter ends with the query it was typed against, and matching folds the Turkish dotted and dotless I (#1480, closes #1409).
  • The Query Safety Check offers Skip analysis and gives up on the AI after 15 seconds (#1322, closes #1313); it is now a Radix alert dialog with trapped focus (#1191, closes #1185). Confirmation dialogs return focus and follow Studio's theme (#1202 closes #1199, #1204 closes #1198), and labels in the connection, import, pivot and toolbar views render at their intended weight (#1140, closes #1124).
  • The inline editor's key-uniqueness check reads its count by name, so an edit on the row whose key is 1 no longer skips it, and Db2 no longer refuses most edits (#1353).
  • The code generator keeps a trailing s in column names (#1177, closes #1138).
  • Typing in the editor no longer re-renders the Studio shell, and first-load JavaScript drops from 2426.8 kB to 2196.0 kB (#1127, #1190, #1217).

Sidebar, admin and configuration

  • Connections can be grouped in collapsible user-defined sections (#1229, closes #1170).
  • Monitoring's Pool card reads "Open clients", the audit log lists the newest event first, and empty gauges show N/A (#1481 closes #1434, #1445, #1463, #1464).
  • Vault Kubernetes login takes a custom auth mount through VAULT_K8S_AUTH_PATH, and both Vault path variables ignore surrounding whitespace (#1282 closes #1245, #1297 closes #1291).
  • The agent's local model roster grows from thirty-five to forty models that passed all six agent surfaces at 30/30 (#1192).
  • The login form renders in the first HTML (#1232).
  • Trino is called by its own name, not Apache Trino, across docs, READMEs, store listings and packaging metadata, and the README attributes the Apache trademarks (#1158).

Distribution and packaging

  • The desktop AppImage no longer bundles libwayland-client, which left the window white on Mesa 26 hosts (#1236, closes #1222).
  • An AUR package, libredb-studio-bin, is ready and stays pending until AUR registration reopens (#1218, closes #971).
  • The container images move to Node 26.10.0: the default image on node:26.10.0-trixie-slim, the alpine image on node:26.10.0-alpine3.23, and the slim alpine image on alpine:3.24 (#1171). Runtime dependencies move to monaco-editor 0.57.0, @platformatic/kafka 2.13.0 (each version taken only after the live read-only Kafka and Redpanda suite passed on it), cassandra-driver 4.10.0, pg 8.23.1, mongodb 7.7.0 and mysql2 3.24.5, and adm-zip is held at 0.6.1 for CVE-2026-77301 (#1172, #1512). The README lists ZopDay and Scoop Extras (#1183, #1226 closes #1120), and the Fly.io and Railway pins move to 0.17.0 (#1207).
  • New Portuguese and Russian READMEs (#1017, #1188 closes #610).

Upgrading

These changes can stop a setup that worked before:

  • Local accounts on sqlite or postgres storage. ADMIN_* and USER_* now only seed the accounts table on first start; changing them later does not change stored passwords. A startup warning reports a mismatch, and ADMIN_PASSWORD_RESET=true restores the env admin at the next start, ends its sessions and removes its passkeys; remove the variable afterwards, because every start applies it again while it is set. Upgrading such a deployment signs local users out once, and MCP tokens issued before the upgrade stop working and must be created again (#1122).
  • Storage schema. On sqlite and postgres storage the first start creates the accounts, passkey_users, passkey_credentials and passkey_spent_challenges tables, so the STORAGE_POSTGRES_URL role needs CREATE on its schema, as it did at first install (#1122, #1195).
  • API routes without a valid session return 401 with {"error": "<message>", "code": "AUTH_REQUIRED"} instead of a 307 to /login; pages still redirect. A script or embedding host that relied on the redirect must handle the 401 (#1484).
  • PostgreSQL TLS to an IP address. With SSL Mode verify-system, verify-ca or verify-full and a host given as an IP address, the server certificate is now checked against that IP instead of the name localhost (pg 8.23.1, #1512). A certificate that lists the IP now connects; one that lists only localhost, reached through 127.0.0.1, is now refused, so connect by the name the certificate carries. STORAGE_POSTGRES_URL is affected only with sslmode=verify-system.
  • libSQL connections that name a user, and no connection string, send HTTP Basic instead of a bearer token; a connection string keeps its bearer token (#1516).
  • Sign-in redirects. A signed-out page now redirects to /login?next=<path and query>, the bare root still to a bare /login, and a signed-in visit to /login?next= goes to that page instead of the role's landing page (#1516).
  • DuckDB no longer auto-installs or auto-loads extensions; a query that relied on that needs an explicit INSTALL and LOAD, and community extensions cannot be enabled. A DuckDB connection to a SQLite or other non-DuckDB file is refused (#1476).
  • DuckDB for standard users. On a non-admin user's DuckDB connection, and for every role on a DuckDB seed a non-admin role can use, file-table reads, COPY to or from a file, URL reads, INSTALL and the LOAD of an extension that is not bundled answer DuckDB's Permission Error; the connection's own database stays writable, and an admin keeps that reach on an inline connection and an admin-only seed (#1519).
  • SQLite for standard users. A SQLite connection opens only for an admin, on an inline connection or an admin-only seed. A standard user's SQLite connection, and every role's use of a SQLite seed offered to any role besides admin, agent runs, the agent hand-over and MCP included, fail at connect with an error saying SQLite needs an administrator; give such a seed roles: ["admin"] to keep it for admins. The bundled Employees SQLite sample is listed for admins only. A SQLite, DuckDB or LibreDB connection to Studio's storage database, its -wal and -shm files or auth-bootstrap.json is refused for every role (#1523, #1529).
  • Redis editor. SELECT, RESET, AUTH and HELLO are refused; pick a database with the Keys panel picker or the connection's Database field (#1121). A command wrapped across lines without quotes is refused; write it on one line (#1379). A wrong-password connect answers 401 instead of a retryable 503 (#1362).
  • SANDBOX refuses DDL and other implicit-commit statements on MySQL and Oracle, refuses to start where BEGIN opens no transaction, and returns 400 TRANSACTION_STATE_UNREPORTED on MySQL-wire servers that report no transaction state, such as Databend, StarRocks and Apache Doris, where a SANDBOX run could autocommit before (#1324, #1473).
  • SQL Server multi-statement runs send each GO batch as one request and never send a GO line. Destructive statements inside a control-flow block now open the safety confirmation (#1332).
  • MongoDB. A $date or $oid object in an insert is stored as a BSON Date or ObjectId instead of a literal subdocument, and a wrapper mixed with another key is refused (#1368).
  • ClickHouse Decimal arrives as a string, small values included, so the grid sorts it as text and pivot no longer offers it as a value field. A Redis DBSIZE reply that is not a count is refused instead of shown as 0 keys (#1327).
  • Schema Diff snapshots saved before this release report spurious type changes on MySQL and Oracle columns, because ColumnSchema.type is now the declared type (#1073, #1187).
  • Exports. JSON exports and copies write binary cells as \x hex strings instead of Buffer objects (#1472). Copy Cell on a binary value copies the whole value as hex (#1248). SQL INSERT export is unavailable while masking hides any column (#1485).
  • HTTP API answers. Clients that call the routes directly should check:
    • Non-finite numbers come back as the strings "NaN", "Infinity" and "-Infinity" instead of null from /api/db/query, /api/db/multi-query, /api/db/transaction, agent artifacts and MCP, and a PostgreSQL infinite timestamptz as "infinity" (#1377).
    • Statement faults classified by SQLSTATE class or driver code answer 400 QUERY_ERROR instead of 500, and the POST /api/db/monitoring payload gains a maintenance field (#1487). A PostgreSQL statement_timeout or lock_timeout answers 408 TIMEOUT_ERROR instead of 499 (#1149). A malformed Kafka read is a query error instead of CONFIG_ERROR (#1137).
    • options.limit and options.offset that are not non-negative integers get a 400; the limiter fetches limit + 1 rows, so a SELECT ... FOR UPDATE without its own LIMIT in a held transaction locks one extra row (#1485).
    • An explain request holding more than one statement gets a 400 (#1318). A queryId that is not a string is refused (#1383). POST /api/db/transaction with a multi-statement query answers in the multi-query script shape plus inTransaction, which now reflects the server state (#1492, #1324).
    • A wrong-depth container on the object counts, list and inventory routes answers 400 with only an error field (#1148). Object edit routes answer 400 for a body that is not valid UTF-8 and 413 for a declared Content-Length over the limit (#1195).
    • POST /api/db/profile leaves unmeasured figures out instead of sending 0, returns refusals in warnings[] and capped columns in omittedColumns, ends a timeout or lost connection with 408 or 503, and answers 400 to non-string column names or an empty column list; the profile's CSV and JSON exports write unknown figures as empty (#1349, #1374).
    • POST /api/ai/query-safety can answer 504 TIMEOUT_ERROR after 30 seconds (#1322). GET /api/admin/audit returns events newest first and limit=0 returns none (#1481).
    • A connectionId that is present but empty, or is not a string, answers 400 Invalid connection ID format; omit the field to send an inline connection (#1519).
    • Fleet health resolves every item the way the query routes do, so a connection whose id names a seed is checked as that server-side seed. /launch is a public path, and POST /api/auth/launch answers 404 while launch sign-in is off (#1516).
  • Sidebar. A starred connection appears both under Favorites and in its own section (#1229).

An adopter embedding @libredb/studio should also check the library surface:

  • DatabaseType gains etcd, db2, neo4j, qdrant, milvus, influxdb, influxdb3 and oxia. QueryTab["type"] gains etcd, oxia, cypher, influxql, qdrant and milvus; ProviderCapabilities.queryDialect gains etcd, oxia, qdrant and milvus; queryLanguage gains cypher and influxql; identifierQuoting gains "double-always"; the QueryEditor language prop gains qdrant and milvus, and KeyScanCapability.totalScope gains none. An exhaustive switch over any of them needs the new arms. KeyScanPage.total now counts what totalScope declares.
  • Maintenance takes the new MaintenanceOperation type, MaintenanceType plus compact, defragment, disarm, load and release, in ProviderCapabilities.maintenanceOperations, maintenanceOperationSpecs and DatabaseProvider.runMaintenance. MaintenanceOperationSpec gains optional title, description, confirmation ("typed" or "typed-target"), kinds and preview, with the exported MaintenancePreview type.
  • createDatabaseProvider, getOrCreateProvider and withOneShotTunnel raise a DatabaseConfigError for readOnly: true on an engine that does not enforce it, and for a readOnly that is not a boolean.
  • getOrCreateProvider takes a third argument, the newly exported EditorExecutionContext, and refuses a readOnly in it; ProviderExecutionContext, which createDatabaseProvider already took, is now exported too and gains an allowExternalFileAccess field. Called without a context, either function opens a DuckDB handle with file and network access closed; pass { allowExternalFileAccess: true } for the full editor reach (#1519).
  • getOrCreateProvider refuses a SQLite connection too unless its execution context passes { allowExternalFileAccess: true }, since an absent context reads as deny; createDatabaseProvider without a context keeps SQLite's trusted behaviour. A SQLite, DuckDB or LibreDB path that names the storage database at STORAGE_SQLITE_PATH (default ./data/libredb-storage.db), its -wal and -shm files or the auth-bootstrap.json beside it raises a DatabaseConfigError (#1523).
  • The embedded workspace requests GET /api/connections/policy when its Schema Diff panel opens; a host without that route answers 404, which reads as allowed (#1516).
  • Rows carry strings instead of Date objects for PostgreSQL date, timestamp and their arrays, MySQL DATE, DATETIME and TIMESTAMP, SQL Server time, date and datetime2, and Oracle DATE and TIMESTAMP. PostgreSQL interval, point and circle arrive as text, and a top-level json null as the string null (#1134, #1505, #1178, #1225, #1503).
  • ColumnSchema.type carries the declared type on MySQL and Oracle, with the family in baseType (#1073, #1187).
  • An AUTH_REQUIRED 401 reaches the host's own code unchanged: embedded components register no session-ended handler (#1484).
  • @libredb/studio/security gains an allowWebAuthnGet option; the default output still denies publickey-credentials-get (#1195).
  • ConnectionModal resets every connection-scoped field when the edit target changes or a new connection is saved with the dialog kept open, and withdraws the previous target's test result (#1157, #1167, #1205).
  • The exported DataProfiler shows unmeasured figures as unknown and gains optional maskingConfig, maskingEnabled and userRole props; DataCharts labels NULL, Date and other non-string categories before drawing (#1349, #1478, #1375).
  • New optional members, which need no change to compile: QueryResult.vectorColumns, resultSets (the new QueryResultSet type) and rolledBack, with the types VectorColumn, VectorKind, VectorDType and SparseEncoding; WorkspaceQueryResult.vectorColumns; ProviderCapabilities.previewProjection and previewTimeWindow with the types PreviewProjection, PreviewProjectionRule and PreviewTimeWindow; ColumnSchema.provenance; QueryTab.runError and origin; QueryHistoryItem.kind; QueryWarning.severity; DatabaseConnection.readOnly, dataServers and allowInsecureAuth; DatabaseOverview.tableCountSampledFrom; DatabaseObject.readRanges with ObjectReadRange; ObjectKindSpec.enumeratedBy and countIsListing; trailing and payloadLabel on the command arm of ObjectEditUnit; KeyScanCapability.separator, cursor, pattern and totalScope; KeyScanPage.skipped; ProviderCapabilities.enforcesReadOnly, readsFileAccessPosture, previewProjection, previewTimeWindow, inlineEditRefusedColumns, containerPathShapes, implicitCommitStatements, implicitCommitExceptions, supportsQueryCancel, blocksServerWhileRunning, supportsConstantPredicate and supportsMultiRowInsert; DatabaseProvider.previewMaintenance, engineUser and connectWarnings; the QuerySafetyDialog connectionName prop and a signal for onAnalyzeSafety.

Helm chart: 0.1.78

Tracks app release 0.18.0. Charts 0.1.72 to 0.1.77 were published against the 0.17.0 image and added config.passkeyOrigin and the metadata for etcd, Neo4j, Db2, Milvus, Qdrant, InfluxDB and Oxia; 0.1.78 is the first chart whose image runs them. Passkeys also need config.storageProvider set to sqlite or postgres: the chart's NOTES warn when config.passkeyOrigin is set with local storage, and on a TLS ingress with sqlite or postgres storage they suggest the origin to set. artifacthub.io/containsSecurityUpdates is true for the OIDC, DuckDB and file-based connection fixes above. The chart's own edit is its description, which now calls Trino by its own name instead of Apache Trino (#1158).

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

Contributors

etcd, IBM Db2, Neo4j and the Graph tab, Qdrant, Milvus, InfluxDB, Oxia, passkey sign-in, the OIDC fix, CapRover and DigitalOcean discovery, launch sign-in for hosting platforms, the DuckDB file-access fix, the reserved server files and admin-only SQLite, PostgreSQL dates, the inline failed-run error and the AppImage fix are by @cevheri.

Full changelog: 0.17.0...0.18.0