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

# Desktop Release Packaging Guide (Local, Reproducible)

> Reproducible local packaging steps for the World Monitor desktop binary, including signing and notarization.

This guide provides reproducible local packaging steps for the desktop app.

**One binary ships.** `World Monitor` is the only desktop artifact, and every
variant — tech, finance, commodity, energy, happy — is selected in-app after
install and persists across restarts. There is no per-variant package, no
per-variant tag, and no per-variant download.

That is a deliberate constraint, not an omission: `/api/version` and
`/api/download` resolve `/releases/latest`, which returns at most one release,
so a second release line could never be served to users (#5908).

Bundle identity is controlled by a single Tauri config,
`src-tauri/tauri.conf.json` → `World Monitor` / `world-monitor`.

## Prerequisites

* Node.js + npm
* Rust toolchain
* OS-native Tauri build prerequisites:
  * macOS: Xcode command-line tools
  * Windows: Visual Studio Build Tools + NSIS + WiX

Install dependencies (this also installs the pinned Tauri CLI used by desktop scripts):

```bash theme={null}
npm ci
```

All desktop scripts call the local `tauri` binary from `node_modules/.bin`; no runtime `npx` package download is required after `npm ci`.
If the local CLI is missing, `scripts/desktop-package.mjs` now fails fast with an explicit `npm ci` remediation message.

## Network preflight and remediation

Before running desktop packaging in CI or managed networks, verify connectivity and proxy config:

```bash theme={null}
npm ping
curl -I https://index.crates.io/
env | grep -E '^(HTTP_PROXY|HTTPS_PROXY|NO_PROXY)='
```

If these fail, use one of the supported remediations:

* Internal npm mirror/proxy.
* Internal Cargo sparse index/registry mirror.
* Pre-vendored Rust crates (`src-tauri/vendor/`) + Cargo offline mode.
* CI artifact/caching strategy that restores required package inputs before build.

See `docs/TAURI_VALIDATION_REPORT.md` for failure classification labels and troubleshooting flow.

## Packaging commands

To view script usage/help:

```bash theme={null}
npm run desktop:package -- --help
```

### macOS (`.app` + `.dmg`)

```bash theme={null}
npm run desktop:package:macos
# or generic runner
npm run desktop:package -- --os macos
```

### Windows (`.exe` + `.msi`)

```bash theme={null}
npm run desktop:package:windows
# or generic runner
npm run desktop:package -- --os windows
```

Bundler targets are pinned in the Tauri config and enforced by packaging scripts:

* macOS: `app,dmg`
* Windows: `nsis,msi`

## Rust dependency modes (online vs restricted network)

From `src-tauri/`, the project supports two packaging paths:

### 1) Standard online build (default)

Use normal Cargo behavior (crates.io):

```bash theme={null}
cd src-tauri
cargo generate-lockfile
cargo tauri build --config tauri.conf.json
```

### 2) Restricted-network build (pre-vendored or internal mirror)

An optional vendored source is defined in `src-tauri/.cargo/config.toml`. To use it, first prepare vendored crates on a machine that has registry access:

```bash theme={null}
# from repository root
cargo vendor --manifest-path src-tauri/Cargo.toml src-tauri/vendor
```

Then enable offline mode using either method:

* One-off CLI override (no file changes):

```bash theme={null}
cd src-tauri
cargo generate-lockfile --offline --config 'source.crates-io.replace-with="vendored-sources"'
cargo tauri build --offline --config 'source.crates-io.replace-with="vendored-sources"' --config tauri.conf.json
```

* Local override file (recommended for CI/repeatable offline jobs):

```bash theme={null}
cp src-tauri/.cargo/config.local.toml.example src-tauri/.cargo/config.local.toml
cd src-tauri
cargo generate-lockfile --offline
cargo tauri build --offline --config tauri.conf.json
```

For CI or internal mirrors, publish `src-tauri/vendor/` as an artifact and restore it before the restricted-network build. If your organization uses an internal crates mirror instead of vendoring, point `source.crates-io.replace-with` to that mirror in CI-specific Cargo config and run the same build commands.

## Optional signing/notarization hooks

Unsigned packaging works by default.

If signing credentials are present in environment variables, Tauri will sign/notarize automatically during the same packaging commands.

### macOS Apple Developer signing + notarization

Set before packaging (Developer ID signature):

```bash theme={null}
export TAURI_BUNDLE_MACOS_SIGNING_IDENTITY="Developer ID Application: Your Company (TEAMID)"
export TAURI_BUNDLE_MACOS_PROVIDER_SHORT_NAME="TEAMID"
# optional alternate key accepted by Tauri tooling:
export APPLE_SIGNING_IDENTITY="Developer ID Application: Your Company (TEAMID)"
```

For notarization, choose one auth method:

```bash theme={null}
# Apple ID + app-specific password
export APPLE_ID="you@example.com"
export APPLE_PASSWORD="app-specific-password"
export APPLE_TEAM_ID="TEAMID"

# OR App Store Connect API key
export APPLE_API_KEY="ABC123DEFG"
export APPLE_API_ISSUER="00000000-0000-0000-0000-000000000000"
export APPLE_API_KEY_PATH="$HOME/.keys/AuthKey_ABC123DEFG.p8"
```

Then run either standard or explicit sign script aliases:

```bash theme={null}
npm run desktop:package:macos
# or
npm run desktop:package:macos:sign
```

### Windows Authenticode signing

Set before packaging (PowerShell):

```powershell theme={null}
$env:TAURI_BUNDLE_WINDOWS_CERTIFICATE_THUMBPRINT="<CERT_THUMBPRINT>"
$env:TAURI_BUNDLE_WINDOWS_TIMESTAMP_URL="https://timestamp.digicert.com"
# optional: if using cert file + password instead of cert store
$env:TAURI_BUNDLE_WINDOWS_CERTIFICATE="C:\path\to\codesign.pfx"
$env:TAURI_BUNDLE_WINDOWS_CERTIFICATE_PASSWORD="<PFX_PASSWORD>"
```

Then run either standard or explicit sign script aliases:

```powershell theme={null}
npm run desktop:package:windows
# or
npm run desktop:package:windows:sign
```

## Bundle identity (names/icons)

`src-tauri/tauri.conf.json` → `World Monitor` / `world-monitor`, with
`bundle.icon` pointing at the shared icon set. Every packaged build carries that
identity regardless of which variant the user later switches to, so the window
title and installed app name are always `World Monitor`.

Adding a second `tauri.*.conf.json` to rebrand a variant is deliberately
rejected by `tests/desktop-one-binary-model.test.mjs`: nothing publishes it, and
the endpoints cannot serve it. Reopen the packaging model in #5902 first if a
branded per-variant binary becomes a real requirement.

## Output locations

Artifacts are produced under:

```text theme={null}
src-tauri/target/release/bundle/
```

Common subfolders:

* `app/` → macOS `.app`
* `dmg/` → macOS `.dmg`
* `nsis/` → Windows `.exe` installer
* `msi/` → Windows `.msi` installer

## Release checklist (clean machine)

1. Build the package(s) for the required OS.
2. Move artifacts to a clean machine (or fresh VM).
3. Install/launch:
   * macOS: mount `.dmg`, drag app to Applications, launch.
   * Windows: run `.exe` or `.msi`, launch from Start menu.
4. Validate startup:
   * App window opens without crash.
   * Map view renders.
   * Initial data loading path does not fatal-error.
5. Validate bundle identity and in-app variant switching:
   * Window title and product name are `World Monitor`.
   * Switching to another variant reloads into that variant's panels/layers and
     survives a restart.
6. If signing was enabled:
   * Verify code-signing metadata in OS dialogs/properties.
   * Verify notarization/Gatekeeper acceptance on macOS.
