The canonical guide to every supported way of installing and operating LibreDB Studio, and to how the release pipeline publishes each channel. For a one-line-per-channel overview, see the Install matrix in the README.
| Channel | Best for | Section |
|---|---|---|
| Docker | Servers, PaaS, CI, quickest start | Docker |
| Helm | Kubernetes | Helm (Kubernetes) |
| npx | Trying it on a laptop with Node installed | npx |
| Homebrew | macOS / Linux workstations | Homebrew |
| .deb / .rpm | Debian/Ubuntu and RHEL/Fedora servers (systemd) | Linux packages (.deb / .rpm) |
| Snap | Ubuntu and other snapd systems | Snap |
All non-Docker channels ship or download the same standalone server payload (Next.js
standalone output, started with node server.js) built by
scripts/build-standalone-payload.sh and attached to
GitHub releases by .github/workflows/release-artifacts.yml.
Standalone artifacts (tarballs, .deb/.rpm, Homebrew formula, snap) are published on release,
starting with the first release that includes the release-artifacts workflow — older releases
have Docker images only.
Runtime note: every channel here runs the production server under Node (
node server.js), including the Docker image — its runner stage isnode:24.16.0-trixie-slimandCMDexecsnode server.js; Bun is only used to install dependencies during the Docker build and for local development (bun dev). The SQLite DB provider adapts to whichever runtime it finds (bun:sqliteunder Bun,node:sqliteunder Node) — see docs/providers/sqlite.md, Runtime & driver selection.
Every channel works with no configuration. When JWT_SECRET / ADMIN_PASSWORD are not set
(and the auth provider is not OIDC), the server generates them on first start, persists them in
<data dir>/auth-bootstrap.json (file mode 0600), and prints the admin password once to the
server log:
============================================================
LibreDB Studio first run: generated admin credentials
Email: admin@libredb.org
Password: <generated>
Stored in <data dir>/auth-bootstrap.json (delete the file to regenerate)
============================================================
- The data dir is the directory of
STORAGE_SQLITE_PATH(default./datainside the payload / container). Each channel below documents where that is and how to read the log. - Explicitly set environment variables always take precedence; only missing values are generated.
- If the data dir is not persisted (ephemeral container, no volume), new credentials are generated on every recreate.
Strict mode: set AUTH_BOOTSTRAP=off to disable generation and require explicit
JWT_SECRET and ADMIN_PASSWORD (recommended for production; missing values then surface as a
clear error on the login page instead of silently generated credentials in collected logs). The
Helm chart defaults to strict mode; all other channels default to zero-config. Unrecognized
AUTH_BOOTSTRAP values log a warning and keep bootstrap on.
Every native channel is local-first: the server binds to 127.0.0.1 by default, and
exposing it on the network is an explicit opt-in. (The Docker image and the Helm chart are the
exception - containers must bind 0.0.0.0 and are isolated by container networking instead.)
| Channel | Default bind | How to expose |
|---|---|---|
| npx | 127.0.0.1 |
npx @libredb/studio --host 0.0.0.0 (or set HOSTNAME) |
| .deb / .rpm (systemd) | 127.0.0.1 |
HOSTNAME=0.0.0.0 in /etc/libredb-studio/env, then restart |
| .deb / .rpm (direct run) | 127.0.0.1 |
LIBREDB_BIND=0.0.0.0 libredb-studio |
| Homebrew service | 127.0.0.1 |
run the binary manually with LIBREDB_BIND=0.0.0.0, or front it with a reverse proxy |
| Snap | 127.0.0.1 |
sudo systemctl edit snap.libredb-studio.libredb-studio.service with [Service] Environment=HOSTNAME=0.0.0.0 |
| Docker / Helm | 0.0.0.0 (container-internal) |
publish/route ports as usual (-p, Service/Ingress) |
For anything reachable from a network, prefer a reverse proxy with TLS in front and strict mode
(AUTH_BOOTSTRAP=off) with explicit credentials.
A direct run of the .deb/.rpm wrapper or the Homebrew binary ignores any inherited HOSTNAME
(empty, or - under Docker - the container ID Next.js would otherwise bind to) and defaults to
loopback; LIBREDB_BIND is the explicit opt-in for that case. Under systemd, HOSTNAME in
/etc/libredb-studio/env is still the override, since the unit resolves it before the wrapper
runs (detected via the systemd-set INVOCATION_ID, so the wrapper leaves it untouched there).
Release tags carry no v prefix (tag 0.9.41 == package.json version). Each release ships:
| Artifact | Name | Targets |
|---|---|---|
| Standalone server tarball | libredb-studio-standalone-<version>-<os>-<arch>.tar.gz |
linux-x64, linux-arm64, darwin-x64, darwin-arm64 |
| Checksums | SHA256SUMS |
covers all tarballs |
| Debian package | libredb-studio_<version>_<arch>.deb (+ .sha256 sidecar) |
amd64, arm64 |
| RPM package | libredb-studio-<version>.<arch>.rpm (+ .sha256 sidecar) |
x86_64, aarch64 |
| Snap | libredb-studio_<version>_<arch>.snap |
amd64, arm64 (also published to the Snap Store) |
SHA256SUMS covers the standalone tarballs; each .deb/.rpm ships its own per-file
<artifact>.sha256 sidecar instead (the packages are built in a separate job).
Standalone tarball entries are rooted under a top-level libredb-studio-<version>/ directory
(not a tarbomb) - extract with tar --strip-components=1 (tar xzf <artifact> --strip-components=1), which is what the npx launcher, the deb/rpm/snap packaging jobs, and
scripts/build-standalone-payload.sh's own --smoke self-test all do; Homebrew strips the single
top-level directory automatically for its main url/sha256 download, so the formula needs no
extra flag.
The payload contains only the runtime (server.js, package.json, .next, node_modules,
public, an empty data/, plus LICENSE/README.md): Next.js output file tracing sweeps the
repo root into .next/standalone, so payload assembly prunes the non-runtime extras (docs,
source, tooling configs, deploy manifests, local build leftovers) via a deny-list
(scripts/lib/prune-standalone-payload.sh).
The deny-list covers project-conventional paths only - on a local (non-CI) build, arbitrary
personal files sitting at the repo root can still be traced in, so keep secrets out of the
repo root (CI release checkouts are clean; published artifacts are unaffected).
Download URL pattern:
https://github.com/libredb/libredb-studio/releases/download/<version>/<artifact>.
There is no Windows standalone artifact yet — Windows users should run Docker; native Windows packaging is tracked in issue #114.
ghcr.io/libredb/libredb-studio is the canonical image (no pull rate limits). Docker Hub
(libredb/libredb-studio) is a discoverability mirror only.
# Zero-config: the first run prints the generated admin password to the log
docker run -d --name libredb-studio -p 3000:3000 \
-v libredb-data:/app/data \
ghcr.io/libredb/libredb-studio:latest
docker logs libredb-studio # shows the first-run credentials bannerThe /app/data volume persists the generated credentials and the server-side SQLite storage;
without it, a recreated container generates new credentials.
Production (strict mode, explicit secrets):
docker run -d --name libredb-studio -p 3000:3000 \
-e AUTH_BOOTSTRAP=off \
-e JWT_SECRET=change-me-to-a-random-32-char-string \
-e ADMIN_EMAIL=admin@libredb.org \
-e ADMIN_PASSWORD=your_secure_admin_password \
ghcr.io/libredb/libredb-studio:latestAll environment variables are documented in .env.example; a ready-to-use
compose file is docker-compose.example.yml.
Published by .github/workflows/docker-build-push.yml:
| Tag | Published from | Mutability |
|---|---|---|
<version> (e.g. 0.9.41) |
GitHub release (or manual dispatch) only | pinned, never overwritten by branch pushes |
latest |
GitHub release only | moves on each release |
main |
every push to main (including PR merges) |
moving pre-release tag |
dev |
every push to a feat/** / fix/** branch |
moving development tag |
sha-<commit> |
every build | immutable |
Use <version> or sha-<commit> for reproducible deployments; main / dev are for testing
unreleased code.
helm repo add libredb https://libredb.org/libredb-studio/
helm install libredb libredb/libredb-studio \
--set secrets.jwtSecret=$(openssl rand -base64 32) \
--set secrets.adminPassword=MyAdmin123Or from the OCI registry:
helm install libredb oci://ghcr.io/libredb/charts/libredb-studio \
--set secrets.jwtSecret=$(openssl rand -base64 32) \
--set secrets.adminPassword=MyAdmin123The chart defaults to zero-config bootstrap (config.authBootstrap: "" — the variable is
omitted and the app default, on, applies): helm install works with no values; missing
JWT_SECRET/ADMIN_PASSWORD are generated on first start, printed once to the pod log, and
stored in /app/data/auth-bootstrap.json (an emptyDir when persistence is off, so pod
recreation regenerates them — set persistence.enabled=true or explicit secrets for stable
credentials). This keeps the chart deployable with default values, as certified catalogs such
as the Rancher partner-charts repository require. For production, inject real secrets as above,
or enforce them with strict mode (--set config.authBootstrap=off), which makes
secrets.jwtSecret and secrets.adminPassword required and fails the install fast when
either is missing — preferable when pod logs are collected centrally. secrets.userPassword
is optional in every mode (the non-admin account exists only when it is set).
Full values reference: charts/libredb-studio/README.md;
chart architecture: docs/HELM_CHART.md.
Requires Node.js 20.9+ on Linux or macOS (x64 / arm64); Node 24 LTS is the recommended,
fully supported runtime. The launcher checks the runtime up front and spells out what an
older Node cannot do (scripts/engine-smoke.sh tests every tier in CI):
| Node | Support |
|---|---|
| 24+ (recommended) | Everything works |
| 22.13 - 23.x | Works; server-side SQLite storage (STORAGE_PROVIDER=sqlite) needs Node 24 (the bundled better-sqlite3 binding targets the Node 24 ABI) and fails with a clear error. node:sqlite may print a one-time ExperimentalWarning. |
| 20.9 - 22.12 | Works, minus all SQLite features: SQLite database connections need the built-in node:sqlite (unflagged from Node 22.13). |
| < 20.9 | Refused with a clear error (Next.js 16 floor) - bare npx may also fall back to an ancient, bin-less package version here; those versions are npm-deprecated with pointers. |
The npm package stays a pure library for
libredb-platform; the launcher downloads the matching standalone tarball from the GitHub
release, verifies it against the SHA256SUMS release asset, caches it under
~/.libredb-studio/<version>/, and starts node server.js:
npx @libredb/studio # first run downloads + verifies, then starts
npx @libredb/studio --port 8080 # or set PORT
npx @libredb/studio --help--archive starts from a local tarball and skips checksum verification unless you pin a
digest with --archive-sha256 <hex> - only use archives you built yourself or obtained from a
trusted source. Downloads retry transient failures with
backoff and abort when the connection stalls. The cache in ~/.libredb-studio/<version>/ lives
in your home directory's trust domain; --verify-cache re-checks the cached tarball against the
cached SHA256SUMS and re-extracts the payload. Re-extraction (--verify-cache and every
--archive run) preserves the payload's data/ directory - generated auth-bootstrap.json
credentials and any STORAGE_PROVIDER=sqlite state survive across it, so a re-verify or a
rebuilt archive never invalidates a previously printed admin password.
- Later runs start straight from the cache (per-version directory; delete it to force a re-download).
- All environment variables are forwarded to the server (
PORT,HOSTNAME,JWT_SECRET,ADMIN_PASSWORD,AUTH_BOOTSTRAP,STORAGE_PROVIDER,STORAGE_SQLITE_PATH,LLM_*, ...). Missing auth secrets are handled by the zero-config first run, which prints the admin password to the terminal. --archive <path>(env:LIBREDB_STUDIO_ARCHIVE) starts from a local standalone tarball instead of downloading — useful for testing a tarball built withscripts/build-standalone-payload.sh(checksum verification is skipped and the archive is re-extracted on every run).- On Windows the launcher exits with a pointer to Docker (issue #114).
- Versions released before the standalone tarballs existed have no artifacts; the launcher
detects this (HTTP 404) and suggests
npx @libredb/studio@latest.
The formula tracks the latest release (it is rendered and pushed to
libredb/homebrew-tap by release CI):
# One-time: Homebrew's untrusted-tap policy requires trusting third-party
# taps. `brew trust` needs Homebrew 6+ - if it prints "Unknown command",
# run `brew update` first (pre-6 Homebrew installs the tap without a trust
# step, but the command below would stop the chain).
brew trust libredb/tap
brew install libredb/tap/libredb-studio
# Foreground (first run prints the generated admin password to the terminal)
libredb-studio
# Or as a background service
brew services start libredb-studio- The formula depends on Homebrew's
node@24— the payload's native SQLite storage binding (better-sqlite3) is built against the Node 24 ABI in release CI, so the floatingnodeformula (a newer major) cannot load it — and installs the standalone payload into the keg'slibexec;libredb-studioon your PATH runs it. brew services start libredb-studioruns the server on port 3000 with server-side SQLite storage (it setsSTORAGE_PROVIDER=sqlite) under$(brew --prefix)/var/libredb-studio/— the data dir where generated credentials are persisted. The service does not capture stdout, so read the generated password from$(brew --prefix)/var/libredb-studio/auth-bootstrap.json. On Linux,brew servicesregisters a per-user systemd unit (~/.config/systemd/user): it starts at login — not at boot — and stops when your last session ends unless lingering is enabled (loginctl enable-linger $USER).- Running
libredb-studiodirectly also defaultsSTORAGE_SQLITE_PATHto$(brew --prefix)/var/libredb-studio/libredb-storage.db— the same locationbrew servicesuses — unless you set it explicitly. This keeps the zero-configauth-bootstrap.json(and anySTORAGE_PROVIDER=sqlitedata) outside the versioned keg so it survivesbrew upgrade, and lets both run modes share one data dir.
All configuration is environment-driven. The formula does not ship an env file (unlike
.deb/.rpm).
Foreground (libredb-studio): export variables in your shell, or prefix the command.
Explicit values override zero-config first run. Bind with
LIBREDB_BIND (the wrapper maps it to HOSTNAME — see Network exposure):
# AI, auth, OIDC, or Postgres storage — same variables as .env.example
export LLM_PROVIDER=openai LLM_API_KEY=sk-... LLM_MODEL=gpt-4o
# export NEXT_PUBLIC_AUTH_PROVIDER=oidc OIDC_ISSUER=... OIDC_CLIENT_ID=... OIDC_CLIENT_SECRET=...
# export STORAGE_PROVIDER=postgres STORAGE_POSTGRES_URL=postgresql://user:pass@127.0.0.1:5432/libredb
# LIBREDB_BIND=0.0.0.0 libredb-studio # expose beyond loopback
libredb-studiobrew services: the service block only sets STORAGE_PROVIDER=sqlite,
STORAGE_SQLITE_PATH, and HOSTNAME=127.0.0.1. Homebrew has no supported way to inject
extra env into that plist (upgrades regenerate it). For LLM, OIDC, Postgres storage, strict
auth, or a non-loopback bind, run the binary in the foreground with the env above (or put a
reverse proxy in front of the loopback service).
Full variable reference: .env.example. OIDC: docs/OIDC.md.
Storage: docs/STORAGE.md.
Native packages for Debian/Ubuntu and RHEL/Fedora (amd64/x86_64 and arm64/aarch64) are attached
to every GitHub release. They bundle the standalone server together
with a private, checksum-verified Node.js runtime under /usr/lib/libredb-studio — nothing else
to install — and register a hardened systemd service:
VERSION=<version> # e.g. 0.9.42 - release tags have no v prefix
# Debian / Ubuntu
curl -fsSLO "https://github.com/libredb/libredb-studio/releases/download/${VERSION}/libredb-studio_${VERSION}_amd64.deb"
curl -fsSLO "https://github.com/libredb/libredb-studio/releases/download/${VERSION}/libredb-studio_${VERSION}_amd64.deb.sha256"
sha256sum -c "libredb-studio_${VERSION}_amd64.deb.sha256"
sudo dpkg -i "libredb-studio_${VERSION}_amd64.deb"
# RHEL / Fedora / Rocky
curl -fsSLO "https://github.com/libredb/libredb-studio/releases/download/${VERSION}/libredb-studio-${VERSION}.x86_64.rpm"
curl -fsSLO "https://github.com/libredb/libredb-studio/releases/download/${VERSION}/libredb-studio-${VERSION}.x86_64.rpm.sha256"
sha256sum -c "libredb-studio-${VERSION}.x86_64.rpm.sha256"
sudo rpm -i "libredb-studio-${VERSION}.x86_64.rpm"Operate it with systemd:
sudo systemctl enable --now libredb-studio # start now and on boot
journalctl -u libredb-studio # first run prints the generated admin password here
sudo systemctl restart libredb-studio # apply configuration changes- Configuration lives in
/etc/libredb-studio/env(KEY=valuelines, loaded by the unit viaEnvironmentFile). The installed file is a commented template coveringPORT,HOSTNAME,AUTH_BOOTSTRAP,JWT_SECRET,ADMIN_*/USER_*, andLLM_*. It is mode 0600 (read by systemd as root before privileges drop, so secrets stay away from other users) and marked as a config file — package upgrades never overwrite your edits. - State (server-side SQLite storage and generated credentials) lives in
/var/lib/libredb-studio, owned by the service'sDynamicUseraccount. - The unit runs with systemd hardening (
ProtectSystem=strict,NoNewPrivileges, empty capability set, ...) and only writes its state directory. - The
libredb-studiocommand (/usr/bin/libredb-studio) can also be run directly without systemd; configuration then comes from your shell environment, and state defaults to${XDG_STATE_HOME:-~/.local/state}/libredb-studio/whenSTORAGE_SQLITE_PATHis unset (the payload directory under/usr/libis read-only). - Removal (
apt remove/rpm -e) stops and disables the service; upgrades restart it if it is running (standard systemd maintainer scripts,packaging/linux/scripts/).
Published on the Snap Store for amd64 and arm64 (live
since 0.9.52; release CI publishes every release to the stable channel):
sudo snap install libredb-studio
sudo snap logs libredb-studio # first run prints the generated admin password here- Installing the snap starts a background daemon serving on port 3000.
- State (SQLite storage, generated credentials) lives in
$SNAP_DATA(/var/snap/libredb-studio/current). - The snap is strictly confined with only the
network/network-bindinterfaces: TCP database connections are the supported path; unix-socket connections to databases on the host (e.g./var/run/postgresql/) are not supported. - The
.snapfile is also attached to each GitHub release for offline installs (sudo snap install --dangerous libredb-studio_<version>_amd64.snap).
Unlike the .deb/.rpm packages (which ship /etc/libredb-studio/env), the snap has no
dedicated config file. Override environment variables with a systemd drop-in on the unit snapd
generates:
sudo systemctl edit snap.libredb-studio.libredb-studio.serviceAdd [Service] Environment= lines, then restart:
sudo systemctl restart snap.libredb-studio.libredb-studio.serviceThe drop-in is written to
/etc/systemd/system/snap.libredb-studio.libredb-studio.service.d/override.conf
(root-owned). Explicit values override the defaults baked into
snap/snapcraft.yaml and take precedence over
zero-config first run generation. Note that systemctl edit
creates the drop-in world-readable (mode 0644, like any systemd override), so after adding
secrets tighten it — systemd reads drop-ins as root, so this does not affect the service:
sudo chmod 600 /etc/systemd/system/snap.libredb-studio.libredb-studio.service.d/override.confExample drop-in (uncomment and fill what you need):
[Service]
# Bind (default is loopback — see Network exposure above)
#Environment=HOSTNAME=0.0.0.0
# Auth (optional; omit to keep zero-config bootstrap)
#Environment=AUTH_BOOTSTRAP=off
#Environment=JWT_SECRET=change-me-to-a-random-32-char-string
#Environment=ADMIN_EMAIL=admin@libredb.org
#Environment=ADMIN_PASSWORD=
# AI query assistance
#Environment=LLM_PROVIDER=gemini
#Environment=LLM_API_KEY=
#Environment=LLM_MODEL=gemini-2.5-flash
#Environment=LLM_API_URL=http://127.0.0.1:11434/v1
# OIDC (login page reads NEXT_PUBLIC_AUTH_PROVIDER at runtime)
#Environment=NEXT_PUBLIC_AUTH_PROVIDER=oidc
#Environment=OIDC_ISSUER=https://example.auth0.com
#Environment=OIDC_CLIENT_ID=
#Environment=OIDC_CLIENT_SECRET=
# Persisted storage (default: sqlite under $SNAP_DATA — leave unset to keep it)
#Environment=STORAGE_PROVIDER=postgres
#Environment=STORAGE_POSTGRES_URL=postgresql://user:pass@127.0.0.1:5432/libredb?sslmode=disableStorage: by default the snap sets STORAGE_PROVIDER=sqlite and
STORAGE_SQLITE_PATH=$SNAP_DATA/libredb-storage.db — no further config is required. To switch
to server-side Postgres, uncomment the STORAGE_PROVIDER / STORAGE_POSTGRES_URL lines above
(TCP only; unix-socket Postgres on the host is not reachable under strict confinement).
Full variable reference: .env.example. OIDC setup details:
docs/OIDC.md. Storage providers: docs/STORAGE.md.
The single source of truth for the release tarballs also works locally (Linux and macOS):
bun install
bash scripts/build-standalone-payload.sh dist --smoke
# -> dist/libredb-studio-standalone-<version>-<os>-<arch>.tar.gz--smoke boots the packed payload with node server.js and requires
GET /api/db/health to return 200. Run the result via the npx launcher:
npx @libredb/studio --archive dist/libredb-studio-standalone-<version>-<os>-<arch>.tar.gz.
Release publishing is driven by two workflows, both triggered on release: published (plus
workflow_dispatch):
docker-build-push.yml— GHCR image (version +latesttags) and the Docker Hub mirror.release-artifacts.yml— standalone tarballs +SHA256SUMS,.deb/.rpm(nfpm,packaging/linux/), the Homebrew formula (packaging/homebrew/rendered byscripts/render-homebrew-formula.mjs), and the snap (snap/snapcraft.yaml).- The npm package (
@libredb/studio, which carries the npx launcherbin/studio.js) is published by the separate npm-publish workflow, also onrelease: published.
All channels have now had their first live run (the Snap publish completed its first with 0.9.52, validated per this runbook). Right after publishing a release:
- Watch the
release-artifactsrun: all four tarball legs green (if themacos-15-intelormacos-14runner labels ever disappear, check the current labels in actions/runner-images - do not fall back to retiredmacos-13or paid-largelabels blindly),.deb/.rpmuploaded with.sha256sidecars,SHA256SUMScomplete, tap push and snap jobs behaving per their secrets. npx @libredb/studio@<version>on a clean machine: download + checksum + first-run banner + login. Thenpx Engine Smokeworkflow (npx-engine-smoke.yml) runs automatically after a successful NPM Publish: it waits for the registry to serve the released version, then runs barenpx @libredb/studioon Node 20.9/22/24 and asserts each tier resolves exactly that release (the #130 regression class - npm's picker avoids engine-incompatible versions for bare specs). Check that it went green; dispatch it manually to re-run.brew tap libredb/tap && brew install libredb-studio && brew services start libredb-studio.- Download the
.debon Debian/Ubuntu:dpkg -i,systemctl start libredb-studio, health 200 on127.0.0.1:3000; verify the arm64 package on an arm64 machine (the CI smoke covers amd64 only; the bundled node arch is statically asserted for both).
Standalone tarballs and .deb/.rpm packages are checksum-verified against SHA256SUMS /
per-package .sha256 sidecars (see Release artifact naming) — both
from the same GitHub release. The .snap release asset ships no sidecar (--dangerous installs
skip the Snap Store's own verification), and the npm package tarball is verified separately
through npm's own registry integrity metadata — a different trust domain than the GitHub release.
Signed provenance (Sigstore/cosign signatures or SLSA attestations) across every channel is
tracked as a follow-up issue.
Optional-channel steps are skipped cleanly when their secret is absent, so forks and partial setups still publish the rest:
| Secret | Gates | Without it |
|---|---|---|
TAP_GITHUB_TOKEN |
Rendering and pushing the Homebrew formula to libredb/homebrew-tap (needs write access to that repo) |
Tap update skipped; tarballs still attach to the release |
SNAPCRAFT_STORE_CREDENTIALS |
The entire snap build/publish job (exported via snapcraft export-login) |
Snap job skipped |
DOCKER_HUB_TOKEN (+ DOCKER_HUB_USERNAME variable) |
The Docker Hub mirror push | GHCR-only publish |
distribution/channels.yaml is the machine-readable inventory
of every distribution channel: identity, update policy, provenance links, and (where measurable)
where its pinned version lives. bun run distribution:check
(scripts/distribution-check.mjs) compares every live
channel's pin against package.json and prints a markdown drift table; the weekly
distribution-check.yml workflow writes the same
table to its Job Summary (cron + manual dispatch — deliberately not release: published, which
GITHUB_TOKEN-published releases never fire). The checker only reads the inventory; bumping a
pin or editing a channel entry is always a human commit.
Tiers describe who controls publication:
| Tier | Meaning | Examples |
|---|---|---|
| 0 | Core registries, published directly by release CI | GitHub Releases, GHCR, Docker Hub, npm |
| 1 | Packaged formats owned by this repo, CI-published | Helm, Homebrew tap, Snap, .deb/.rpm |
| 2 | LibreDB-owned copies and listings, bumped by hand | CapRover source/mirror, Railway, Koyeb button |
| 3 | Upstream community catalogs, bumped via PR | CapRover official, Dokploy, Cosmos, Kubero |
| 4 | Partner or curated catalogs (not self-serve) | Rancher partner charts, Koyeb catalog, DO, winget |
SLAs (update.sla) state how quickly a channel is expected to follow a release:
every_release (bumped as part of releasing), minor_plus (bumped for minor releases and
above), major_only, on_demand (bumped when someone gets to it — the honest default for PaaS
catalog templates).
Pin strategies: local_file (version lives in files in this repo), remote_file (fetched
from an upstream raw URL, best-effort — fetch failures degrade to UNKNOWN and never fail the
run), none (nothing to measure, stated explicitly with a note). Measurable pins carry an
extract regex with exactly one capture group; when a source yields multiple different
matches the channel is reported instead of silently using the first hit.
Strict mode: bun run distribution:check --strict exits non-zero only for local_file
channels with sla: every_release that are drifted or unmeasurable. Remote catalogs and
on_demand templates never gate, so strict is enableable today without first paying off
historical PaaS drift. For the Helm chart the enforcement remains the required chart:check CI
gate (#138) — the matrix row is visibility, not a second gate. --json emits the rows for
scripting.
Adding a channel = one new entry in channels.yaml (copy a neighbour of the same tier; the
schema is validated on every run). Set links.first_pr to the PR that landed the listing, and
update links.last_bump_pr whenever a version-bump PR for that channel merges — it is null
until the first post-listing bump and is displayed, not auto-discovered.
- Snap Store listing screenshots: the description and icon ship with the snap
(
snap/snapcraft.yaml,public/logo.svg), but screenshots are a manual upload in the Snap Store web UI (https://snapcraft.io/libredb-studio/listing). The snap name is registered andSNAPCRAFT_STORE_CREDENTIALSis configured — the channel went live with 0.9.52. - Website install docs: the libredb-website documentation must be updated with the new channels (npx, Homebrew, .deb/.rpm, Snap) — a cross-repo step and part of issue #111's "README and website docs" acceptance criterion (and implicitly of #110/#112/#113).
- Windows: no standalone payload / installer yet; winget/Chocolatey and the
win32-x64tarball are tracked in issue #114. - Desktop app: a native desktop wrapper (Tauri v2 sidecar; unlocks AppImage, Flathub, .dmg,
Microsoft Store, brew cask) has a go recommendation —
see
docs/DESKTOP_WRAPPER_SPIKE.md.
Deviations and partial deliveries to record on the tracking issues when closing them, so the record matches the implementation:
- #110 (npx): the "Works on Linux, macOS, and Windows" acceptance criterion is NOT delivered
for Windows — the launcher exits on
win32with a Docker pointer. Amend/re-scope that criterion to #114 before closing. - #111 (Homebrew): the website half of "Install instructions added to README and website docs" is a cross-repo step (see Manual steps above).
- #113 (Snap): closed after the 0.9.52 live validation (store publish from release CI,
amd64+arm64 on
stable). Listing screenshots remain a store-side manual upload (see Manual steps above); version-bump auto-refresh is observable at the next release. - #115 (desktop wrapper spike): the written go/no-go recommendation is delivered
(
docs/DESKTOP_WRAPPER_SPIKE.md), but the hands-on spike scope (Tauri prototype, WebKitGTK/Monaco validation, unsigned PoC builds) was re-scoped into its Phase 1 — close as "recommendation delivered, hands-on spike open" and open the Phase 1 follow-up issue. - #118 (Helm AUTH_BOOTSTRAP): the chart originally defaulted to strict mode
(
config.authBootstrap: "off"), deviating from the issue's "default to the app default (on)" wording — generated credentials in centrally collected pod logs are undesirable. This was later reversed for the Rancher partner-charts certification, whose repository requires charts to be deployable with default values: the chart now defaults to""(zero-config bootstrap), and strict mode remains available viaconfig.authBootstrap=off.