Desktop Distribution
This project ships Vued for macOS as a signed and notarized Electron DMG.
Required Apple Setup
Install an AIRCAPS Developer ID Application certificate/private key in the local login keychain:
Developer ID Application: AIRCAPS, PBC (29RMTVJVXL)
Create a notarytool profile:
xcrun notarytool store-credentials "aircaps-notary" \
--apple-id "<apple-id>" \
--team-id 29RMTVJVXL \
--password "<app-specific-password>"
If the certificate name differs, set:
export CSC_NAME="AIRCAPS, PBC (29RMTVJVXL)"
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:
export APPLE_KEYCHAIN_PROFILE="aircaps-notary"
Build
npm run desktop:dist:preflight
npm run desktop:dist
The signed, notarized DMG/ZIP are written to dist/, along with dist/desktop-release-manifest.json.
Verify manually when needed:
codesign -dv --verbose=4 "dist/mac-arm64/Vued.app"
spctl --assess --type execute --verbose=4 "dist/mac-arm64/Vued.app"
spctl --assess --type open --context context:primary-signature --verbose=4 "dist/Vued-0.1.0-arm64.dmg"
xcrun stapler validate "dist/mac-arm64/Vued.app"
xcrun stapler validate "dist/Vued-0.1.0-arm64.dmg"
Upload To Supabase Storage
Create a public Supabase Storage bucket. The default scripts assume:
bucket: downloads
prefix: desktop/macos
Recommended bucket layout:
downloads/
desktop/macos/0.1.0/Vued-0.1.0-arm64.dmg
desktop/macos/0.1.0/Vued-0.1.0-arm64-mac.zip
desktop/macos/0.1.0/Vued-0.1.0-arm64-mac.zip.blockmap
desktop/macos/0.1.0/Vued-0.1.0-arm64.dmg.blockmap
desktop/macos/0.1.0/desktop-release-manifest.json
desktop/macos/latest.json
desktop/macos/latest-mac.yml
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.
export SUPABASE_URL="https://<project-ref>.supabase.co"
export SUPABASE_SERVICE_ROLE_KEY="<service-role-key>"
export VUED_RELEASE_BUCKET="downloads"
export VUED_RELEASE_PREFIX="desktop/macos"
VUED_UPLOAD_DRY_RUN=1 npm run desktop:dist:upload
npm run desktop:dist:upload
npm run desktop:dist bumps the current patch version by default and writes the
new version to package.json and package-lock.json. To choose the version
explicitly:
npm run desktop:dist -- --version 0.1.1
To bump another semver part:
npm run desktop:dist -- --bump minor
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:
npm run desktop:dist:upload:dmg
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:
https://<project-ref>.supabase.co/storage/v1/object/public/downloads/desktop/macos
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:
Vued > Install Updates Automatically
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:
https://<project-ref>.supabase.co/storage/v1/object/public/downloads/desktop/macos/latest.json
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 27, 2026