Skip to content

feat(distribution): npx, Homebrew, deb/rpm, Snap, Helm strict bootstrap, desktop spike - epic #108 batch - #122

Merged
cevheri merged 22 commits into
mainfrom
feat/distribution-channels
Jul 4, 2026
Merged

feat(distribution): npx, Homebrew, deb/rpm, Snap, Helm strict bootstrap, desktop spike - epic #108 batch#122
cevheri merged 22 commits into
mainfrom
feat/distribution-channels

Conversation

@cevheri

@cevheri cevheri commented Jul 4, 2026

Copy link
Copy Markdown
Member

Delivers every non-Windows/iOS workstream of epic #108 plus the open zero-config follow-ups as one reviewed branch. 12 commits, 46 files, +3244/-140.

Closes #115
Closes #116
Closes #119
Refs #108, #110, #111, #112, #113, #118 (in-repo work complete; final acceptance happens with the first release that publishes standalone artifacts)

What is in here

Workstream Issue Delivery
Multi-process bootstrap race guard #116 Fresh credentials file is created with an exclusive wx write; on EEXIST the winner's values are re-read and injected. Deterministic race test; auth-bootstrap stays at 100% coverage.
Central auth env config #119 The three duplicated JWT-secret readers (auth.ts, proxy.ts, oidc.ts) now consume src/lib/config/auth-env.ts. Behavior byte-identical for auth/login paths; proxy now throws typed AuthConfigError, OIDC now enforces the 32-char minimum. All existing tests pass unchanged; build:lib + attw green.
SQLite provider on Node - The sqlite DB provider now runs under both runtimes: bun:sqlite under Bun, node:sqlite under plain Node (architectural deviation from better-sqlite3, forced by Bun refusing to load better-sqlite3 in-process and the bun-installed binding targeting a different ABI than Node 24; node:sqlite is built into the declared Node >= 24 target with an almost identical API). `LIBREDB_SQLITE_DRIVER=bun
Standalone release artifacts #110/#111/#112 scripts/build-standalone-payload.sh (single source, CI + local) and .github/workflows/release-artifacts.yml: on every published release, four tarballs (linux-x64, linux-arm64, darwin-x64, darwin-arm64) + SHA256SUMS are built (matrix incl. macos-15-intel/macos-14), boot-smoked against /api/db/health, and uploaded to the release.
npx launcher #110 npx @libredb/studio downloads the platform tarball from the release, verifies its sha256 before unpacking, caches under ~/.libredb-studio/<version>, and starts node server.js (zero-config first run supplies credentials). --port, --archive/LIBREDB_STUDIO_ARCHIVE overrides; testable helpers in bin/lib/ with unit tests. Library surface unchanged (npm pack delta is bin/ only).
.deb / .rpm #112 nfpm packaging with a bundled, checksum-verified Node 24 runtime, /usr/bin/libredb-studio wrapper, hardened systemd unit (DynamicUser, StateDirectory, EnvironmentFile=-/etc/libredb-studio/env). Verified end to end: real .deb built locally and installed in an ubuntu:24.04 container, zero-env boot + health 200. Packages upload with .sha256 sidecars.
Homebrew #111 packaging/homebrew/libredb-studio.rb.tmpl (pinned node@24, brew services block incl. STORAGE_PROVIDER=sqlite), render script with unit tests, and a release-workflow step that pushes the rendered formula to libredb/homebrew-tap (repo already created), gated on TAP_GITHUB_TOKEN.
Snap #113 snap/snapcraft.yaml (core24, strict confinement, daemon: simple, data under SNAP_DATA) and a release-workflow job gated on SNAPCRAFT_STORE_CREDENTIALS; the built .snap also attaches to the release.
Helm strict bootstrap #118 config.authBootstrap chart value, default "off": Kubernetes deployments get strict auth by default (chart always injects real secrets; generated passwords do not belong in collected pod logs). Chart version bumped; helm lint --strict green.
Desktop wrapper spike #115 docs/DESKTOP_WRAPPER_SPIKE.md: Tauri v2 sidecar recommendation, lifecycle design, packaging matrix (AppImage/dmg/Flathub/MSIX/cask), signing cost table, phased go plan reusing the standalone tarballs as sidecar payload.
Docs - docs/DISTRIBUTION.md (canonical install/ops guide incl. image-tag model and maintainer secret matrix) + README install matrix.

Review process

Implemented as an orchestrated pipeline: 11 sequential implementation agents (each gate-checked), then 4 parallel adversarial reviewers (workflow/shell security, packaging realism, repo invariants, per-issue completeness), adversarial verification of every Critical/Important finding, and a single fix wave. 16 findings raised, 5 confirmed and fixed in 2ec72cb, notably: retired macos-13 runner label replaced with macos-15-intel, actions/setup-node SHA-pinned, Homebrew pinned to node@24 (ABI match with the bundled runtime), STORAGE_PROVIDER=sqlite added to the brew service, tap push switched to http.extraheader auth so the token never lands in .git/config.

Final gates on HEAD: format, lint (0 errors), typecheck, full test suite (all 18 groups), build, build:lib, attw, helm lint --strict - all green.

Maintainer setup required before/with the next release

  • Add TAP_GITHUB_TOKEN secret (push access to libredb/homebrew-tap) - Homebrew publishing stays skipped until then
  • Create the Snap Store account/name and add SNAPCRAFT_STORE_CREDENTIALS - Snap publishing stays skipped until then
  • Cut the next release (0.9.42) to produce the first standalone artifacts; then validate npx @libredb/studio, brew install libredb/tap/libredb-studio, and the .deb/.rpm downloads against it

Known follow-ups (not in this PR)

  • Standalone payload size: Next output file tracing pulls repo-root extras (docs/, charts/, e2e/) into the payload (~78 MB .deb); trim via outputFileTracingExcludes or payload pruning
  • Windows packaging remains Windows distribution: winget and Chocolatey packages #114; desktop wrapper implementation per the spike doc
  • First CI run on macOS legs is unverified until the next release triggers the matrix

cevheri added 12 commits July 4, 2026 03:46
…116)

bootstrapAuth wrote auth-bootstrap.json via temp+rename in every case, so
two processes booting fresh against one shared data dir could each
generate different credentials; last writer won the file while the loser
kept injecting divergent values, causing intermittent 401s.

Fresh creation now uses fs.writeFileSync with the wx (O_CREAT|O_EXCL)
flag, making the first write atomic across processes. On EEXIST the
losing process re-reads the file and adopts the winner's jwtSecret and
adminPassword, discarding its locally generated ones; only fields the
winner's file is missing keep the local values and are persisted via the
existing temp+rename update path, which remains last-writer-wins by
design for partial-field updates. The fail-open contract is unchanged:
bootstrapAuth still never throws.

Tests: deterministic EEXIST race coverage (full winner file and
partial winner file), and the temp+rename fallback tests now pre-seed a
partial file since fresh boots no longer take that path. Coverage on
src/lib/auth-bootstrap.ts stays at 100 percent lines and functions.
…/config/auth-env

Closes #119. auth.ts, proxy.ts, and oidc.ts each had their own JWT_SECRET
reader with drifting behavior (AuthConfigError vs plain Error, missing
min-length check in oidc). All three now consume a single stateless reader
in src/lib/config/auth-env.ts with the strictest semantics: AuthConfigError
when missing in production or shorter than 32 characters, and the
development fallback (with warning) outside production.

OIDC state encryption keeps its stricter no-fallback behavior via
allowDevFallback: false and its existing error message; it now also gains
the 32-character minimum and AuthConfigError. Consumers keep their own
lazy caches, so misconfiguration still surfaces at login/verify time
rather than module load. Behavior is otherwise unchanged; all existing
auth, proxy, oidc, and login tests pass unmodified.
Extract driver loading into a small internal adapter
(src/lib/db/providers/sql/sqlite-driver.ts) that picks the runtime's
built-in SQLite driver: bun:sqlite under Bun, node:sqlite (DatabaseSync)
under Node, with a LIBREDB_SQLITE_DRIVER=bun|node override for
deterministic tests. Both drivers load lazily via dynamic import and the
provider's public behavior (results, error mapping) is unchanged.

node:sqlite is used for the Node path instead of better-sqlite3 because
Bun refuses to load better-sqlite3 outright and its native binding must
match the installing runtime's ABI (a bun-installed binding fails under
Node with NODE_MODULE_VERSION mismatch), while node:sqlite is built into
the package's Node >= 24 target with no native dependency.

tsup: keep node:-prefixed builtin imports intact (removeNodeProtocol:
false) - tsup otherwise rewrites import("node:sqlite") to
import("sqlite"), which does not exist - and mark bun:sqlite external
so the driver adapter bundles cleanly.

Tests: driver-name resolution cases (override + runtime default) run
in-process; the core CRUD/schema/maintenance/error-mapping cases run
against the node driver in a real node subprocess (harness bundled with
bun build --target=node, temp on-disk database via mkdtempSync), since
Bun cannot load any non-bun SQLite driver in-process.

Docs tri-sync: docs/providers/sqlite.md gains a Runtime & driver
selection section and loses the Bun-only constraint; providers README,
CLAUDE.md, FEATURES.md, and .env.example updated to match.

Prerequisite for the npx launcher story (#110).
…ases

Add scripts/build-standalone-payload.sh as the single source of truth for
assembling the standalone Next.js server payload. It mirrors exactly what
the Dockerfile runner stage copies (standalone output, .next/static,
public, better-sqlite3 native binding plus its runtime deps, and the
lazy-imported @libredb/libredb package), verifies the native binding
loads under the target node (rebuilding it if the installed ABI does not
match), strips local .env files and dev databases, and packs
libredb-studio-standalone-<version>-<os>-<arch>.tar.gz. An optional
--smoke flag extracts the produced tarball, boots node server.js on a
random port with a temp STORAGE_SQLITE_PATH, and requires
GET /api/db/health to return 200 within 30s.

Add .github/workflows/release-artifacts.yml: on release published (or
workflow_dispatch with a semver-validated version input that must match
package.json, mirroring the docker-build-push guard), build the tarball
on linux-x64, linux-arm64, darwin-x64 and darwin-arm64 runners with the
smoke test, then aggregate all tarballs, generate SHA256SUMS, and attach
everything to the GitHub release. Event payload values are passed via
env, never inlined in run scripts. These tarballs are the canonical
artifact for the npx, homebrew and deb channels.
Add a bin entry so 'npx @libredb/studio' starts LibreDB Studio without
shipping the server build inside the npm package (which stays a pure
library for libredb-platform; tarball only gains bin/, ~14 kB).

bin/studio.js is a dependency-free Node >= 24 ESM launcher that:
- maps process.platform/arch to the release artifact name produced by
  scripts/build-standalone-payload.sh (linux/darwin x x64/arm64)
- downloads the tarball plus SHA256SUMS from the GitHub release matching
  its own package version into ~/.libredb-studio/<version>/, skipping
  downloads and unpacking when cached
- verifies the sha256 (node:crypto) before unpacking with tar, deleting
  corrupted downloads
- spawns 'node server.js' from the payload with stdio inherit, the full
  environment forwarded, and SIGINT/SIGTERM propagation; missing secrets
  are handled by the server's zero-config bootstrap (#109)
- supports --port <n>, --archive <path> (or LIBREDB_STUDIO_ARCHIVE) to
  bypass download and checksum for locally built tarballs, and --help
- on Windows points to Docker and issue #114; on 404 explains that the
  release has no standalone artifacts yet and suggests --archive or a
  newer release

Pure helpers (artifact naming, SHA256SUMS parsing, hashing, cache path,
arg parsing) live in bin/lib/launcher-utils.mjs and are unit tested
without network access. bin/package.json scopes "type": "module" to
the launcher because the library dist ships CJS .js files.
Add packaging/linux with a single nfpm config that produces both formats
from the linux standalone tarballs:

- nfpm.yaml: env-templated (VERSION, ARCH, PAYLOAD_DIR with expand: true);
  payload installs under /usr/lib/libredb-studio, MIT license, maintainer
  and homepage metadata, /etc/libredb-studio/env as config|noreplace
- libredb-studio wrapper (/usr/bin): execs the bundled private Node
  runtime against the payload server.js
- libredb-studio.service: DynamicUser + StateDirectory, sqlite storage in
  /var/lib/libredb-studio, EnvironmentFile=-/etc/libredb-studio/env, and
  hardening (NoNewPrivileges, ProtectSystem=strict, ReadWritePaths, ...)
- fetch-node.sh: downloads the pinned Node 24.18.0 LTS dist tarball for
  the target arch, verifies it against the official SHASUMS256.txt, and
  ships only bin/node inside the payload

Extend release-artifacts.yml with a linux-packages job (amd64 + arm64):
unpack the standalone tarball, bundle Node, build both packages with a
pinned checksum-verified nfpm 2.47.0, smoke test the .deb on the amd64
runner (dpkg -i + /api/db/health + first-run banner), and upload
libredb-studio_<version>_<arch>.deb and
libredb-studio-<version>.<arch>.rpm to the GitHub release.

Verified locally end to end: amd64 .deb built with nfpm from a real
payload installs on ubuntu:24.04 (dpkg -i, systemd-analyze verify OK,
health 200, zero-config banner) and the .rpm installs and serves health
200 on rockylinux:9.

Closes #112
Add packaging/homebrew/libredb-studio.rb.tmpl, a complete formula template
(per-platform release tarball URLs, brew services definition, launcher that
runs the standalone server under Homebrew's node), and
scripts/render-homebrew-formula.mjs, which fills the version and the four
per-platform sha256 placeholders from the release SHA256SUMS file and
refuses to emit a half-rendered formula.

The release-artifacts publish job now renders the formula after SHA256SUMS
is generated and pushes it to libredb/homebrew-tap as
Formula/libredb-studio.rb - gated on the optional TAP_GITHUB_TOKEN secret
exactly like the DOCKER_HUB_TOKEN gate in docker-build-push.yml, so forks
without the secret skip the step with a notice.

The renderer is unit tested against the real template with a fixture
SHA256SUMS (tests/unit/render-homebrew-formula.test.ts); the rendered
formula passes ruby -c and brew style (two remaining autocorrectable cops
suggest DSL helpers newer than what older Homebrew installs support).

Part of the distribution-channels epic #108; closes #111 delivery half
(the tap repository itself is created separately).
Add snap/snapcraft.yaml (issue #113): a strictly confined core24 server
snap running the standalone payload as a daemon on port 3000, with state
under SNAP_DATA. The payload part stages the prebuilt standalone payload
from snap-payload/ and bundles the pinned Node runtime via the existing
packaging/linux/fetch-node.sh; the snap version is adopted from
package.json at build time. TCP database connections are the supported
path; unix-socket access to host databases is out of scope under strict
confinement (documented in the recipe).

Extend release-artifacts.yml with a snap job per arch (amd64/arm64) that
unpacks the linux standalone tarball into snap-payload/, builds with
snapcore/action-build, publishes to the Snap Store stable channel with
snapcore/action-publish, and attaches the .snap to the GitHub release.
The job is gated on SNAPCRAFT_STORE_CREDENTIALS following the Docker Hub
gating pattern, so forks without the secret skip it cleanly.
… default

Add config.authBootstrap to the chart (default "off"): Kubernetes
deployments inject real secrets, and generated credentials in pod logs
are undesirable with central log collection. The value is rendered into
the ConfigMap as AUTH_BOOTSTRAP only when non-empty, so "" falls back
to the app default (on). Document zero-config vs strict mode in the
chart README, including the persistence.enabled=true requirement for
generated credentials to survive pod restarts. Bump chart version to
0.1.1.

Closes #118
Written deliverable for the desktop-wrapper spike: recommend Tauri v2
with the standalone server as a Node sidecar, justified against
Electron (bundle size, memory, updater) with the concrete blockers
that would force Electron and why none hold up.

Covers the sidecar lifecycle design (loopback free-port selection,
/api/db/health boot gate matching the release smoke test, bounded
crash-restart policy, clean shutdown), how the wrapper reuses the
zero-config bootstrap (#109) via env-var precedence for a one-shot
localhost-only admin session so the user never sees a login form, the
payload strategy reusing the per-platform standalone tarballs (Node
runtime bundled via the fetch-node.sh pattern, better-sqlite3
prebuilds, the missing win32 payload), a packaging matrix (AppImage,
deb overlap, dmg, Flathub, MSI/MSIX, brew cask), signing and
notarization costs, and a phased spike -> PoC -> store-submission plan
with a conditional GO and the follow-up issues to open.

No build scaffolding is added; this is documentation only.
Add docs/DISTRIBUTION.md as the canonical per-channel install and
operations guide: Docker (image tag model), Helm (strict authBootstrap
default), npx launcher, Homebrew, .deb/.rpm (systemd +
/etc/libredb-studio/env), Snap, the zero-config first run and
AUTH_BOOTSTRAP=off strict mode, the release-artifact naming scheme, and
maintainer notes on the CI secrets that gate publishing plus the manual
store steps still open. Add a compact Install matrix to the README
linking to the guide, and cross-link the desktop wrapper spike and the
SQLite runtime driver note.
Review-wave fixes for the distribution-channels branch:

- release-artifacts.yml: replace the retired macos-13 label with
  macos-15-intel (darwin-x64 leg would otherwise never get a runner and
  kill every downstream release job); SHA-pin actions/setup-node to
  v4.4.0 like every other action; require workflow_dispatch runs to be
  on the exact release-tag commit before --clobber uploads; clone/push
  the Homebrew tap with a per-command http.extraheader instead of a
  token-in-URL that persists in .git/config; upload per-file .sha256
  sidecars for the .deb/.rpm packages.
- Homebrew formula: depend on node@24 (the payload's better-sqlite3
  binding targets the Node 24 ABI; the floating node formula is a newer
  major and fails with ERR_DLOPEN_FAILED) and set
  STORAGE_PROVIDER=sqlite in the brew services block so server-side
  SQLite storage is actually on, matching the systemd unit and the snap.
- fetch-node.sh: verify the bundled Node runtime against sha256 digests
  pinned in-repo (both digests match the official SHASUMS256.txt for
  v24.18.0) instead of a checksum file fetched from the same origin.
- deb/rpm: add standard systemd maintainer scripts (daemon-reload plus
  restart-if-active on upgrade, stop/disable on removal, daemon-reload
  after removal) and stop overpromising in the package description.
- packaged wrapper: default STORAGE_SQLITE_PATH to the XDG state dir for
  direct non-root runs so the zero-config first run can persist its
  generated credentials (the payload dir under /usr/lib is read-only).
- docs/DISTRIBUTION.md: checksum-verification steps for deb/rpm installs,
  the XDG default for direct runs, website-docs and Snap-Store-screenshot
  items under manual steps, and issue close-out notes recording the
  Windows gap (#110 -> #114), the #118 strict-default deviation, and the
  #115 spike re-scope.
- auth-env.ts: drop the emoji from the JWT_SECRET warning (no-emoji rule).
Comment thread bin/studio.js Fixed
cevheri added 2 commits July 4, 2026 10:36
Extract createNodeSQLiteDriver with an injectable DatabaseSync ctor and an
injectable module importer so the node:sqlite adapter semantics (miss->null,
bigint changes normalization, option bridging) are unit-testable under Bun,
which cannot import node:sqlite. The real node:sqlite path stays covered by
the forced-Node subprocess integration test; the Bun-side import failure now
also pins the DatabaseConfigError wrap and error caching.
…che paths

CodeQL flagged the package.json version flowing into the release download
request (js/file-access-to-http). The flow is by design - the launcher's own
public version selects the artifact - but the value also lands in the cache
path, so a corrupted or tampered manifest could steer the request or escape
the cache directory. assertReleaseVersion now enforces plain semver inside
artifactName/releaseDownloadUrl/resolveCacheDir, and asset names are
URL-encoded.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR expands LibreDB Studio’s distribution story beyond Docker by adding a release-artifact pipeline (standalone tarballs + checksums) and layering multiple installers/consumers (npx launcher, Homebrew formula, deb/rpm packaging, Snap), while also tightening bootstrap/auth config and enabling the SQLite target DB provider to run under both Bun and Node via a runtime-selected driver adapter.

Changes:

  • Adds a GitHub Releases artifacts workflow producing standalone tarballs + SHA256SUMS, and builds/publishes downstream packages (deb/rpm, optional Homebrew tap update, optional Snap publish).
  • Introduces an npx @libredb/studio launcher (plus unit tests) that downloads, verifies, caches, and runs the standalone payload.
  • Consolidates JWT secret reading into src/lib/config/auth-env.ts, adds an auth-bootstrap multi-process race guard, and adds a Node-compatible SQLite provider driver path (node:sqlite) alongside Bun.

Reviewed changes

Copilot reviewed 43 out of 47 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tsup.config.ts Preserves node: protocol and externalizes sqlite builtins for lib builds.
tests/unit/render-homebrew-formula.test.ts Unit coverage for Homebrew formula rendering against SHA256SUMS.
tests/unit/lib/config/auth-env.test.ts Unit coverage for centralized JWT secret env reader behavior.
tests/unit/lib/auth-bootstrap.test.ts Extends auth-bootstrap tests for rename/copy fallback and EEXIST race cases.
tests/unit/launcher-utils.test.ts Unit coverage for pure npx launcher helper functions.
tests/unit/db/sqlite-driver.test.ts Unit coverage for the sqlite driver adapter (node:sqlite bridging + caching/errors).
tests/integration/db/sqlite-provider.test.ts Integration coverage for sqlite provider under Bun and (optionally) Node via subprocess harness.
tests/integration/db/sqlite-node-harness.ts Node-runtime harness for exercising sqlite provider behavior under node:sqlite.
src/proxy.ts Switches proxy JWT secret loading to the centralized auth-env reader.
src/lib/oidc.ts Uses centralized auth-env reader for OIDC state secret with strict semantics.
src/lib/db/providers/sql/sqlite.ts Replaces bun-only sqlite loading with runtime-selected driver adapter.
src/lib/db/providers/sql/sqlite-driver.ts Adds Bun/Node runtime driver selection and node:sqlite adapter implementation.
src/lib/config/auth-env.ts New single-source JWT_SECRET reader with consistent validation and typed errors.
src/lib/auth.ts Replaces local JWT reader with centralized auth-env reader and keeps lazy memoization.
src/lib/auth-bootstrap.ts Adds exclusive-create (wx) path and EEXIST adoption logic to avoid multi-process divergence.
snap/snapcraft.yaml New strict-confinement snap recipe bundling payload + pinned Node runtime.
snap/local/launch.sh Snap daemon launcher that execs the bundled node against payload server.js.
scripts/render-homebrew-formula.mjs Renders Homebrew formula from template + SHA256SUMS with placeholder validation.
scripts/build-standalone-payload.sh Builds standalone Next.js payload tarball and adds an optional smoke test.
README.md Adds install matrix and Linux packages section pointing to distribution docs.
packaging/linux/scripts/preremove.sh Stops/disables systemd unit on removal (not upgrade) for deb/rpm packages.
packaging/linux/scripts/postremove.sh Runs systemctl daemon-reload after removal/upgrade where applicable.
packaging/linux/scripts/postinstall.sh Reloads units and restarts service on upgrade if already running.
packaging/linux/nfpm.yaml nfpm config to build deb/rpm from payload + bundled Node + systemd/unit/env files.
packaging/linux/libredb-studio.service Hardened systemd unit for packaged installs (DynamicUser, strict FS protections).
packaging/linux/libredb-studio Packaged launcher wrapper that runs the bundled node and ensures writable sqlite path.
packaging/linux/fetch-node.sh Downloads and checksum-verifies a pinned Node runtime and stages only bin/node.
packaging/linux/env Template environment file for systemd-managed installs (secrets + optional config).
packaging/homebrew/libredb-studio.rb.tmpl Homebrew formula template consuming standalone release tarballs with pinned node@24.
package.json Adds a bin entry and publishes bin/ alongside dist/.
docs/providers/sqlite.md Updates provider docs for Bun/Node runtime driver selection and testing approach.
docs/providers/README.md Updates driver listing for sqlite to reflect Bun/Node built-in drivers.
docs/FEATURES.md Updates SQLite feature description to reflect runtime-selected built-in driver.
docs/DISTRIBUTION.md New canonical distribution/ops guide for all install channels and release artifacts.
docs/DESKTOP_WRAPPER_SPIKE.md New written desktop wrapper recommendation/spike deliverable (no implementation).
CLAUDE.md Updates architecture notes to reflect sqlite driver selection behavior.
charts/libredb-studio/values.yaml Adds config.authBootstrap value and defaults chart to strict bootstrap mode.
charts/libredb-studio/templates/configmap.yaml Wires AUTH_BOOTSTRAP env injection behind a values guard.
charts/libredb-studio/README.md Documents strict vs zero-config bootstrap modes and bumps chart version reference.
charts/libredb-studio/Chart.yaml Bumps chart version and records change note for AUTH_BOOTSTRAP exposure.
biome.json Includes bin/** in formatting/lint scope.
bin/studio.js Adds the npx launcher CLI that downloads/verifies/caches/extracts and spawns the payload.
bin/package.json Marks bin/ as ESM-only without changing the root package module type.
bin/lib/launcher-utils.mjs Adds pure helper utilities for artifact naming, URL building, checksums, and args parsing.
.gitignore Ignores snap build scratch directory and built .snap artifacts.
.github/workflows/release-artifacts.yml New workflow to build tarballs, attach assets, build deb/rpm, optionally update tap, optionally publish snap.
.env.example Documents advanced LIBREDB_SQLITE_DRIVER override for the sqlite target provider.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/lib/auth-bootstrap.ts Outdated
Comment thread src/lib/auth-bootstrap.ts Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 43 out of 47 changed files in this pull request and generated 4 comments.

// (see the harness for the exact scenario).
// ============================================================================

const nodeSqliteProbe = spawnSync("node", ["-e", "require('node:sqlite')"], { timeout: 30_000 });
Comment on lines +17 to +18
# Pinned Node LTS (Krypton). Keep >= the package.json "engines.node" floor
# and bump deliberately - the checksum verification below always follows.
Comment thread scripts/render-homebrew-formula.mjs Outdated
Comment thread .github/workflows/release-artifacts.yml Outdated
…ownloads

External review hardening (PR #122 review notes):
- All native channels now bind 127.0.0.1 by default: the npx launcher
  (with a new --host flag), the systemd unit (override via
  /etc/libredb-studio/env), the Homebrew service, and the snap daemon
  (override via systemctl edit). Docker/Helm keep 0.0.0.0 as containers
  require. Verified live: ss shows LISTEN on 127.0.0.1 only.
- Launcher downloads get an idle watchdog (abort after 60s without data)
  plus bounded retries with backoff for transient failures; definitive
  4xx answers still fail fast with guidance.
- New --verify-cache flag re-hashes the cached tarball against the cached
  SHA256SUMS and re-extracts; --archive docs now carry an explicit
  trusted-source warning.
- docs/DISTRIBUTION.md: network exposure matrix per channel and an
  artifact provenance roadmap note (tracked in #123).
@cevheri

cevheri commented Jul 4, 2026

Copy link
Copy Markdown
Member Author

Hardening from an external review pass (2ea58f4):

  • Local-first bind: every native channel now defaults to 127.0.0.1 - npx launcher (new --host flag), systemd unit (override in /etc/libredb-studio/env), Homebrew service, snap daemon (override via systemctl edit). Docker/Helm keep 0.0.0.0 as containers require. Verified live: ss shows LISTEN on 127.0.0.1 only.
  • Launcher download resilience: 60s idle watchdog (AbortController) plus bounded retries with backoff for transient failures; 404/4xx still fail fast with guidance.
  • New --verify-cache flag re-hashes the cached tarball against the cached SHA256SUMS and re-extracts; --archive now documents that it skips verification and is for trusted local tarballs only.
  • docs/DISTRIBUTION.md gains a per-channel network exposure matrix and an artifact provenance roadmap note; signed provenance (cosign/SLSA) is tracked in Sign release artifacts: Sigstore/cosign signatures or SLSA provenance #123.

Comment thread bin/studio.js Dismissed
External review follow-ups (PR #122):
- The version regexes in release-artifacts.yml, docker-build-push.yml and
  scripts/render-homebrew-formula.mjs now match bin/lib/launcher-utils.mjs
  exactly (a suffix must start with an alphanumeric, rejecting inputs like
  0.9.41..).
- The node:sqlite availability probe in the sqlite provider integration
  test uses a dynamic import, matching how the adapter actually loads the
  driver.
- The intentional sequential awaits in the launcher retry loop carry
  eslint-disable comments with reasons.
External review follow-ups (PR #122, third pass):
- release-artifacts concurrency no longer cancels an in-flight release run
  (a manual dispatch retry must not kill a half-finished asset upload);
  branch/dispatch runs stay cancelable.
- The linux-packages job statically asserts the bundled node binary
  matches the target architecture - the container smoke only covers amd64,
  so a wrong-arch arm64 bundle now fails the job instead of shipping.
- New --archive-sha256 flag pins a digest for local archives, closing the
  intentional --archive verification bypass when the caller has a known
  hash; malformed digests are rejected at parse time.
- docs/DISTRIBUTION.md gains a first-release validation runbook (macOS
  runner label guidance, per-channel manual acceptance steps).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 44 out of 48 changed files in this pull request and generated 2 comments.

Comment thread src/lib/auth-bootstrap.ts
Comment on lines +88 to +91
function createBootstrapFile(filePath: string, data: BootstrapFile): void {
fs.mkdirSync(path.dirname(filePath), { recursive: true });
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), { flag: "wx", mode: 0o600 });
}
Comment thread packaging/linux/env Outdated
Comment on lines +6 to +8
# Port and bind address (defaults: 3000, all interfaces).
#PORT=3000
#HOSTNAME=127.0.0.1
cevheri added 2 commits July 4, 2026 14:57
docs/API_DOCS.md - version bump, error envelope/code table, maintenance matrix, login payload, and auth env vars aligned to createErrorResponse/auth-bootstrap
docs/ARCHITECTURE.md - document standalone boot flow (auth-bootstrap, sample seeding) and runtime-adaptive SQLite driver selection
docs/DATABASE_PROVIDERS.md - add sqlite-driver.ts adapter, fix factory import path and async createDatabaseProvider signature
docs/DESKTOP_WRAPPER_SPIKE.md - clarify snap/homebrew publish gating on CI credentials
docs/DISTRIBUTION.md - correct runtime note (all channels run Node, not Bun) and expand artifact provenance/checksum coverage
docs/FEATURES.md - drop shipped SQL toolbar/batch-editing TODOs, renumber sections, add LibreDB embedded store and AI command bar details
docs/HELM_CHART.md - document LOG_LEVEL/AUTH_BOOTSTRAP env wiring and the chart's strict-default override
docs/OIDC.md - fix login UI file reference (login-form.tsx), correct discovery cache variable name, note JWT_SECRET has no dev fallback for OIDC state signing
docs/SEED_CONNECTIONS.md - add libredb type, document dismissed-seeds behavior replacing re-import on delete, correct validation error response
docs/STORAGE.md - correct migration description (whole-collection upsert, not ID-based dedup) and add dismissed_seeds collection
docs/TOOLCHAIN.md - sync biome.json snippet and format scripts with the as-implemented files.includes scoping
docs/editor/monaco-performance.md - add toggleAi to the QueryEditorRef interface
docs/editor/query-optimization.md - correct LIMIT/OFFSET injection behavior and estimate-mismatch threshold
docs/providers/README.md - add LibreDB provider row
docs/providers/libredb.md - remove stale design-spec links, fix factory.ts line reference
docs/providers/mongodb.md - fix source line references after code changes
docs/providers/mssql.md - fix source line references after code changes
docs/providers/mysql.md - fix source line references after code changes
docs/providers/oracle.md - fix source line references after code changes
docs/providers/postgres.md - fix source line references, drop stale status emoji, add planetscale to SSL host list
docs/providers/redis.md - fix source line references after code changes
docs/providers/sqlite.md - correct runtime/driver story across distribution channels and factory.ts snippet quoting
docs/ui/login-page.md - document login failure messaging (AuthConfigError) and AUTH_BOOTSTRAP env var
Follow-ups from the docs truth-pass consistency review:
- packaging/linux/env comment matched the old 0.0.0.0 default; now states
  loopback-only with the HOSTNAME=0.0.0.0 opt-in.
- docs/API_DOCS.md stale v0.5.x changelog replaced with a pointer to
  GitHub releases (single source of truth); footer date refreshed.
- charts: values.schema.json gains the config.authBootstrap property and
  Chart.yaml appVersion catches up to the released 0.9.41 (helm lint
  --strict green).
- CLAUDE.md tri-sync list includes the embedded libredb type-id, which
  already has its code/docs/tests triad.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 63 out of 74 changed files in this pull request and generated no new comments.

… code

- Test suite numbers measured, not estimated: 3,000+ tests (unit 1,609,
  api 279, integration 346, hooks 251, components 570), 32 E2E; coverage
  claim corrected from 96%+ to the SonarCloud-measured 90%+.
- SQLite driver row reflects the runtime-selected bun:sqlite/node:sqlite
  adapter; sample-data stack says PostgreSQL 18 (docker/postgres.yml).
- Environment table matches zero-config reality: ADMIN_PASSWORD and
  JWT_SECRET auto-generate unless AUTH_BOOTSTRAP=off, USER_* optional
  since #106; AUTH_BOOTSTRAP row added.
- Roadmap Phase 11 lists all five migration dialects the generator
  supports; stale helm --version pin dropped; broken nested bullet fixed.
@cevheri
cevheri merged commit 4d66b72 into main Jul 4, 2026
10 checks passed
@cevheri
cevheri deleted the feat/distribution-channels branch July 4, 2026 14:57
@sonarqubecloud

sonarqubecloud Bot commented Jul 4, 2026

Copy link
Copy Markdown

cevheri added a commit that referenced this pull request Jul 31, 2026
… discussion

The proposal was sitting on an unpushed local branch from 2026-07-01, one
machine away from being lost. It lands under docs/archived/backlogs/ next to
000-006 rather than reviving the top-level docs/backlogs/ directory the docs
cleanup removed - "archived" reads oddly for a live proposal, but one location
beats two conventions for a single file.

Discussion instead of an issue: there is no bug and no scoped feature yet, only
an architectural decision (config-as-env -> users-as-data) that has not been
made. An issue would sit open indefinitely; the thread can be converted to one
once a direction is chosen. Opened as libredb/discussions#254 under Ideas, and
the Status section links to it. This file stays the canonical text - it is
versioned alongside the code paths it references, which is how the two stale
references below were caught in the first place.

Two corrections against today's main, a month after drafting: the onboarding
motivation is superseded by the zero-config first run (#109, shipped in #122),
which generates and prints the admin password on first boot, so the
"login unavailable" wall the RFC opens with no longer exists. What survives is
the conceptual argument plus the hashing upside, both marked as such rather than
silently left to read as current.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants