> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vued.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Desktop distribution

# 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:

```text theme={null}
Developer ID Application: AIRCAPS, PBC (29RMTVJVXL)
```

Create a notarytool profile:

```bash theme={null}
xcrun notarytool store-credentials "aircaps-notary" \
  --apple-id "<apple-id>" \
  --team-id 29RMTVJVXL \
  --password "<app-specific-password>"
```

If the certificate name differs, set:

```bash theme={null}
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:

```bash theme={null}
export APPLE_KEYCHAIN_PROFILE="aircaps-notary"
```

## Build

```bash theme={null}
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:

```bash theme={null}
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:

```text theme={null}
bucket: downloads
prefix: desktop/macos
```

Recommended bucket layout:

```text theme={null}
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.

```bash theme={null}
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:

```bash theme={null}
npm run desktop:dist -- --version 0.1.1
```

To bump another semver part:

```bash theme={null}
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:

```bash theme={null}
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:

```text theme={null}
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:

```text theme={null}
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:

```text theme={null}
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.
