feat(desktop): native one-click installer with bundled PostgreSQL 17 + pgvector (macOS/Linux/Windows)#355
Merged
Merged
Conversation
Builds the native installer end-to-end on macOS+Linux (Windows non-blocking), wires it to run in-run from auto-release.yml (GITHUB_TOKEN releases don't fire the release event), renames build/ -> buildResources/ (was gitignored so the signing hook was never committed), and folds in third-pass review fixes (embeddedDb lock leak, secrets fail-closed cache, supervisor supersession, updater snapshot quiescing).
argon2 + sharp are N-API (ABI-stable, no rebuild needed). Only better-sqlite3 is non-N-API; it publishes Electron prebuilds, so prebuild-install fetches one (no Visual Studio / node-gyp), with a source-rebuild fallback. Should unblock the Windows leg, which couldn't compile argon2/better-sqlite3 via node-gyp.
better-sqlite3 prebuild + N-API argon2/sharp make the Windows leg pass reliably (run all-green), so drop continue-on-error; keep the 30min timeout.
…tion) The provision step rebuilds only better-sqlite3; argon2/sharp are assumed N-API (ABI-stable, prebuilds reused). Require all three under ELECTRON_RUN_AS_NODE so a wrong assumption fails CI instead of crashing the kernel at boot.
…dules) extraResources native modules are copied verbatim into every arch's DMG, so a single arm64 runner produced an x64 DMG with an arm64 better_sqlite3.node that crashes on Intel Macs. Build macOS per-arch on matching runners (macos-latest= arm64 --arm64, macos-13=Intel --x64); each provisions its own arch's prebuild. Also: move run-artifact upload after notarize/verify (final stapled installers), and delete the temp signing keychain in teardown.
The per-arch split was correct but GitHub's macos-13 (Intel) runners queue for hours, blocking releases. Ship Apple-Silicon-only DMG/zip for v1 (the vast majority of active Macs); an arm64 DMG is honest where the broken x64 one wasn't. Intel x64 tracked as a follow-up (cross-compile x64 prebuilds on the arm64 runner).
The packaged app crashed at kernel boot (ERR_MODULE_NOT_FOUND @omadia/*): the npm
workspace symlinks node_modules/@omadia/* -> packages/* are ABSOLUTE on CI runners
and fs.cpSync({dereference:true}) left them as dangling symlinks to the build
machine. stage-runtime now replaces each @Omadia symlink with a real copy of its
resolved package. Verified locally: kernel activates all @Omadia plugins, 0
module-resolution errors (only the green CI build hid this — caught by booting the
actual packaged artifact).
PGlite (Postgres-in-WASM, exposed over pglite-socket) booted but crashed under the kernel's real query load (RuntimeError: unreachable) and is single-connection. Replace it with a real bundled PostgreSQL 17 + pgvector: full SQL compatibility (no WASM traps) and normal connection pooling. - embeddedDb.ts: drive initdb/postgres child procs directly (the embedded-postgres npm wrapper is asar-unaware). Loopback-TCP only, -A trust loopback-only, locale C. `stopping` flag so a deliberate shutdown is not logged as a crash. - supervisor.ts: drop GRAPH_POOL_MAX=1 (native PG pools normally); add OMADIA_EMBEDDED_DB=1 so the kernel treats the live DATABASE_URL as authoritative over the first-boot value frozen in the vault. - knowledge-graph-neon plugin: prefer env DATABASE_URL when OMADIA_EMBEDDED_DB=1, else keep vault precedence (cloud unchanged). Fixes a boot crash-loop when the embedded Postgres port drifts between launches (collision -> new port) while the vault holds the old port. - stage-runtime.mjs: stage the @embedded-postgres native tree to runtime/omadia-pg; pgvector gate now requires control + module + SQL. - electron-builder.yml: ship omadia-pg as extraResources, exclude embedded-postgres from the asar, mac arm64-only. - afterPack.js: detect Mach-O by magic bytes (PG binaries are extensionless); sign both omadia/ and omadia-pg/ trees. - desktop-apps.yml: provision pgvector for PG17 (brew) into the engine; matrix mac-only until Linux/Windows pgvector provisioning is wired. Validated on the packaged .app: auth/providers + health 200, 25/25 sustained, CREATE EXTENSION vector (0.8.3) loads, graceful shutdown, and a real port-drift boot recovers (55671 busy -> 55772, graph ready).
…engine
The CI-built .app failed to launch initdb/postgres:
dyld: Library not loaded: @loader_path/../lib/libicudata.68.dylib
because fs.cpSync({dereference:true}) does not preserve the engine's
relative same-dir symlinks (libicudata.68.dylib -> libicudata.68.2.dylib).
It rewrites them into ABSOLUTE links pointing at the build machine's
node_modules, which resolve locally (masking the bug) but dangle on CI
(/Users/runner/work/...) and on every end-user machine.
After staging, recreate each of the engine's relative symlinks verbatim
from the source tree (all 17 are relative + in-tree), so the dylib chain
resolves wherever the installer lands. Same class of bug as the @Omadia
workspace-symlink materialisation already handled above.
Verified: packaged .app now boots, cluster inits, "database system is
ready", no dyld errors.
macOS proved the bundled-native-Postgres engine end-to-end; bring back the other two platforms, each provisioning a PG17 pgvector for its embedded engine before staging: - Linux: install postgresql-17-pgvector from the pgdg APT repo and copy vector.so + control + SQL into the linux-x64 engine (lib/postgresql, share/postgresql/extension — same layout as macOS). - Windows: no prebuilt exists, so build vector.dll from source with MSVC (nmake /F Makefile.win) against a Chocolatey PG17 (headers + lib), then copy into the windows-x64 engine. Windows layout is FLAT: module in lib/, control + SQL in share/extension/. stage-runtime.mjs made platform-aware: - pgvector gate now checks the correct module/share paths per OS (Windows flat lib/ + share/extension; Unix nested under postgresql/). - engine symlinks restored from the @embedded-postgres pg-symlinks.json manifest (relative, in-tree) instead of walking real fs symlinks. The tarballs ship zero real symlinks (postinstall materialises them), so reading the manifest is robust regardless of postinstall timing and is required for Linux's versioned .so chain. macOS output unchanged (still 17 relative links); Windows manifest is empty (flat DLLs). macOS re-verified locally (0 absolute links, gate passes). Linux/Windows are CI-validated only — their engines can't run on the macOS dev box.
This was referenced Jun 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Ships the native one-click desktop installer for omadia (macOS / Linux / Windows) — an Electron shell that bundles and supervises the existing middleware kernel + web-ui plus a bundled native PostgreSQL 17 + pgvector, with no Docker and no separate Node runtime. Once merged,
auto-release.ymlbuilds and attaches the installers to every release automatically (thedesktop-appsreusable-workflow job).Why native Postgres (not PGlite)
The first iteration embedded PGlite (Postgres compiled to WASM) over
pglite-socket. It booted, but the WASM engine crashed (RuntimeError: unreachable) under the kernel's real query load and is single-connection. This PR replaces it with a real bundled PostgreSQL 17: full SQL compatibility (no WASM traps) and normal connection pooling. The DB engine is driven directly viainitdb/postgreschild processes (loopback-TCP, trust auth), staged asextraResourcesso it is executable on disk (never trapped in the asar).Platforms (all CI-green — run on this branch)
postgresql@17postgresql-17-pgvectornmakebuild from pgvector source (Chocolatey PG17 for headers)Validation
.appboot-tested:/api/v1/auth/providers+/health→ 200, 25/25 sustained queries,CREATE EXTENSION vector(0.8.3) loads and computes, graceful shutdown.database_urlonly from the vault (frozen at first boot), so a changed embedded-DB port would crash-loop boot. GatedOMADIA_EMBEDDED_DB=1makes the live env DSN authoritative on desktop (cloud unchanged). Verified: forced the frozen port busy → app drifted to a new port → graph reconnected, no crash.dyld: Library not loaded). Fixed by restoring relative in-tree symlinks from the engine'spg-symlinks.jsonmanifest. Re-verified: CI artifact boots on a different machine than the one that built it.vector.so/vector.dllplacement per platform layout, relative (not absolute) symlinks, valid PE32+/ELF arch.Signing (fail-soft)
Unsigned/ad-hoc without secrets; the build still produces installers. macOS reuses the proven
_HIGH5Apple secret names (Developer ID + notarization). Windows Authenticode is optional (WINDOWS_CSC_*).Known limitations / follow-ups
vector.so/vector.dllload is the same PG17 ABI bet proven on macOS.extraResourcescan't be arch-split). Intel x64 is a tracked follow-up.