Releases: libredb/libredb-studio
Release list
0.9.66
Couchbase is now a supported database
LibreDB Studio speaks SQL++ to Couchbase Server and Capella, over the documented Query and
management REST APIs. It is the ninth provider and the first that adds no driver dependency at
all — package.json is unchanged, so the Docker image, Snap, AppImage, Flatpak, deb/rpm and the
@libredb/studio npm package are all exactly the size they were before.
That was a deliberate choice rather than a shortcut. The official SDK is 64.6 MB unpacked across
3765 files and runs a postinstall step that downloads a prebuilt binary or compiles from source,
which fails outright in an air-gapped or egress-restricted install. Going over REST also travels
through corporate HTTP proxies, which the binary key-value protocol on port 11210 does not, and
needs no native module in the container image.
What you get
- The bucket, scope and collection explorer, with scopes flattened the way PostgreSQL schemas are:
_defaultis implicit, everything else reads asscope.collection. - Column types inferred per collection with
INFER, and the document key surfaced as__id— in
Couchbase the key is not part of the document, so without it you would never see it. - EXPLAIN plans rendered as a tree.
- Read-your-writes consistency by default. The server default would let a
SELECTimmediately after
anINSERTreturn nothing; the provider asks forrequest_plusso you always see your own writes. - Monitoring — cluster version and uptime, bucket size, connections, cache hit ratio, slow queries
and active requests — and maintenance for update-statistics, deferred index builds and killing a
running request. - Query ports are discovered from the cluster rather than configured, so only the management port
goes in the connection form. NAT and Docker deployments work through the cluster's advertised
alternate addresses, and Capella SRV endpoints resolve automatically.
Un-indexed collections are readable on Server 7.6 and later through a sequential scan; on 7.0 to 7.2
the query fails and the error now carries the CREATE PRIMARY INDEX statement that fixes it.
Reference: docs/providers/couchbase.md
Distribution coverage matrix
docs/CHANNELS.md is now a coverage matrix for three audiences at once — users looking for an
install route, developers checking what is automated, and anyone evaluating the project's reach. It
is generated from distribution/channels.yaml, so the table cannot drift from the inventory: 26
channels, 20 live, across Linux, macOS, Windows, containers, Kubernetes and cloud.
Adding a provider is now documented
docs/ADDING_A_PROVIDER.md carries the whole path: a seven-criteria rubric for deciding whether a
database needs a driver at all, the transport seam that keeps the wire protocol out of provider
logic, and the failure modes specific to HTTP databases — a 200 response that carries an error, an
envelope that does not describe the rows, rows that are not objects. Couchbase is the worked example.
Helm chart: 0.1.28
Application version only. No template changes — the rendered manifests are identical to 0.1.27.
Full changelog: 0.9.65...0.9.66
libredb-studio-0.1.28
Web-based SQL IDE for cloud-native teams supporting PostgreSQL, MySQL, SQLite, Oracle, SQL Server, MongoDB, and Redis
0.9.65
Explain no longer runs a statement it cannot explain
The Explain button deliberately skips the dangerous-query confirmation dialog - prompting "are you sure?" before a read-only EXPLAIN would be pure noise. That shortcut was only sound while an EXPLAIN was actually built, and the direct-explain path fell back to the original statement whenever it was not:
let queryToRun = queryToExecute;
if (isExplain && explainStrategy) {
const explainSql = explainStrategy.buildSql(queryToExecute, "analyze");
if (explainSql) {
queryToRun = explainSql; // else: keeps the original statement
}
}Every dialect strategy returns nothing for a non-SELECT, and the Explain affordance is offered for whatever sits in the editor. So clicking Explain with an UPDATE employee SET ... executed that UPDATE for real - no confirmation dialog, no undo - and then rendered an empty plan.
An explain run now only ever sends SQL a dialect strategy built for it. When there is none, nothing is executed and the reason is reported:
- The statement is not a SELECT: "Only SELECT statements can be explained."
- The provider declares no EXPLAIN support, or ships no plan format: "EXPLAIN is not available for this database type."
- Connection metadata has not resolved yet: "Connection metadata is still loading. Try again in a moment."
The pattern predates the plan-rendering work - PostgreSQL and MySQL carried it for as long as the Explain button has existed. SQLite EXPLAIN QUERY PLAN, added in 0.9.56, is what widened the exposure, because the zero-config sample database puts the Explain button in front of every fresh install.
Fixing it also surfaced a gap in our own tests: the supportsExplain: false denial had a test that could not fail. It mocked no fetch route, so the request failed and raised an error toast either way, and expect(mockToastError).toHaveBeenCalled() passed whether the capability check ran or not - at 100% line coverage. Coverage proves a line ran, never that it mattered. All three refusal paths now assert both that no request is sent and which reason the user is given.
Distribution: three channels stop reporting SKIP
The drift table could measure any channel whose served version is one document a regex can read. Three tier-0 channels did not fit that shape and printed a permanent SKIP, which meant their publish-time green checkmark was the only evidence that publication had worked - what a user actually pulls today was never re-verified.
bun run distribution:check gains a probe pin strategy for exactly those channels:
| Probe | Channel | Measures |
|---|---|---|
ghcr-tag-digest |
docker-ghcr |
digest(:latest) == digest(:<version>) |
dockerhub-tag-digest |
docker-hub-mirror |
the same equality on the mirror |
snap-store-channel |
snap |
the stable version of each architecture |
winget-max-version |
winget |
the highest version the catalog enumerates |
Probe URLs live in distribution/channels.yaml rather than in the script, so the unit tests point them at a local Bun.serve and the suite never touches the real network. A probe reports its sources through the same agree/compare path as a multi-file pin, so a lagging Snap architecture reads as drift with both architectures visible.
Two blockers the issue had assumed turned out not to exist: GHCR needs no read:packages secret (its anonymous pull token is enough), and no manifest-list unwrapping is required to compare tag digests.
winget is live
The first LibreDB.Studio manifests merged into microsoft/winget-pkgs, so the channel moved from pending to live in distribution/channels.yaml and in the docs. winget install LibreDB.Studio now resolves against the community catalog, and the new winget-max-version probe above is what keeps that claim honest release to release.
Helm chart: 0.1.27
Chart 0.1.27 tracks this app release. There are no chart template changes - the rendered manifests are identical to 0.1.26.
Full changelog: 0.9.64...0.9.65
libredb-studio-0.1.27
Web-based SQL IDE for cloud-native teams supporting PostgreSQL, MySQL, SQLite, Oracle, SQL Server, MongoDB, and Redis
0.9.64
A too-short JWT_SECRET now stops the app instead of failing every login
A JWT_SECRET under 32 characters used to pass every startup path untouched. The zero-config bootstrap only fills in a missing secret, nothing validated one that was set explicitly, and GET /api/db/health answered healthy regardless. The result was the worst kind of failure: a deployment that looks fine from the outside while every single login returns 503.
That is not hypothetical. The Cosmos one-click servapp shipped broken for three weeks this way - Cosmos generates 24-character passwords and its template passed one straight into JWT_SECRET.
Startup now runs an auth-config preflight after bootstrap. A secret that is set but too short prints an operator-facing banner (its length, never the value) and the process exits:
LibreDB Studio cannot start: JWT_SECRET is too short
Got 24 characters; the minimum is 32.
1. Set a strong secret: JWT_SECRET=$(openssl rand -base64 32)
2. Unset JWT_SECRET and let the first run generate one
Refusing to start is deliberate, rather than reporting it through the health check: /api/db/health is the Kubernetes livenessProbe and the Docker/PaaS health check, so failing it would restart the pod forever without fixing anything - and would hide the login screen's actionable 503 behind CrashLoopBackOff. Refusing costs nothing, because a too-short secret can sign no session at all: no working deployment can regress into this. The preflight runs on standalone boot only, so embedding @libredb/studio in a host app is unaffected.
The same trap was in our own README: the Koyeb one-click button prefilled a 17-character JWT_SECRET, which would have produced exactly the Cosmos failure for anyone deploying without editing it. Fixed.
Helm chart: 0.1.26
Chart 0.1.26 tracks this app release. The behaviour changes landed in 0.1.25, published earlier today, and are live for anyone on the chart repo:
- Strict mode stopped demanding an admin password OIDC never uses.
secrets.adminPasswordis now required only whileauthProvider=local, and theADMIN_PASSWORDsecretKeyRefis mandatory only for strict + local - anexistingSecretbuilt for OIDC has noadmin-passwordkey, and the hard reference kept the pod from starting. persistence.emptyDirSizeLimitcaps the/app/dataemptyDir used when persistence is off. Empty (the default) keeps it unlimited, as before.persistence.fixPermissionschowns a mounted volume torunAsUser:fsGroupin a short root init container, for the hostPath and statically provisioned volumes the kubelet does not applyfsGroupto. Off by default, and rendering refuses the combination with OpenShift's security-context adaptation rather than emitting a manifest that cannot work there.- The
admin-emailSecret key is written only whensecrets.adminEmailis set, like every sibling key. Both e-mail values now accept"", which hands the default back to the app. - Retrieving generated credentials is documented for the cases where the banner is not in the current pod log:
kubectl logs --previous, reading/app/data/auth-bootstrap.json, and the arbitrary pod thatkubectl logs deployment/...picks when there is more than one replica.
The chart README is now the single canonical description of bootstrap behaviour; HELM_CHART.md, DISTRIBUTION.md and RANCHER.md link to it instead of restating it - that drift had already left a wrong claim in the docs. DOCKERHUB.md no longer marks the auth variables as required, which contradicted the zero-config default.
Full changelog: 0.9.63...0.9.64
0.9.63
Every download in this release is signed, and the launcher checks it
0.9.63 is the first release where every published artifact carries a SLSA build provenance attestation: the standalone archives, the Debian and RPM packages, the Windows zip, the snap, the desktop AppImage and GUI .deb, the npm tarball, and the GHCR container image. Each attestation is signed through Sigstore and names the repository, workflow and commit that produced the file.
Verify any release asset yourself:
gh attestation verify libredb-studio-standalone-0.9.63-linux-x64.tar.gz \
--repo libredb/libredb-studio \
--signer-workflow libredb/libredb-studio/.github/workflows/release-artifacts.ymlThe container image and the npm package have their own trust roots:
gh attestation verify oci://ghcr.io/libredb/libredb-studio:0.9.63 --repo libredb/libredb-studio
npm i @libredb/studio && npm audit signaturesThe npx launcher enforces it
npx @libredb/studio already verified the archive's SHA256 against the release's SHA256SUMS. That check has a gap: an attacker who can replace a release asset can replace its checksum line too. So after the checksum passes, the launcher now also runs gh attestation verify on the downloaded archive, pinned to the workflow that signs releases - an attestation from any other workflow, or from a branch build, fails the policy.
The outcome is deliberately tri-state:
| Outcome | Launcher |
|---|---|
| Verified | prints Provenance verified and starts |
Cannot verify - no gh, not authenticated, no network, API rate limit, or a release older than 0.9.63 |
warns, naming the reason, and starts on checksum alone |
| Rejected - the attestation fails the signer policy, or a release from 0.9.63 on has no attestation for the archive's digest | refuses to start |
The last row is what the feature exists for: for a release that must carry an attestation, "GitHub holds no attestation for this digest" is a tampering signal, not missing information. gh stays optional - the package keeps its zero-runtime-dependency contract, so everything that merely prevents verification warns and continues. To start anyway after a rejection (for example during a GitHub attestation outage): LIBREDB_STUDIO_SKIP_PROVENANCE=1.
The check runs where checksum verification runs: on a fresh download and on --verify-cache, never on a plain cache hit, and never for a locally built --archive.
The SQL editor works air-gapped
Monaco is now served from the app's own origin instead of a CDN, so the workspace opens on a machine with no internet access - a deployment inside a closed network no longer lands on an editor that never finishes loading. The Explain action also stays in step with the active connection's provider, so switching from, say, PostgreSQL to SQLite no longer leaves the previous provider's plan behaviour in place.
Fixes
- SQLite is now offered in the connection form's type picker. It was supported everywhere else, but could not be selected when creating a connection.
- An already-released Helm chart version can no longer be re-published. A
charts/**change that did not bump the chart version used to rewrite that version's repository index digest and its OCI copy while the release asset kept the original bytes; both publish surfaces are now gated, and the same rule is enforced at PR time.
Distribution
The Flatpak channel moved: FlatPark is live and is now the Flatpak route for LibreDB Studio, while the Flathub submission is marked deprecated in-repo. Every Flatpak instruction in the docs points at FlatPark.
Chart 0.1.24 tracks this release; no chart template changed, so its rendered manifests are identical to 0.1.23.
Full changelog: 0.9.62...0.9.63
libredb-studio-0.1.26
Web-based SQL IDE for cloud-native teams supporting PostgreSQL, MySQL, SQLite, Oracle, SQL Server, MongoDB, and Redis
libredb-studio-0.1.25
Web-based SQL IDE for cloud-native teams supporting PostgreSQL, MySQL, SQLite, Oracle, SQL Server, MongoDB, and Redis
libredb-studio-0.1.24
Web-based SQL IDE for cloud-native teams supporting PostgreSQL, MySQL, SQLite, Oracle, SQL Server, MongoDB, and Redis
0.9.62
Desktop: a GUI Debian package, and a new Flatpak channel
The desktop build now ships two Linux artifacts instead of one. Alongside the AppImage, every release carries a GUI Debian package:
libredb-studio-desktop_0.9.62_amd64.deb
libredb-studio-desktop_0.9.62_arm64.deb
Unlike the AppImage it needs no FUSE, it integrates with the desktop menu on install, and it takes WebKitGTK and GTK from the distribution instead of bundling them.
curl -fLO https://github.com/libredb/libredb-studio/releases/download/0.9.62/libredb-studio-desktop_0.9.62_amd64.deb
sudo apt install ./libredb-studio-desktop_0.9.62_amd64.debThis is not the server package. libredb-studio_0.9.62_amd64.deb installs a systemd service; the -desktop one installs an application. Different dpkg package names, so both can be installed side by side.
FlatPark
The new package exists because FlatPark - a signed Flatpak remote that repackages official vendor downloads as extra-data - does not accept AppImages. The descriptor set now lives in packaging/flatpark/; the listing itself is not live yet and is tracked in #241.
The bundled Node sidecar was renamed
Inside the desktop bundle the pinned Node runtime is now installed as libredb-studio-node, not node. A .deb that claims /usr/bin/node collides with the distribution's own nodejs package and dpkg refuses to install it - which would have made the GUI package uninstallable on most developer machines. The desktop shell probes both names, so an AppImage built before this release keeps working.
Nothing changes for the server, Docker, Helm, Snap or npm channels.
Full changelog: 0.9.61...0.9.62