feat(distribution): npx, Homebrew, deb/rpm, Snap, Helm strict bootstrap, desktop spike - epic #108 batch - #122
Conversation
…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).
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.
There was a problem hiding this comment.
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/studiolauncher (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.
| // (see the harness for the exact scenario). | ||
| // ============================================================================ | ||
|
|
||
| const nodeSqliteProbe = spawnSync("node", ["-e", "require('node:sqlite')"], { timeout: 30_000 }); |
| # Pinned Node LTS (Krypton). Keep >= the package.json "engines.node" floor | ||
| # and bump deliberately - the checksum verification below always follows. |
…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).
|
Hardening from an external review pass (2ea58f4):
|
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).
| 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 }); | ||
| } |
| # Port and bind address (defaults: 3000, all interfaces). | ||
| #PORT=3000 | ||
| #HOSTNAME=127.0.0.1 |
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.
… 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.
|
… 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.



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
wxwrite; on EEXIST the winner's values are re-read and injected. Deterministic race test; auth-bootstrap stays at 100% coverage.auth.ts,proxy.ts,oidc.ts) now consumesrc/lib/config/auth-env.ts. Behavior byte-identical for auth/login paths; proxy now throws typedAuthConfigError, OIDC now enforces the 32-char minimum. All existing tests pass unchanged;build:lib+attwgreen.bun:sqliteunder Bun,node:sqliteunder 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:sqliteis built into the declared Node >= 24 target with an almost identical API). `LIBREDB_SQLITE_DRIVER=bunscripts/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) +SHA256SUMSare built (matrix incl.macos-15-intel/macos-14), boot-smoked against/api/db/health, and uploaded to the release.npx @libredb/studiodownloads the platform tarball from the release, verifies its sha256 before unpacking, caches under~/.libredb-studio/<version>, and startsnode server.js(zero-config first run supplies credentials).--port,--archive/LIBREDB_STUDIO_ARCHIVEoverrides; testable helpers inbin/lib/with unit tests. Library surface unchanged (npm packdelta isbin/only)./usr/bin/libredb-studiowrapper, hardened systemd unit (DynamicUser,StateDirectory,EnvironmentFile=-/etc/libredb-studio/env). Verified end to end: real .deb built locally and installed in anubuntu:24.04container, zero-env boot + health 200. Packages upload with.sha256sidecars.packaging/homebrew/libredb-studio.rb.tmpl(pinnednode@24,brew servicesblock 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 onTAP_GITHUB_TOKEN.snap/snapcraft.yaml(core24, strict confinement,daemon: simple, data underSNAP_DATA) and a release-workflow job gated onSNAPCRAFT_STORE_CREDENTIALS; the built .snap also attaches to the release.config.authBootstrapchart 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 --strictgreen.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/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-13runner label replaced withmacos-15-intel,actions/setup-nodeSHA-pinned, Homebrew pinned tonode@24(ABI match with the bundled runtime),STORAGE_PROVIDER=sqliteadded to the brew service, tap push switched tohttp.extraheaderauth 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
TAP_GITHUB_TOKENsecret (push access tolibredb/homebrew-tap) - Homebrew publishing stays skipped until thenSNAPCRAFT_STORE_CREDENTIALS- Snap publishing stays skipped until thennpx @libredb/studio,brew install libredb/tap/libredb-studio, and the .deb/.rpm downloads against itKnown follow-ups (not in this PR)
outputFileTracingExcludesor payload pruning