Desktop Distribution
This project ships Vued for macOS as a signed and notarized Electron DMG, plus paired native headless runtime archives used by one-command installers.
Required Apple Setup
Install an AIRCAPS Developer ID Application certificate/private key in the local login keychain:
Create a notarytool profile:
If the certificate name differs, set:
Electron Builder chooses the Developer ID Application certificate type automatically; CSC_NAME
should be the identity qualifier without the Developer ID Application: prefix.
If the notary profile differs, set:
Build
npm run release:dist bumps the patch version once by default, builds the signed/notarized Desktop DMG/ZIP, then packages the current-machine Headless runtime with the same version. The signed Desktop artifacts are written to dist/, along with dist/desktop-release-manifest.json; the local Headless archive is written under dist/headless/releases/<version>/.
Use a fixed version when you need to rebuild or upload an existing release:
Verify manually when needed:
Upload To Supabase Storage
Create a public Supabase Storage bucket. The default scripts assume:
Recommended bucket layout:
Use immutable versioned paths for release history. The only mutable object is
desktop/macos/latest.json, which points the website to the current versioned
DMG URL.
latest-mac.yml is the Electron auto-update feed. It stays at the prefix root
and points to the current versioned ZIP. The DMG is for website downloads; the
ZIP is for in-app updates.
The upload script writes desktop-release-manifest.json and latest.json
before large files. It writes latest-mac.yml after the versioned ZIP so open
apps never see a new updater feed before the update artifact exists.
Use GitHub Actions Headless Release for the public headless latest manifest. To choose the version explicitly while building:
Desktop-only and headless-only scripts still exist for focused rebuilds:
Files larger than 6 MB use Supabase’s resumable Storage endpoint on the direct
storage hostname. The chunk size is fixed at 6 MB to match Supabase’s TUS upload
requirements. The default large-file transport is macOS curl; set
VUED_UPLOAD_TRANSPORT=node only when debugging Node transport behavior. To
upload only the DMG and release manifests for the website:
If notarization already completed and the artifacts are still present in dist/, upload without rebuilding:
Do not use local headless:dist:upload for the public latest channel unless all platform runtime folders are staged. The upload script refuses partial latest manifests by default.
Headless Linux And Windows
Do not publish Linux/Windows headless from a Mac build. The runtime includes the native better-sqlite3-multiple-ciphers module, so each platform should package on its own runner.
Use the GitHub Actions workflow:
Inputs:
Required repository secrets:
Optional repository variable:
The workflow packages:
Then it uploads every staged runtime in one final job, so headless/latest/manifest.json contains all supported platforms. After it finishes, verify:
The manifest should include at least macos/arm64, linux/x64, and windows/x64.
For a private test channel or an intentional one-platform upload:
To intentionally bypass the latest-channel safety check:
The bucket should be public for direct browser downloads. Keep SUPABASE_SERVICE_ROLE_KEY server-only. If an upload fails with fetch failed, verify:
SUPABASE_URL has no braces or path suffix, e.g. https://<project-ref>.supabase.co
SUPABASE_SERVICE_ROLE_KEY is a current service-role key
- the
downloads bucket exists and accepts objects around 200 MB
- local network/DNS can reach the Supabase project
VUED_SUPABASE_STORAGE_URL points to https://<project-ref>.storage.supabase.co if you use a nonstandard Supabase URL
Rotate the service-role key immediately if it is pasted into terminal logs, issue trackers, or chats.
Automatic Updates
Production builds use electron-updater with a generic provider pointed at:
The updater reads latest-mac.yml, downloads the versioned ZIP, verifies the
update, and stages installation through quitAndInstall(). The app checks on
startup, periodically, and when the desktop SSE stream receives desktop.release.available,
desktop.update.available, or desktop_release_available.
Users can approve automatic installs from the macOS app menu:
After approval, a downloaded update calls quitAndInstall() automatically.
Without approval, Vued shows a native “Restart and Update” prompt when the
download is ready. The renderer can also call desktopBridge().updater.install()
when the state is ready, for example from the Integrations settings page.
Plain app quit does not install updates; this avoids a macOS race where Squirrel
can still be staging the downloaded ZIP.
Website Context For vued.ai/app
The landing page should fetch:
Use downloadUrl from that manifest for the primary macOS install button.
Suggested page behavior:
- Primary CTA:
Download for Mac
- Secondary text:
Open the DMG, then drag Vued into Applications.
- Show file metadata from
latest.json: version, updated date, and optionally SHA-256.
- If
navigator.platform or UA is not macOS, keep the button visible but label it Download for Mac.
- Link directly to the DMG, not the raw
.app.
- Do not expose Supabase service-role credentials in the page; reads should use the public bucket URL only.
Supabase Storage public buckets can serve public download URLs, and Supabase stores assets behind a CDN. Use low cache control for latest.json and higher cache control for versioned artifacts.Last modified on June 30, 2026