From 14a5fb2c7fa0ccd022d78caca3944f57a56e22e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Flor=C3=AAncio?= Date: Sun, 28 Jun 2026 19:13:16 +0100 Subject: [PATCH] fix: support unsigned desktop releases (#1520) --- .agents/skills/release/SKILL.md | 4 +- .github/workflows/release.yml | 86 +++++++++++++------ apps/cli/src/release-config.test.ts | 29 ++++++- apps/desktop/AGENTS.md | 2 +- apps/desktop/src-tauri/windows-sign.ps1 | 9 +- docs/decisions/0026-tauri-desktop-shell.md | 2 +- docs/desktop-app.md | 2 +- docs/desktop-tauri-signing.md | 6 +- docs/plans/desktop-tauri-app/plan.md | 2 +- .../task-05-signing-and-release-trust.md | 2 +- docs/specs/desktop-tauri-app/spec.md | 14 +-- 11 files changed, 109 insertions(+), 49 deletions(-) diff --git a/.agents/skills/release/SKILL.md b/.agents/skills/release/SKILL.md index 56a722ec04..39ee3610e1 100644 --- a/.agents/skills/release/SKILL.md +++ b/.agents/skills/release/SKILL.md @@ -22,7 +22,7 @@ Kandev uses a **single SemVer** `X.Y.Z` shared across all distribution channels. Entirely in CI via `.github/workflows/release.yml`, triggered by a maintainer from the GitHub Actions UI: -1. Maintainer clicks "Run workflow" → picks `bump` (patch/minor/major) → optional `dry_run`. +1. Maintainer clicks "Run workflow" → picks `bump` (patch/minor/major) → optional `dry_run`, `allow_unsigned_desktop`, or `desktop_validation_only`. 2. `prepare` job bumps version + regenerates CHANGELOG, opens release PR, squash-merges, tags `vX.Y.Z`. 3. `build-web` + `build-cli` + `build-bundles` (5 platforms) build the release artifacts. 4. `publish-release` creates the GitHub release with platform tarballs + sha256 + auto-generated notes. @@ -31,6 +31,8 @@ Entirely in CI via `.github/workflows/release.yml`, triggered by a maintainer fr There is no local release script — the entire flow runs in GHA. +`allow_unsigned_desktop=true` only disables macOS/Windows desktop signing enforcement. It still creates and publishes the normal release outputs unless `desktop_validation_only=true` is also selected. `desktop_validation_only=true` builds artifacts from the current workflow ref for maintainer inspection and skips the release PR, tag, GitHub release, npm publish, Homebrew update, and public container tags. + ## Runtime resolution In `apps/cli/src/runtime.ts`, the CLI locates its bundled runtime via: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1bb480eb07..944a4f530c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -47,7 +47,11 @@ on: type: boolean default: false allow_unsigned_desktop: - description: "Allow unsigned macOS/Windows desktop artifacts for internal validation only" + description: "Allow unsigned macOS/Windows desktop artifacts; still publishes unless validation-only is checked" + type: boolean + default: false + desktop_validation_only: + description: "Build desktop artifacts only (no PR, tag, publish, npm, or tap update)" type: boolean default: false @@ -108,7 +112,7 @@ jobs: id: compute env: BUMP: ${{ inputs.bump }} - ALLOW_UNSIGNED_DESKTOP: ${{ inputs.allow_unsigned_desktop }} + DESKTOP_VALIDATION_ONLY: ${{ inputs.desktop_validation_only }} CURRENT_REF: ${{ github.sha }} run: | set -euo pipefail @@ -159,7 +163,7 @@ jobs: echo "Next tag: $TAG" echo "version=$NEXT" >> "$GITHUB_OUTPUT" echo "tag=$TAG" >> "$GITHUB_OUTPUT" - if [ "$ALLOW_UNSIGNED_DESKTOP" = "true" ]; then + if [ "$DESKTOP_VALIDATION_ONLY" = "true" ]; then echo "ref=$CURRENT_REF" >> "$GITHUB_OUTPUT" else echo "ref=$TAG" >> "$GITHUB_OUTPUT" @@ -176,7 +180,7 @@ jobs: fi - name: Bump version + generate CHANGELOG (in working tree) - if: ${{ !inputs.dry_run && !inputs.allow_unsigned_desktop }} + if: ${{ !inputs.dry_run && !inputs.desktop_validation_only }} env: NEXT: ${{ steps.compute.outputs.version }} TAG: ${{ steps.compute.outputs.tag }} @@ -214,7 +218,7 @@ jobs: git-cliff --tag "$TAG" -o CHANGELOG.md - name: Create release PR + squash-merge + tag - if: ${{ !inputs.dry_run && !inputs.allow_unsigned_desktop }} + if: ${{ !inputs.dry_run && !inputs.desktop_validation_only }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} NEXT: ${{ steps.compute.outputs.version }} @@ -269,14 +273,16 @@ jobs: } } - - name: Unsigned desktop validation summary - if: ${{ !inputs.dry_run && inputs.allow_unsigned_desktop }} + - name: Desktop validation summary + if: ${{ !inputs.dry_run && inputs.desktop_validation_only }} env: NEXT: ${{ steps.compute.outputs.version }} TAG: ${{ steps.compute.outputs.tag }} REF: ${{ steps.compute.outputs.ref }} + ALLOW_UNSIGNED_DESKTOP: ${{ inputs.allow_unsigned_desktop }} run: | - echo "Unsigned desktop validation for $TAG / $NEXT." + echo "Desktop validation for $TAG / $NEXT." + echo "Unsigned desktop artifacts allowed: $ALLOW_UNSIGNED_DESKTOP." echo "No release PR, tag, GitHub release, public container tags, npm publish, or Homebrew update will be created." echo "Build jobs will check out $REF." @@ -544,6 +550,11 @@ jobs: run: | set -euo pipefail + if [ "$ALLOW_UNSIGNED_DESKTOP" = "true" ]; then + echo "::notice::Unsigned desktop artifacts requested; skipping macOS signing and notarization setup." + exit 0 + fi + missing=() for key in APPLE_CERTIFICATE APPLE_CERTIFICATE_PASSWORD KEYCHAIN_PASSWORD; do if [ -z "${!key:-}" ]; then @@ -566,10 +577,6 @@ jobs: fi if [ "${#missing[@]}" -gt 0 ]; then - if [ "$ALLOW_UNSIGNED_DESKTOP" = "true" ]; then - echo "::warning::Unsigned desktop artifacts are internal validation only; missing macOS signing inputs: ${missing[*]}" - exit 0 - fi echo "::error::Public macOS desktop releases require signing and notarization inputs: ${missing[*]}" >&2 exit 1 fi @@ -662,6 +669,22 @@ jobs: WINDOWS_SIGNTOOL_PATH: ${{ secrets.WINDOWS_SIGNTOOL_PATH }} run: | set -euo pipefail + if [ "$ALLOW_UNSIGNED_DESKTOP" = "true" ]; then + unset APPLE_CERTIFICATE + unset APPLE_CERTIFICATE_PASSWORD + unset APPLE_ID + unset APPLE_PASSWORD + unset APPLE_TEAM_ID + unset APPLE_PROVIDER_SHORT_NAME + unset APPLE_API_KEY + unset APPLE_API_ISSUER + unset APPLE_API_KEY_PATH + unset APPLE_SIGNING_IDENTITY + unset WINDOWS_CERTIFICATE + unset WINDOWS_CERTIFICATE_PASSWORD + unset WINDOWS_TIMESTAMP_URL + unset WINDOWS_SIGNTOOL_PATH + fi pnpm -C apps --filter @kandev/desktop build:vite cd apps/desktop pnpm tauri build \ @@ -670,10 +693,9 @@ jobs: --bundles "${{ matrix.tauri_bundles }}" - name: Sign Windows desktop artifacts - if: matrix.platform == 'windows-x64' + if: matrix.platform == 'windows-x64' && !inputs.allow_unsigned_desktop shell: pwsh env: - ALLOW_UNSIGNED_DESKTOP: ${{ inputs.allow_unsigned_desktop }} WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }} WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }} WINDOWS_TIMESTAMP_URL: ${{ secrets.WINDOWS_TIMESTAMP_URL }} @@ -684,10 +706,6 @@ jobs: if ([string]::IsNullOrWhiteSpace($env:WINDOWS_CERTIFICATE_PASSWORD)) { $missing += "WINDOWS_CERTIFICATE_PASSWORD" } if ($missing.Count -gt 0) { - if ($env:ALLOW_UNSIGNED_DESKTOP -eq "true") { - Write-Warning "Unsigned desktop artifacts are internal validation only; missing Windows signing inputs: $($missing -join ', ')" - exit 0 - } Write-Error "Public Windows desktop releases require signing inputs: $($missing -join ', ')" exit 1 } @@ -803,7 +821,7 @@ jobs: docker-amd64: name: Build & push container image (amd64) needs: [prepare, build-bundles] - if: ${{ !inputs.dry_run && !inputs.allow_unsigned_desktop }} + if: ${{ !inputs.dry_run && !inputs.desktop_validation_only }} runs-on: ubuntu-latest permissions: contents: read @@ -874,7 +892,7 @@ jobs: docker-arm64: name: Build & push container image (arm64) needs: [prepare, build-bundles] - if: ${{ !inputs.dry_run && !inputs.allow_unsigned_desktop }} + if: ${{ !inputs.dry_run && !inputs.desktop_validation_only }} runs-on: ubuntu-24.04-arm permissions: contents: read @@ -933,7 +951,7 @@ jobs: docker-manifest: name: Assemble multi-arch base manifest needs: [prepare, docker-amd64, docker-arm64] - if: ${{ !inputs.dry_run && !inputs.allow_unsigned_desktop }} + if: ${{ !inputs.dry_run && !inputs.desktop_validation_only }} runs-on: ubuntu-latest permissions: contents: read @@ -987,7 +1005,7 @@ jobs: docker-universal-amd64: name: Build & push universal image (amd64) needs: [prepare, docker-manifest] - if: ${{ !inputs.dry_run && !inputs.allow_unsigned_desktop }} + if: ${{ !inputs.dry_run && !inputs.desktop_validation_only }} runs-on: ubuntu-latest permissions: contents: read @@ -1039,7 +1057,7 @@ jobs: docker-universal-arm64: name: Build & push universal image (arm64) needs: [prepare, docker-manifest] - if: ${{ !inputs.dry_run && !inputs.allow_unsigned_desktop }} + if: ${{ !inputs.dry_run && !inputs.desktop_validation_only }} runs-on: ubuntu-24.04-arm permissions: contents: read @@ -1090,7 +1108,7 @@ jobs: # docker-universal-manifest is included in publish-release's `needs` so # its size gate (and any per-arch build failure) blocks the release. needs: [prepare, docker-universal-amd64, docker-universal-arm64] - if: ${{ !inputs.dry_run && !inputs.allow_unsigned_desktop }} + if: ${{ !inputs.dry_run && !inputs.desktop_validation_only }} runs-on: ubuntu-latest permissions: contents: read @@ -1147,7 +1165,7 @@ jobs: publish-release: name: Publish GitHub release needs: [prepare, build-bundles, build-desktop, docker-universal-manifest] - if: ${{ !inputs.dry_run && !inputs.allow_unsigned_desktop }} + if: ${{ !inputs.dry_run && !inputs.desktop_validation_only }} runs-on: ubuntu-latest permissions: contents: write # softprops/action-gh-release creates the release @@ -1166,6 +1184,20 @@ jobs: OUTPUT: RELEASE_NOTES.md GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Add unsigned desktop warning to release notes + if: ${{ inputs.allow_unsigned_desktop }} + run: | + set -euo pipefail + tmp="$(mktemp)" + { + echo "> [!WARNING]" + echo "> macOS and Windows desktop installers in this release are unsigned." + echo "> They may require manual OS security bypasses and are not trusted downloads." + echo + cat RELEASE_NOTES.md + } > "$tmp" + mv "$tmp" RELEASE_NOTES.md + - uses: actions/download-artifact@v5 with: pattern: bundle-* @@ -1211,7 +1243,7 @@ jobs: publish-npm: name: Publish npm packages needs: [prepare, publish-release] - if: ${{ !inputs.dry_run && !inputs.allow_unsigned_desktop }} + if: ${{ !inputs.dry_run && !inputs.desktop_validation_only }} runs-on: ubuntu-latest permissions: id-token: write # Required for OIDC trusted publishing @@ -1246,7 +1278,7 @@ jobs: update-homebrew-tap: name: Update Homebrew tap needs: [prepare, publish-release] - if: ${{ !inputs.dry_run && !inputs.allow_unsigned_desktop }} + if: ${{ !inputs.dry_run && !inputs.desktop_validation_only }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 diff --git a/apps/cli/src/release-config.test.ts b/apps/cli/src/release-config.test.ts index dd21ee8e02..26efa8ca98 100644 --- a/apps/cli/src/release-config.test.ts +++ b/apps/cli/src/release-config.test.ts @@ -252,21 +252,39 @@ describe("release desktop artifacts", () => { tauri_bundles: deb,rpm`); }); - it("fails public macOS and Windows desktop builds closed when signing secrets are absent", () => { + it("supports unsigned desktop releases separately from validation-only builds", () => { const workflow = releaseWorkflow(); const signingDocs = readRepoFile("docs/desktop-tauri-signing.md"); const tauriConfig = readRepoFile("apps/desktop/src-tauri/tauri.conf.json"); const windowsSignScript = readRepoFile("apps/desktop/src-tauri/windows-sign.ps1"); expect(workflow).toContain("allow_unsigned_desktop"); - expect(workflow).toContain("Unsigned desktop artifacts are internal validation only"); + expect(workflow).toContain("desktop_validation_only"); expect(workflow).toContain("ref: ${{ needs.prepare.outputs.ref }}"); expect(workflow).toContain("persist-credentials: false"); - expect(workflow).toContain("Unsigned desktop validation summary"); + expect(workflow).toContain("Desktop validation summary"); expect(workflow).toContain("No release PR, tag, GitHub release, public container tags"); - expect(workflow).toContain("if: ${{ !inputs.dry_run && !inputs.allow_unsigned_desktop }}"); + expect(workflow).toContain("if: ${{ !inputs.dry_run && !inputs.desktop_validation_only }}"); + expect(workflow).toContain('if [ "$DESKTOP_VALIDATION_ONLY" = "true" ]; then'); + expect(workflow).toContain( + "Allow unsigned macOS/Windows desktop artifacts; still publishes unless validation-only is checked", + ); + expect(workflow).toContain("Add unsigned desktop warning to release notes"); + expect(workflow).toContain( + "macOS and Windows desktop installers in this release are unsigned.", + ); + expect(workflow).not.toMatch( + /if \[ "\$ALLOW_UNSIGNED_DESKTOP" = "true" \]; then\r?\n\s*echo "ref=\$CURRENT_REF"/, + ); expect(workflow).toContain("docker-amd64:"); expect(workflow).toContain("docker-universal-manifest:"); + expect(workflow).toContain('if [ "$ALLOW_UNSIGNED_DESKTOP" = "true" ]; then'); + expect(workflow).toContain("unset APPLE_CERTIFICATE"); + expect(workflow).toContain("unset APPLE_ID"); + expect(workflow).toContain("unset WINDOWS_CERTIFICATE"); + expect(workflow).toContain("unset WINDOWS_CERTIFICATE_PASSWORD"); + expect(workflow).toContain("unset WINDOWS_TIMESTAMP_URL"); + expect(workflow).toContain("unset WINDOWS_SIGNTOOL_PATH"); for (const key of [ "APPLE_CERTIFICATE", @@ -303,9 +321,12 @@ describe("release desktop artifacts", () => { expect(tauriConfig).toContain("windows-sign.ps1"); expect(tauriConfig).not.toContain('"csp": null'); expect(windowsSignScript).toContain('"https://timestamp.digicert.com"'); + expect(windowsSignScript).toContain("Skipping Windows signing for unsigned desktop artifact"); expect(windowsSignScript).toContain("Remove-Item -LiteralPath $certificatePath"); expect(signingDocs).toContain("Public recommended desktop releases require signing"); expect(signingDocs).toContain("allow_unsigned_desktop"); + expect(signingDocs).toContain("desktop_validation_only"); + expect(signingDocs).toContain("publishes the normal release outputs"); expect(signingDocs).toContain("does not publish a GitHub release"); expect(signingDocs).toContain("public container tags"); expect(signingDocs).toContain("Ubuntu 22.04"); diff --git a/apps/desktop/AGENTS.md b/apps/desktop/AGENTS.md index 595207e3c2..f0e4782065 100644 --- a/apps/desktop/AGENTS.md +++ b/apps/desktop/AGENTS.md @@ -32,4 +32,4 @@ Use `scripts/release/prepare-desktop-runtime.sh` and `scripts/release/verify-des ## Release -Desktop artifacts are built in `.github/workflows/release.yml` after the platform runtime bundles. Public macOS and Windows artifacts require signing unless `allow_unsigned_desktop=true` is explicitly selected for internal validation. +Desktop artifacts are built in `.github/workflows/release.yml` after the platform runtime bundles. Public recommended macOS and Windows artifacts require signing unless `allow_unsigned_desktop=true` is explicitly selected. Use `desktop_validation_only=true` for artifact-only validation runs that skip the release PR, tag, publish jobs, and public container tags. diff --git a/apps/desktop/src-tauri/windows-sign.ps1 b/apps/desktop/src-tauri/windows-sign.ps1 index f3a3f4cdae..8072d9305a 100644 --- a/apps/desktop/src-tauri/windows-sign.ps1 +++ b/apps/desktop/src-tauri/windows-sign.ps1 @@ -5,15 +5,16 @@ param( $ErrorActionPreference = "Stop" +if ($env:ALLOW_UNSIGNED_DESKTOP -eq "true") { + Write-Warning "Skipping Windows signing for unsigned desktop artifact." + exit 0 +} + $missing = @() if ([string]::IsNullOrWhiteSpace($env:WINDOWS_CERTIFICATE)) { $missing += "WINDOWS_CERTIFICATE" } if ([string]::IsNullOrWhiteSpace($env:WINDOWS_CERTIFICATE_PASSWORD)) { $missing += "WINDOWS_CERTIFICATE_PASSWORD" } if ($missing.Count -gt 0) { - if ($env:ALLOW_UNSIGNED_DESKTOP -eq "true") { - Write-Warning "Skipping Windows signing for internal unsigned desktop validation; missing: $($missing -join ', ')" - exit 0 - } throw "Public Windows desktop releases require signing inputs: $($missing -join ', ')" } diff --git a/docs/decisions/0026-tauri-desktop-shell.md b/docs/decisions/0026-tauri-desktop-shell.md index 3a4dbe91ef..90780fffae 100644 --- a/docs/decisions/0026-tauri-desktop-shell.md +++ b/docs/decisions/0026-tauri-desktop-shell.md @@ -14,7 +14,7 @@ Add a Tauri v2 desktop app as a thin native shell over the existing Kandev runti The installed desktop app will not include a Node.js runtime. Node.js and pnpm remain build-time tooling for Vite, TypeScript, Tauri CLI invocation, tests, and release automation. -Desktop launches set the backend to a loopback-only bind address and an app-selected local port. Public desktop releases require macOS Developer ID signing/notarization and Windows code signing before the artifacts are presented as trusted downloads. The first desktop implementation does not include Tauri's in-app updater; updates continue through GitHub release downloads/manual reinstall until a separate updater spec is approved. +Desktop launches set the backend to a loopback-only bind address and an app-selected local port. Public recommended desktop releases require macOS Developer ID signing/notarization and Windows code signing before the artifacts are presented as trusted downloads; unsigned desktop artifacts require an explicit release workflow override. The first desktop implementation does not include Tauri's in-app updater; updates continue through GitHub release downloads/manual reinstall until a separate updater spec is approved. ## Consequences diff --git a/docs/desktop-app.md b/docs/desktop-app.md index 8477835c8b..3cf2eec803 100644 --- a/docs/desktop-app.md +++ b/docs/desktop-app.md @@ -16,7 +16,7 @@ Supported desktop platforms: Public recommended macOS artifacts must be Developer ID signed and notarized. Public recommended Windows artifacts must be code signed. Linux artifacts are checksum-gated; package-manager signatures can be added later. -Unsigned macOS or Windows desktop artifacts are for internal validation only. They may require manual OS security bypasses and should not be presented as trusted public downloads. +Unsigned macOS or Windows desktop artifacts may be produced only through an explicit release workflow override. They may require manual OS security bypasses and should not be presented as trusted downloads. ## Runtime Requirements diff --git a/docs/desktop-tauri-signing.md b/docs/desktop-tauri-signing.md index 077927c1be..a5e436818e 100644 --- a/docs/desktop-tauri-signing.md +++ b/docs/desktop-tauri-signing.md @@ -2,7 +2,11 @@ Public recommended desktop releases require signing on macOS and Windows. The release workflow fails closed by default when those secrets are missing. -Use `allow_unsigned_desktop=true` only for internal validation builds. That mode may upload workflow artifacts for maintainer inspection, but it does not publish a GitHub release, npm packages, Homebrew updates, or public container tags. Unsigned desktop artifacts are internal validation only and must not be presented as trusted public downloads. +Use `allow_unsigned_desktop=true` only when intentionally producing unsigned macOS/Windows desktop artifacts. Unless `desktop_validation_only=true` is also selected, that mode still publishes the normal release outputs: the release PR and tag, GitHub release, npm packages, Homebrew update, public container tags, and desktop artifacts. Unsigned desktop artifacts may require manual OS security bypasses and must not be presented as trusted downloads. + +When unsigned desktop artifacts are published in a normal release, the workflow prefixes the GitHub release notes with an unsigned desktop warning. + +Use `desktop_validation_only=true` for maintainer inspection builds. That mode uploads workflow artifacts but does not publish a GitHub release, npm packages, Homebrew updates, or public container tags. ## macOS diff --git a/docs/plans/desktop-tauri-app/plan.md b/docs/plans/desktop-tauri-app/plan.md index 7f78bf4a91..7c9f52ed75 100644 --- a/docs/plans/desktop-tauri-app/plan.md +++ b/docs/plans/desktop-tauri-app/plan.md @@ -166,7 +166,7 @@ Add signing inputs/secrets behind conditional release steps: - Windows: certificate/key-vault or custom signing command wiring. - Linux: checksum required; GPG/AppImage signing optional. -Unsigned desktop builds remain allowed for dry runs and internal validation, but public recommended artifacts should require signing success on macOS and Windows. +Unsigned desktop builds remain allowed only through explicit workflow overrides, but public recommended artifacts should require signing success on macOS and Windows. --- diff --git a/docs/plans/desktop-tauri-app/task-05-signing-and-release-trust.md b/docs/plans/desktop-tauri-app/task-05-signing-and-release-trust.md index cac7e9bcd4..2533c9a7ab 100644 --- a/docs/plans/desktop-tauri-app/task-05-signing-and-release-trust.md +++ b/docs/plans/desktop-tauri-app/task-05-signing-and-release-trust.md @@ -14,7 +14,7 @@ spec: "../../specs/desktop-tauri-app/spec.md" - macOS desktop release builds support Developer ID signing and notarization through GitHub Actions secrets. - Windows desktop release builds support code signing through the selected signing mechanism. -- Unsigned desktop artifacts are clearly limited to dry runs/internal validation and are not treated as trusted public artifacts. +- Unsigned desktop artifacts require an explicit workflow override and are not treated as trusted public artifacts. ## Verification diff --git a/docs/specs/desktop-tauri-app/spec.md b/docs/specs/desktop-tauri-app/spec.md index 7d86efb543..6113a53c05 100644 --- a/docs/specs/desktop-tauri-app/spec.md +++ b/docs/specs/desktop-tauri-app/spec.md @@ -22,7 +22,7 @@ Users who prefer a desktop workflow should be able to install and open Kandev fr - The desktop app launches the backend with a predictable process environment for GUI launches, including common user binary locations, so configured agent CLIs can be discovered as reliably as they are from terminal-launched Kandev. - Opening a second desktop instance focuses the existing app window or exits without starting a second backend for the same desktop launch context. - GitHub releases include desktop artifacts alongside the existing runtime tarballs and checksums. -- Unsigned desktop artifacts may be produced for development or internal validation, but public recommended desktop releases require macOS Developer ID signing/notarization and Windows code signing before they are presented as trusted user-facing downloads. +- Unsigned desktop artifacts may be produced only through explicit development, validation, or release workflow overrides. Public recommended desktop releases require macOS Developer ID signing/notarization and Windows code signing before they are presented as trusted user-facing downloads. - Desktop release notes and install docs tell users which artifact to download for each operating system and call out any unsigned/pre-release limitations. ## Data model @@ -82,9 +82,9 @@ The desktop shell passes an explicit environment to the native launcher: Each released version includes platform desktop artifacts with checksums. Exact file names may follow Tauri's generated naming, but the release must expose these platform groups: -- macOS arm64: signed/notarized `.dmg` for public release, unsigned `.dmg` allowed only for internal validation. -- macOS x64: signed/notarized `.dmg` for public release, unsigned `.dmg` allowed only for internal validation. -- Windows x64: signed installer (`.exe` or `.msi`) for public release, unsigned installer allowed only for internal validation. +- macOS arm64: signed/notarized `.dmg` for recommended public release, unsigned `.dmg` allowed only through an explicit override. +- macOS x64: signed/notarized `.dmg` for recommended public release, unsigned `.dmg` allowed only through an explicit override. +- Windows x64: signed installer (`.exe` or `.msi`) for recommended public release, unsigned installer allowed only through an explicit override. - Linux x64: AppImage and/or distro package generated by Tauri, with checksum. - Linux arm64: AppImage and/or distro package generated by Tauri, with checksum. @@ -128,7 +128,7 @@ Transitions: - If `/health` does not succeed before the configured timeout, the desktop app terminates the launched child process tree and shows a timeout error. - If the WebView runtime is unavailable or broken on the host OS, the app fails using the platform's normal Tauri/WebView error behavior; the backend process must not remain running. - If an agent CLI cannot be found from a GUI-launched environment, the desktop app/backend surfaces the same setup-health guidance as browser/CLI Kandev rather than failing silently. -- If signing/notarization secrets are absent in CI, release automation may still build explicitly unsigned internal artifacts, but public trusted desktop artifacts are not marked complete. +- If signing/notarization secrets are absent in CI, release automation may still build explicitly unsigned desktop artifacts, but trusted desktop artifacts are not marked complete. ## Persistence guarantees @@ -147,8 +147,8 @@ Transitions: - **GIVEN** the user quits the desktop app, **WHEN** the app exits normally, **THEN** the launcher/backend child process tree is terminated. - **GIVEN** a bundled backend binary is missing, **WHEN** the user opens the desktop app, **THEN** startup fails with a visible error naming the missing binary. - **GIVEN** the release workflow builds desktop artifacts for a version, **WHEN** the GitHub release is published, **THEN** desktop installers and checksums are attached alongside the existing runtime tarballs. -- **GIVEN** a public macOS desktop release, **WHEN** the user downloads and opens the `.dmg`, **THEN** the app is signed and notarized so macOS does not report the download as damaged or from an unidentified developer. -- **GIVEN** a public Windows desktop release, **WHEN** the user runs the installer, **THEN** the installer is code signed to reduce SmartScreen trust warnings. +- **GIVEN** a recommended public macOS desktop release, **WHEN** the user downloads and opens the `.dmg`, **THEN** the app is signed and notarized so macOS does not report the download as damaged or from an unidentified developer. +- **GIVEN** a recommended public Windows desktop release, **WHEN** the user runs the installer, **THEN** the installer is code signed to reduce SmartScreen trust warnings. ## Out of scope