| layout | single | ||
|---|---|---|---|
| title | Release Guide | ||
| sidebar |
|
This guide covers release packaging for Windows, macOS, and Linux, plus how GitHub Actions publishes artifacts.
Run these before creating a beta or release tag.
npm run preflight:release:quickRuns everything in the full pre-flight below except the dist build and packaged-artifact checks
(--skip-dist): native-module loading (sharp, better-sqlite3, @napi-rs/canvas, lz4-napi,
node-llama-cpp), the critical-path baseline comparison and commercial compliance. It also needs a
clean git worktree. Use it to catch native-binding breakage and regressions early.
npm run preflight:releaseIt requires a clean git worktree before it begins. On the current platform this runs:
- Native module verification (
verify:native-modules) - Critical-path baseline comparison (
test:critical-path:twice, unless--skip-critical) - Commercial compliance verification (
compliance:commercial:verify, unless--skip-commercial) - Clean
release/build - Platform dist build (e.g.
dist:winon Windows) - Packaged artifact verification (
verify:packaged-artifacts)
scripts/stability/compare-baseline.js checks out two temporary git worktrees under
.tmp/stability-worktrees/ — the previous release tag (the baseline) and HEAD (the target) — and:
- Runs the fixed smoke suite (
scripts/stability/run-smoke-suite.js) on the baseline - Runs the same smoke suite on the target
- Runs the threshold check (
scripts/stability/check-release-thresholds.js), catching:- Required-journey failures
- Baseline-pass to target-fail regressions
- ≥50% latency regressions from baseline to target
If system-metrics exports are provided to the threshold script, it also enforces fallback-rate guardrails for search and Uncategorized routing.
npx jest --config test/jest.config.js test/components/WelcomePhase.test.js test/components/ModelSetupWizard.test.js
npx jest --config test/jest.config.js test/components/PhaseRenderer.test.js test/phaseTransitions.test.js- Pre-flight command exits with code
0 - Critical-path smoke suite passes on the candidate SHA with no regressions against the baseline tag
- Wizard/phase regression suites pass
- Dist artifacts are generated under
release/build - No unexpected warnings/errors in command output
- Fallback telemetry is reviewed when available (search fallback rate, Uncategorized routing rate)
.github/workflows/ci.yml(full CI gate).github/workflows/release.yml(Windows packaging/publish).github/workflows/mac-release.yml(macOS + Linux packaging/publish).github/workflows/manual-dist.yml(manual packaging builds)
- Update release metadata
- Bump
versioninpackage.json - Move release notes from
CHANGELOG.md[Unreleased] to the new version section
- Bump
- Run quality gates
npm run ci
- Smoke-test installers
- Windows: run
npm run dist:win, installStratoSortStack-Setup-*.exe - macOS: on a macOS host, run
npm run dist:mac:arm64, openStratoSortStack-*.dmg - Linux: on a Linux host, run
npm run dist:linux, launch the AppImage or install the.deb - Confirm the installed app actually launches after install, not just that packaging succeeded
- Windows: run
- Verify first-run experience
- AI setup confirms bundled OCR runtime availability
- Base-model download works from the app UI
- Disposable profile (temp demo, Windows) — optional, for demos or screen recordings without
touching your real
$env:APPDATA\StratoSort Stackdata. See Exact walkthrough: temp demo profile (Windows) below. - Confirm release branding inputs
- Either keep
assets/icons/StratoSortStackLogo.svg(or legacyassets/stratosort-logo.png) in the repo so workflows can regenerate icons/installer graphics - Or commit the generated files under
assets/icons/andassets/installer/
- Either keep
- Confirm docs
- Ensure
docs/INSTALL_GUIDE.mdmatches current installer names and OS prompts - Ensure release references are updated for the upcoming tag:
README.mdversion badgeCHANGELOG.mdcompare links ([Unreleased]and new version anchor).github/WORKFLOW_README.mdartifact names/checksum filenames
- Ensure
Packaged macOS builds intentionally skip Electron's single-instance lock on macOS 13.x and older
to avoid a launch-time Electron crash seen on older Intel macOS systems.
Tradeoff:
- Affected macOS versions may allow duplicate app launches instead of focusing an existing instance.
- This is preferred over a crash-on-launch failure for supported release artifacts.
Release guidance:
- When smoke-testing macOS releases, confirm the packaged app launches on the oldest macOS version you still support.
- Revisit this workaround after any Electron runtime upgrade (currently
^45.1.0inpackage.json; see.github/dependabot.ymlfor the upgrade policy).
Use a throwaway --user-data-dir when you need first-run–style behavior for a demo or recording,
without changing your normal profile under $env:APPDATA\StratoSort Stack (same folder as
%APPDATA%\StratoSort Stack in Explorer).
Use Windows PowerShell (not Command Prompt). The & operator is required to run an executable
whose path contains spaces.
-
Quit StratoSort completely (including the system tray if it stays open).
-
Create an empty folder for the disposable profile (adjust the path if you prefer):
New-Item -ItemType Directory -Force -Path C:\temp\stratosort-demo-profile
-
Launch the app with
--user-data-dirpointing at that folder. Use one of these:A — Packaged app (change the
.exepath if your install location differs):& "C:\Program Files\StratoSort Stack\StratoSort Stack.exe" --user-data-dir=C:\temp\stratosort-demo-profile
B — From the repo (after a normal dev build, from the repo root):
cd <path-to-your-clone> npx cross-env NODE_ENV=development electron . --enable-logging --user-data-dir=C:\temp\stratosort-demo-profile
-
Confirm you’re on the temp profile — open
C:\temp\stratosort-demo-profilein Explorer. After the UI loads, you should see new files (for examplesettings.json, logs, and later models if you download models). Your usual profile folder ($env:APPDATA\StratoSort Stack) should stay unchanged for this run. -
Record your demo — you should get first-run–style behavior. Allow time if large models download into this profile’s
modelsfolder. -
Quit the app when done.
-
Optional: delete the temp profile
Remove-Item -Recurse -Force C:\temp\stratosort-demo-profile
Your real profile under
$env:APPDATA\StratoSort Stackwas not used for that session.
Tip: If something feels stuck or locked, use a new folder name (for example
stratosort-demo-profile-2) for the next take.
Push a semver tag:
git tag vX.Y.Z
git push origin vX.Y.ZReplace X.Y.Z with the version from package.json after you bump it (for example v5.0.0).
This triggers:
mac-release.yml(macOS + Linux)release.yml(Windows)
Both also support workflow_dispatch against an existing tag for re-runs (e.g. after adding signing
secrets or fixing a transient build failure).
Release workflows are packaging/publish workflows, not full CI replacements. Run npm run ci before
tagging, then use the release workflows to build and upload artifacts.
release.yml writes a release's notes once, when it publishes: the "Release Notes" download and
signing bullets, then the version's CHANGELOG.md section. To correct a shipped version's notes,
fix its CHANGELOG.md section on main, then run Update Release Notes
(update-release-notes.yml) from the Actions tab with the release tag. It replaces only that
version's section on the release page (scripts/update-release-notes.js) and keeps the bullets
above it. dry_run is on by default and puts the change in the run summary; run it again with
dry_run off to edit the release. Re-running the release workflows instead would rebuild and
re-upload every artifact.
Windows workflow publishes:
StratoSortStack-Setup-*.exeStratoSortStack-*-win-x64.zip(x64 only: the no-install copy, see § Windows zip; never listed inlatest.yml)latest.yml— one channel file listing both the x64 and-arm64installers. electron-updater requests exactlylatest.ymlon Windows for every arch, so the x64 and arm64 matrix legs stagelatest-x64.yml/latest-arm64.ymlas artifacts and thepublish-releasejob merges them withscripts/merge-updater-metadata.js(x64 first — the updater serves the first.exeto clients whose arch appears in no filename) beforeverify-updater-metadataand upload. The staging names are never published.*.blockmapchecksums-windows-x64.sha256/checksums-windows-arm64.sha256(per matrix arch; installers, the x64 zip and blockmaps)
macOS workflow publishes:
StratoSortStack-*.dmgStratoSortStack-*.ziplatest-mac.yml— one channel file listing the x64 and arm64 zips/dmgs (signed + notarized builds only; unsigned builds ship no updater metadata). electron-updater requests exactlylatest-mac.ymlon macOS, so the per-archlatest-mac-<arch>.ymlstaging artifacts are merged inpublish-releasethe same way as Windows and are never published.checksums-macos.sha256
Linux workflow publishes:
StratoSortStack-*-linux-x86_64.AppImageandStratoSortStack-*-linux-arm64.AppImageStratoSortStack-*-linux-amd64.debandStratoSortStack-*-linux-arm64.deblatest-linux.yml(x64) andlatest-linux-arm64.yml(when produced)checksums-linux.sha256
Shipped installers check the public GitHub Releases feed for updates by default, so the repository
and its releases must stay public (private releases are not visible to electron-updater without
auth). STRATOSORT_ENABLE_AUTO_UPDATES=false disables the updater; optional
STRATOSORT_UPDATE_FEED_URL points at a generic HTTPS directory with latest.yml. See
CONFIG.md and
UPDATE_HOST.md.
The renderer/main wiring is oracled in unit + e2e suites (UpdateIndicator.test.js,
autoUpdater.test.js), but only a real installed build proves the end-to-end chain. Once per
update-enabled release:
- Build TWO versions: the release candidate v(N) and a throwaway v(N+1) (bump
package.jsonversion, rebuild). Both signed. - Stage v(N+1)'s
latest.yml+ installer +.blockmapin an HTTPS directory with a valid certificate (Cloudflare R2 / any static host).scripts/verify-updater-metadata.jsmust pass on the staged files. - On a clean Windows VM: install v(N), then launch with
STRATOSORT_UPDATE_FEED_URL=<staging URL>,STRATOSORT_UPDATE_FEED_ALLOWED_HOSTS=<staging host>. - Verify the full chain: update event → background download → "Update Ready" indicator → Apply → in-flight-op drain → relaunch as v(N+1).
- Negative checks: a copy extracted from the zip (no
Uninstall StratoSort Stack.exebeside the exe) refuses with the zip reason; a Microsoft Store build (process.windowsStore) refuses with the Store reason; a wrongALLOWED_HOSTSvalue blocks the feed. - Uninstall/reinstall v(N) to confirm the rollback story.
Use this after npm run dist:win or an official CI-built StratoSortStack-Setup-*.exe to confirm
the install and first-run path matches INSTALL_GUIDE.md.
- Quit StratoSort completely (tray included).
- Prefer a clean profile: new Windows user, VM, or disposable folder with
--user-data-dir(see the disposable profile section above).
From the repo root after a Windows dist build:
node scripts/verify-updater-metadata.js --platform=win --build-root=release/build
npm test -- test/autoUpdater.test.js --runInBand --no-coverage- Install — Run
release\build\StratoSortStack-Setup-<version>.exe(or your tagged artifact). - Profile path — Confirm app data lives under
%APPDATA%\StratoSort Stack(not the legacy lowercasestratosort-corepath that older postinstall scripts derived from the npm package name). - First launch — App opens without an updater error notification; the startup update check against GitHub Releases succeeds (or reports "up to date").
- Settings → Application — "Background Update Checks" is on and can be switched off (see CONFIG.md and UPDATE_HOST.md).
- Models — Complete welcome / model setup; optional "Download Base Models" and confirm
.gguffiles appear under%APPDATA%\StratoSort Stack\modelswhen downloads succeed.
Run these once per release on a packaged build. Every path below exists only when packaged:
main, preload and the renderer live inside the integrity-validated app.asar, while the eight
worker entry bundles (every entry in webpack.main.config.js except main) and the packages they
require at runtime are unpacked beside it (asarUnpack in electron-builder.json). A worker runs
from app.asar.unpacked/dist/ and resolves its requires only from
app.asar.unpacked/node_modules, so one that loads fine in npm run dev can still fail when
packaged — that is exactly how the settings-import worker broke in #441, and how the OCR,
image-preprocess and embedding workers crashed in v5.1.2
(Cannot find module 'pino-std-serializers') — so development runs prove nothing here.
- Renderer loads from inside the asar.
getRendererIndexPath()prefersapp.asar/dist/index.html; the unpacked renderer is a legacy fallback only. Confirm the window loads the in-asar copy (a packaged run that silently falls back meansasarUnpackwidened again). - OCR on an image — exercises the unpacked
ocrWorkerbundle plustesseract.js-core. - An embedding batch — exercises
embeddingWorkerand theEmbeddingQueueretry path. - An Atlas UMAP projection — exercises
umapProjectionWorker. - Log export (Settings → Diagnostics) — exercises
logExportWorker. - A settings import — exercises
settingsImportParserWorker(the #441 regression surface). - Vector-store restore at launch — exercises
oramaRestoreWorker. Relaunch a profile that already has a library and check that the[OramaVectorService] Restored <name> databaselog line carriesparsedIn: 'worker'. Restore only uses the worker for saved files of 4 MB or more (smaller ones always logparsedIn: 'main'), so use a library that large;'main'on a bigger file means the worker failed to load and restore fell back to the main thread. The packaged smoke workflow below checks this on every platform with a seeded 5 MB index. - Worker dependency trees.
verify:packaged-artifacts(pre-flight step 6) fails when a worker bundle's externals, with their full dependency closure, do not resolve insideapp.asar.unpacked. On the running app, confirm no log line shows a worker failing withCannot find module. - Analyze a document and an image — exercises
textExtractionWorkerandimagePreprocessWorker. - Visual C++ Redistributable on a clean Windows (Windows Sandbox has none:
C:\Windows\System32\msvcp140.dllandvcruntime140.dllabsent). Confirmresources\assets\runtime\vcredist\vc_redist.<arch>.exeis still Microsoft-signed in the installed copy (Get-AuthenticodeSignature:Valid, signerO=Microsoft Corporation, not Kennedy Applied Sciences). Interactive install: one UAC prompt for Microsoft's installer, a/passiveprogress window, then both DLLs exist and local AI loads with no banner. Repeat declining the UAC prompt: the download-page prompt appears, and in the app Install now → UAC → Restart StratoSort brings local AI up. Silent install (/S): runs/quietwithout dialogs. An auto-update (--updated) must not run it. On the zip build, Install now works the same way. On an Arm64 PC, the x64 package must still install the x64 runtime although System32 already has the ARM64 runtime's DLLs (afterwardsHKLM\SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64hasInstalled= 1). - Tamper test. Edit one byte inside
app.asar, then launch: the app must refuse to start. A successful launch means asar integrity is not being enforced. - Fuses on the built binary.
npx @electron/fuses read --app <path to the app>and compare againstelectronFusesinelectron-builder.json. (npm run verify:electron-fuses-configchecks the config in CI, including that noasarUnpackpattern unpacksdist/main.js; it cannot check the shipped bits.) - First-launch timing of both downloaded artifacts. On a clean Windows VM with Microsoft
Defender real-time protection left on, download the installer and the
-win-x64.zipfrom the release page (not from the build machine, so SmartScreen and the mark-of-the-web apply). Install and time the installer's first launch from double-click to first window. Extract the zip once toC:\StratoSortwith Explorer's Extract All (confirm it finishes without "path too long" errors), then time its first and second start. Expect each first start to take up to a minute or two while Defender scans the new files and later starts to be fast; a second zip start as slow as the first is a regression. Also confirm the extracted copy's log shows the updater skipping with the zip reason. Record the numbers in the release notes.
Two more release-time runtime items pair with this pass, both verifiable only on a packaged build:
- the updater live smoke — § Updater live smoke, including the zip/Store negative checks, and
- the license smoke on a fresh
--user-data-dir: with no key, Settings → License reportsfreeand every feature works; after pasting a valid key it reportslicensedwith the licensee and update window; a key whose update window ended before this build reportsversion_not_covered.
Windows Sandbox starts a clean Windows 11 with no Visual C++ runtime, no models and no profile, and discards it on close: the closest local stand-in for a new user's PC. It covers most of the packaged-integrity checks above without a VM (Windows Pro or Enterprise with the Windows Sandbox feature on).
.wsbconfig.<Networking>Disable</Networking>, so nothing can download a model or reach GitHub and a missing local file fails instead of being fetched;<VGpu>Disable</VGpu>;<MemoryInMB>of at least 5120. Map three host folders: the installer and zip read-only, the models read-only, and an output folder writable. Start an unattended PowerShell script from<LogonCommand>.- Models. Fill the host models folder with hard links (
New-Item -ItemType HardLink) to the.gguffiles, so nothing is copied and the read-only mapping protects the originals. Inside the sandbox, point%APPDATA%\StratoSort Stack\modelsat the mapped folder with a directory junction (mklink /J) and seedsettings.jsonwithmodelAutoDownloadSkipped: trueand thetextModel,visionModelandembeddingModelfile names. - Downloads as a user gets them. Copy the installer and zip into
%USERPROFILE%\Downloadsand give each copy a mark of the web (aZone.Identifierstream withZoneId=3).Expand-Archivedoes not carry the mark onto extracted files the way Explorer does, so add it to those too. - Run. Silent install (
/S), then the installed app's first and second launch; extract the zip toC:\StratoSortand launch it twice; then drop a few synthetic files into a watched smart folder and wait for analysis. Close the app withCloseMainWindow()before force-killing it, and copy%APPDATA%\StratoSort Stack\logsandsettings.jsonto the output folder before the script shuts the sandbox down. - What to look for in the logs.
[LlamaService] Initialized successfullyand noNoBinaryFoundErrororai.vc-runtime-missing(how the runtime gets installed is the Visual C++ Redistributable item in § Packaged-integrity checks); the OCR, image-preprocess and embedding worker pools running with noCannot find module; the zip copy's updater skipping with "was not installed with the StratoSort Stack installer"; and, on a relaunch with a large enough library,parsedIn: 'worker'on theRestored … databaseline. - What it cannot show. Sandbox turns Microsoft Defender off, so first-launch timing under real-time protection still needs a real VM (the timing item above). An unattended run clicks through no UAC prompt and no Install now, and the memory-limited sandbox can time out the vision model.
.github/workflows/packaged-smoke.yml is dispatch-only and publishes nothing. Run it on the release
branch before tagging (Actions → Packaged Smoke → Run workflow, or
gh workflow run packaged-smoke.yml --ref <branch>). Each leg can be switched off with its input.
- Windows x64 and arm64 use the same Azure Trusted Signing steps as
release.yml. Withrequire_signingon (the default), the leg fails unless our installer and app carry the Kennedy Applied Sciences signature andvc_redist.<arch>.exekeeps Microsoft's, in the built package and in the silently installed copy. That answers the Visual C++ item's signature check above. - macOS arm64 builds unsigned (
CSC_IDENTITY_AUTO_DISCOVERY=false), so it does not replace the signed and notarized check inmac-release.ymlandverify-mac-signing.yml. - Linux x64 launches
release/build/linux-unpacked(the files the AppImage wraps) underxvfbwith--no-sandbox. - Every leg launches the app with a throwaway profile through
scripts/packaged-launch-smoke.js(no model downloads, updater off). It passes only if coordinated startup completes, the native AI engine loads, the packaged OCR worker reads a synthetic image, and a seeded search index is restored through the restore worker, with noCannot find module, worker error,ERR_DLOPEN_FAILED, integrity failure or failed startup in the log. A GPU backend that fails and falls back to the CPU (Vulkan on the runner's Hyper-V GPU) is recorded undergpuFallbacksand allowed;NoBinaryFoundErroron the CPU backend fails the run. Logs, the verdict and the Windows signature table are uploaded aspackaged-smoke-<platform>-<arch>-<run number>. - Restore worker. Before launch the script writes a 5 MB LZ4-compressed
fileChunkscollection of 800 synthetic passages, in the app's own saved format, into the profile'svector-dbfolder. That is past the 4 MB threshold, so the run fails unless theRestored fileChunks databaseline showsparsedIn: 'worker'with all 800 documents inserted. A missing worker bundle or LZ4 binding would otherwise fall back to the main thread silently. - Still manual. The runners have no models and click nothing, so the vision model, Atlas, log export, settings import, the UAC and Install now paths, Defender timing, the tamper test and the updater live smoke stay with the checklist and the Windows Sandbox run above.
StratoSortStack-<version>-win-x64.zip is electron-builder's zip target (win.target in
electron-builder.json, named by the top-level artifactName). It archives the same win-unpacked
folder the installer is built from, with no wrapper folder, so the user extracts it once (to a short
path such as C:\StratoSort) and runs StratoSort Stack.exe from there; later starts reuse the
extracted files. It replaced the NSIS portable target after v5.0.0: that stub unpacked about 1.8
GB into %TEMP% on every launch and Defender rescanned it each time, which is why users saw a busy
cursor for a minute or more on every start.
- x64 only. The arm64 leg runs
electron-builder --win nsis --arm64, which overrideswin.target, so it ships the installer alone. - Never an update. app-builder-lib writes Windows update info only for
nsis(and opted-inappx) targets (isSuitableWindowsTargetinout/publish/PublishManager.js), and the zip target writes no blockmap.scripts/verify-updater-metadata.jsfails iflatest.ymllists anything but.exeinstallers. - Runtime guard.
resources/app-update.ymlis still inside the zip, because electron-builder writes it once into the sharedwin-unpackedfolder whenever annsistarget is in the build.getUpdateTargetCapability()insrc/main/core/autoUpdater.jstherefore treats a packaged Windows run withoutUninstall StratoSort Stack.exebeside the exe (the NSIS installer writes it into the install folder) as unable to update in place, instead of letting electron-updater run the installer against a different folder. - Signing. A zip carries no Authenticode signature. The release workflow verifies
win-unpacked\StratoSort Stack.exe, the exe the zip is packed from, alongside the installer. - Path length. The deepest file in the app (under
resources\app.asar.unpacked\node_modules\node-llama-cpp\dist\) is about 175 characters below the app folder, an estimate from the current dependency tree. Explorer's built-in extractor stops at 260 characters, which is why the install guide asks for a short folder.
scripts/verify-packaged-artifacts.js --platform=win --arch=x64 fails when the zip is missing.
The x64 app is about 1.8 GB on disk; arm64 is much smaller. Most of the difference is GPU prebuilts
that only exist for x64 (npm unpackedSize, version 3.22.2 of the node-llama-cpp packages and 1.1.3
of the whisper ones):
| Package | Size | Role |
|---|---|---|
@node-llama-cpp/win-x64-cuda-ext |
388 MB | CUDA 12 build of ggml-cuda.dll, loaded from fallback/ (see below) |
@node-llama-cpp/win-x64-cuda |
183 MB | Primary CUDA build, linked against the CUDA 13 runtime |
@node-llama-cpp/win-x64-vulkan |
74 MB | Vulkan build (AMD, Intel, NVIDIA without CUDA) |
@node-llama-cpp/win-x64 |
33 MB | CPU build |
@fugood/node-whisper-win32-x64-cuda / -vulkan / base |
125 / 42 / 3 MB | Whisper transcription variants |
arm64 ships @node-llama-cpp/win-arm64 (11 MB) and the arm64 whisper base and Vulkan variants, no
CUDA, and no bundled FFmpeg.
Why win-x64-cuda-ext stays. node-llama-cpp 3.22.2 (dist/bindings/Llama.js) first loads the
primary ggml-cuda.dll and tries the fallback/ folder of the ext package only when no GPU backend
loaded. The two DLLs need different CUDA libraries (from their PE import tables): the primary
imports cublas64_13.dll (CUDA 13 cuBLAS, runtime linked in), the fallback imports
cudart64_12.dll and cublas64_12.dll. So on an NVIDIA PC with a CUDA 12 toolkit the fallback is
the only way the text and embedding models run on CUDA; dropping the package would move those users
to Vulkan. The cudart DLLs the vision runtime stages (cudart-llama-bin-win-cuda-12.4-x64.zip by
default, or the 13.1 build when STRATOSORT_CUDA_VERSION=13.1, both from VisionService) do not
change this: WhisperService puts that folder on PATH only when it first loads for a
transcription, after the text and embedding engine has already chosen its backend, and
node-llama-cpp's startup CUDA check looks only on PATH and in the CUDA toolkit folders. CUDA 13
also needs a newer driver and no longer targets some older NVIDIA GPU generations that CUDA 12
supports (check NVIDIA's CUDA 13 release notes for the exact list before revisiting). Revisit only
if the app starts staging CUDA 13 libraries by default, before backend selection, and the
supported-GPU floor moves with it. verify-packaged-artifacts.js requires all four
@node-llama-cpp/win-x64* packages on x64.
Build-only files excluded (files in electron-builder.json). Each was checked against the
package code: nothing reads it unless it builds from source, and nothing builds from source inside
Electron.
| Pattern | Why it is unused at runtime | Estimated saving |
|---|---|---|
node-llama-cpp/llama/gitRelease.bundle |
Read only by utils/gitReleaseBundles.js, which only the source download/clone path uses. getLlama defaults build to "never" when process.versions.electron is set and the app never overrides it, so canBuild is false and the clone path is unreachable. |
36 MB, 1 file |
node-llama-cpp/llama/{addon,cmake,patches,profiles,toolchains,xpack}, llama/CMakeLists.txt |
Inputs to compileLlamaCpp (cmake-js), behind the same canBuild gate. |
0.3 MB, 57 files |
node-llama-cpp/templates |
Used only by the init CLI command. |
0.2 MB, 3 files |
@fugood/whisper.node/{src,whisper.cpp}, CMakeLists.txt |
C/C++ sources for building the addon. lib/ only requires the prebuilt variant packages (or build/Release). |
5.4 MB, about 117 files |
better-sqlite3/{deps,src} |
SQLite amalgamation and addon sources for node-gyp. lib/binding.js loads prebuilds/<platform>-<arch>.node or build/Release. |
10.4 MB, 33 files |
better-sqlite3/prebuilds/ other than this build's own |
getPrebuildPath() loads only <platform>-<arch>.node, or linuxmusl-<arch>.node on musl Linux. The rule keeps ${platform}-${arch} and ${platform}musl-${arch}: electron-builder expands ${platform} to the build machine's process.platform (each release leg builds on its own OS) and ${arch} per packed arch, and only Linux has musl prebuilds. |
15 MB, 7 files (Windows, macOS); 12 MB, 6 files (Linux) |
Together that is roughly 65 MB and 220 files less to write and scan on first launch (estimates from
the on-disk sizes in node_modules; the download shrinks less, by an estimated 40 to 50 MB, because
the installer compresses the source files well). Update the download size in
INSTALL_GUIDE.md after the next packaged build. verify-packaged-artifacts.js
checks that llama/binariesGithubRelease.json (read by node-llama-cpp on import) still ships and
that llama/gitRelease.bundle does not. @node-llama-cpp/win-arm64 (11 MB) declares
cpu: ["arm64", "x64"], so npm installs it on x64 runners too. The rule
!**/node_modules/@node-llama-cpp/win-!(${arch}|${arch}-*)/** excludes the other arch's llama
prebuilts: electron-builder expands ${arch} per packed arch, so the x64 build drops win-arm64
and the arm64 build (electron-builder.win-arm64.json extends the base config) drops any win-x64*
package. npm does not install win-* packages on macOS or Linux. verify-packaged-artifacts.js
fails if a package for the other arch still holds files. It also requires each build's own
better-sqlite3 prebuild (without it lib/binding.js falls back to build/Release, which is not
packaged, and SQLite cannot load) and fails if a prebuild for another platform or arch was packaged.
Keep every files rule in the top-level list. electron-builder turns the top-level strings into one
file set, but a string list under win, mac or linux becomes a second matcher that holds only
exclusions, and electron-builder starts such a matcher with **/*. 5.1.0 shipped that way: its
app.asar held the whole project folder (webpack cache, docs, sources, tests; about 300 MB more),
so every download grew by 57 to 82 MB instead of shrinking. verify-packaged-artifacts.js now fails
when app.asar holds anything besides dist, node_modules, package.json and src/shared, and
test/electron-builder-config.test.js runs electron-builder's own matchers for each release leg.
Tag-triggered release workflows support both official signed releases and unsigned ad-hoc artifacts. If the required signing/notarization secrets are missing, the workflows continue in unsigned mode. As of v4.0.0, macOS releases are signed and notarized when all five Apple secrets below are set.
Required credentials:
- Windows (Azure Trusted Signing): repository Variables
AZURE_TENANT_ID,AZURE_CLIENT_ID(identifiers, not secret) and repository SecretAZURE_CLIENT_SECRET(the only true credential). - macOS:
MACOS_CSC_LINK,MACOS_CSC_KEY_PASSWORD,APPLE_ID,APPLE_APP_SPECIFIC_PASSWORD,APPLE_TEAM_ID
Windows signing uses Azure Trusted Signing (account stratosort-signing, certificate profile
stratosortcore-public-trust-cert, endpoint https://eus.codesigning.azure.net/, publisher
Kennedy Applied Sciences, Inc.). Those four signing-identity values are public and stable, so they
are hardcoded in scripts/configure-win-signing.js. The workflow injects win.azureSignOptions
into the per-arch electron-builder config at build time via that script — the block is intentionally
absent from the committed configs so local and fork builds stay unsigned without Azure credentials.
electron-builder signs every .exe it copies through extraResources (bundled ffmpeg.exe and
llama-server.exe included). The bundled Microsoft Visual C++ Redistributable must keep Microsoft's
own signature — the app verifies the signature before its Install now runs it, and the
packaged-artifact verifier fails a build whose copy is not Microsoft-signed (the NSIS installer runs
the bundled file without a signature check of its own) — so win.signExts in
electron-builder.json excludes vc_redist.x64.exe / vc_redist.arm64.exe (pinned by
test/electron-builder-config.test.js), and scripts/verify-packaged-artifacts.js fails if the
packaged copy's sha256 differs from the staged one or, on a Windows host, if it is not
Microsoft-signed. Business/commercially licensed builds must confirm Visual Studio redistribution
licensing before shipping it (docs/COMMERCIAL_COMPLIANCE.md).
Credential sources:
AZURE_TENANT_ID/AZURE_CLIENT_ID(repo Variables) +AZURE_CLIENT_SECRET(repo Secret): a Microsoft Entra app registration (service principal) granted the Trusted Signing Certificate Profile Signer role on the signing account. The tenant/client IDs are plain identifiers; only the client secret authenticates. Paste with no trailing newline — the detect step inrelease.ymltrims the variables, but a clean paste avoids surprises.
For the end-to-end Windows runbook that produces the five macOS secrets from a Windows 11 host
without any Mac hardware (OpenSSL CSR + cert request + .p12 assembly + GitHub Secrets paste), see
MAC_SIGNING.md. The runbook is wrapped by npm run setup:mac-signing
(PowerShell helper at scripts/setup-mac-signing.ps1).
Unsigned macOS artifacts will not be notarized and may trigger Gatekeeper warnings on first launch. For one-off rebuilds or draft releases, you can still use the manual dist workflow or local dist commands instead of the tag-triggered workflows.
Recommended operator practice for ad-hoc unsigned builds:
- Ensure
checksums-*.sha256files are uploaded with each build. - Verify install flows manually on clean Windows/macOS test machines.
Use .github/workflows/manual-dist.yml (workflow_dispatch) when you need:
- Windows-only or macOS-only rebuilds
- A draft release with combined artifacts
- Ad-hoc release testing without tagging
npm ci
npm run dist:win# Run on macOS only
npm run dist:mac:arm64Windows cannot produce macOS DMG/ZIP artifacts locally. Build macOS distributions on a macOS host or
via the mac-release.yml / manual-dist.yml GitHub Actions workflows, which run on macos-latest.
All artifacts are written to release/build/.
Windows checksum file:
Get-ChildItem release/build -File |
Where-Object { $_.Name -match '^StratoSortStack-.*\.(exe|blockmap)$|^latest\.yml$' } |
ForEach-Object {
$hash = (Get-FileHash $_.FullName -Algorithm SHA256).Hash
"$hash *$($_.Name)"
} | Out-File release/build/checksums-windows-x64.sha256 -Encoding ASCII
# (CI generates per-arch files: checksums-windows-x64.sha256 / checksums-windows-arm64.sha256)macOS checksum file:
cd release/build
shasum -a 256 StratoSortStack-*.dmg StratoSortStack-*.zip > checksums-macos.sha256- The AI stack (node-llama-cpp + Orama) runs in-process.
- AI models are not bundled in installers; users download them in-app.
- OCR runtime support is bundled/fallback-ready via packaged runtime assets.