feat: add tauri desktop app (#1478)

This commit is contained in:
Carlos Florêncio
2026-06-24 23:02:50 +01:00
committed by GitHub
parent e52a8c55f2
commit 7e92f8da7a
106 changed files with 13504 additions and 32 deletions
@@ -0,0 +1,30 @@
# 0026: Tauri Desktop Shell Over Native Runtime
**Status:** accepted
**Date:** 2026-06-23
**Area:** frontend, backend, cli, infra
## Context
Kandev now has a native Go launcher/backend path and embeds the Vite SPA into the backend binary. Users can run Kandev from CLI-oriented channels, but a desktop install should open from the OS launcher, manage a local backend process, and avoid a production Node.js runtime. The desktop packaging choice needs to preserve the existing local-first backend, agent executors, and release versioning model.
## Decision
Add a Tauri v2 desktop app as a thin native shell over the existing Kandev runtime. The shell lives under `apps/desktop`, packages the platform runtime binaries as resources, launches the native Kandev launcher in headless mode, waits for the backend `/health` endpoint, and loads the existing Go-served SPA in a native WebView.
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.
## Consequences
The desktop app can reuse the existing backend, embedded web UI, data directory, agent runtime, and supervisor behavior instead of introducing a parallel application stack. Release artifacts grow by the Tauri shell and installer overhead, but avoid Electron's bundled Chromium and avoid a runtime Node.js dependency.
CI becomes more complex because desktop installers must be built on OS-specific runners and signing requires protected secrets. The desktop app also adds a new security boundary: the WebView must not get broad shell/filesystem permissions, and backend process spawning should stay in Rust-side Tauri code.
## Alternatives Considered
1. **Electron desktop app.** Rejected because it would add a bundled Chromium and Node.js runtime, increasing artifact size and conflicting with the no-production-Node direction established by ADR-0021 and ADR-0022.
2. **Rewrite the UI in a native toolkit.** Rejected because it would duplicate the existing React product surface and slow delivery without improving the backend/runtime model.
3. **Browser-only PWA.** Rejected because it does not solve OS-level desktop installation, app launcher integration, or process lifecycle ownership for the local backend.
4. **Tauri shell that embeds all UI assets separately from the Go backend.** Rejected for the first implementation because the backend already embeds and serves the release-quality Vite SPA. Duplicating the assets would increase packaging complexity and risk frontend/backend version drift.
+1
View File
@@ -31,3 +31,4 @@ Read individual ADRs for full context. Create new ones via `/record decision` or
| 0023 | [Active workspace cookie for boot state](0023-active-workspace-cookie.md) | accepted | backend, frontend | 2026-06-18 |
| 0024 | [Go-fronted Vite dev mode](0024-go-fronted-vite-dev-mode.md) | accepted | backend, frontend, cli | 2026-06-18 |
| 0025 | [Runtime cleanup uses `executors_running`](0025-runtime-cleanup-uses-executors-running.md) | accepted | backend | 2026-06-22 |
| 0026 | [Tauri desktop shell over native runtime](0026-tauri-desktop-shell.md) | accepted | frontend, backend, cli, infra | 2026-06-23 |
+67
View File
@@ -0,0 +1,67 @@
# Kandev Desktop App
Kandev desktop is a Tauri app that starts the native Kandev runtime locally and shows the existing Kandev UI in a system WebView. It does not require Node.js at runtime. Node.js, pnpm, Rust, and the Tauri CLI are build-time tools only.
## Install Artifacts
GitHub releases publish desktop artifacts alongside the existing runtime tarballs. Desktop artifact names start with `kandev-desktop-<platform>-` and each artifact has a `.sha256` checksum.
Supported desktop platforms:
- `macos-arm64`
- `macos-x64`
- `linux-x64`
- `linux-arm64`
- `windows-x64`
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.
## Runtime Requirements
The desktop app packages the native Kandev runtime binaries and starts:
```text
kandev --headless --port <local-port>
```
The backend binds to `127.0.0.1` and serves the same embedded Vite UI used by the CLI/Homebrew/npm runtime.
Platform WebView requirements:
- macOS: system WebKit.
- Windows: Microsoft WebView2 runtime.
- Linux: WebKitGTK and package dependencies installed by the `.deb`/`.rpm` package.
Kandev data still lives in the existing Kandev home directory, `~/.kandev` by default, unless overridden by existing environment/config settings.
## Updates
The first desktop release does not include an in-app Tauri updater. Update by downloading and installing a newer desktop artifact from GitHub Releases. Existing worktrees, settings, tasks, and the SQLite database remain in the Kandev data directory.
The npm and Homebrew channels continue to update through:
```bash
brew upgrade kandev
npm install -g kandev@latest
npx kandev@latest
```
Those channels are separate from desktop installers, even though all release artifacts share the same SemVer.
## Agent CLI Discovery
Desktop apps launched from an OS app launcher may not inherit the same shell initialization as a terminal. Kandev preserves available environment variables and adds common user binary directories such as `/usr/local/bin`, `/opt/homebrew/bin`, `/usr/bin`, `/bin`, `~/.local/bin`, and common agent install directories.
If an agent CLI is not discoverable from the desktop app:
- Set the agent profile command to the full executable path.
- Confirm the executable works from a normal terminal.
- Put custom install directories in a location visible to GUI apps, or configure them through OS-level environment settings rather than only shell startup files.
Existing explicit command paths in Kandev settings remain authoritative over `PATH` lookup.
## Release Trust
See [desktop-tauri-signing.md](desktop-tauri-signing.md) for the CI secrets and signing expectations used for trusted desktop releases.
+36
View File
@@ -0,0 +1,36 @@
# Tauri Desktop Signing
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.
## macOS
Required signing secrets:
- `APPLE_CERTIFICATE`: base64 `.p12` Developer ID Application certificate.
- `APPLE_CERTIFICATE_PASSWORD`: export password for the `.p12`.
- `KEYCHAIN_PASSWORD`: temporary CI keychain password.
Required notarization secrets, choose one path:
- Apple ID path: `APPLE_ID`, `APPLE_PASSWORD`, `APPLE_TEAM_ID`.
- App Store Connect API path: `APPLE_API_KEY`, `APPLE_API_ISSUER`, `APPLE_API_KEY_P8`.
Optional:
- `APPLE_PROVIDER_SHORT_NAME` when the Apple ID belongs to multiple provider teams.
## Windows
Required signing secrets:
- `WINDOWS_CERTIFICATE`: base64 `.pfx` code signing certificate.
- `WINDOWS_CERTIFICATE_PASSWORD`: export password for the `.pfx`.
Optional:
- `WINDOWS_TIMESTAMP_URL`: timestamp server, defaults to `http://timestamp.digicert.com`.
- `WINDOWS_SIGNTOOL_PATH`: custom `signtool.exe` path.
Linux desktop artifacts are checksum-gated. The x64 `.deb`/`.rpm` artifacts are built on Ubuntu 22.04 for an older glibc baseline. The arm64 artifacts use GitHub's Ubuntu 24.04 arm64 runner baseline. GPG/RPM signing can be added later without changing the macOS and Windows trust gate.
+1
View File
@@ -25,6 +25,7 @@ This page expands the short feature list in the README without turning the READM
## Executors And Runtime
- **Desktop app:** install Kandev as a Tauri desktop app that starts the local backend and shows the existing Kandev UI without requiring Node.js at runtime. See [desktop-app.md](desktop-app.md).
- **Executor types:** run agents as local host processes, git worktrees, Docker containers, remote SSH sessions, or Sprites cloud environments.
- **Executor profiles:** save per-runtime configuration, including prepare scripts, environment variables, credentials, and profile-specific settings.
- **Worktree isolation:** concurrent agents work in isolated git worktrees so their changes do not collide.
+259
View File
@@ -0,0 +1,259 @@
---
spec: docs/specs/desktop-tauri-app/spec.md
created: 2026-06-23
status: done
---
# Implementation Plan: Tauri Desktop App
## Overview
Add `apps/desktop` as a Tauri v2 shell that packages the existing native Kandev runtime, starts it in headless mode, waits for backend health, and loads the Go-served SPA in a desktop WebView. The implementation starts by tightening the backend/launcher contract for loopback desktop launches, then scaffolds the app, wires release packaging, adds signing gates, and finishes with desktop smoke coverage and docs.
---
## Architecture Records
- Spec: `docs/specs/desktop-tauri-app/spec.md`
- Decision: `docs/decisions/0026-tauri-desktop-shell.md`
- Existing dependencies: ADR-0021 Go-served SPA with boot state, ADR-0022 embedded Vite assets, and the native launcher spec at `docs/specs/native-kandev-cli/spec.md`.
---
## Backend
### Loopback bind contract
Files:
- `apps/backend/internal/common/config/config.go`
- `apps/backend/internal/backendapp/main.go`
- `apps/backend/internal/backendapp/helpers_test.go` or a focused backend server test
- `apps/backend/internal/launcher/env.go`
- `apps/backend/internal/launcher/start_test.go` or `apps/backend/internal/launcher/run_test.go`
Use the existing `ServerConfig.Host` field when constructing the HTTP server address and listener address. Preserve the current default unless a caller sets `server.host` / `KANDEV_SERVER_HOST`. The desktop app will set `KANDEV_SERVER_HOST=127.0.0.1`.
Add tests proving:
- `buildHTTPServer` uses the configured host in `http.Server.Addr`.
- `startHTTPServer` listens on the configured host/port.
- launcher env construction can pass a backend host override for desktop/headless launches without changing normal CLI defaults.
### Headless launcher lifecycle contract
Files:
- `apps/backend/internal/launcher/start.go`
- `apps/backend/internal/launcher/health.go`
- `apps/backend/internal/launcher/process.go`
- Existing launcher tests under `apps/backend/internal/launcher/`
Keep `kandev --headless --port <port>` as the desktop process contract. It must supervise `kandev __backend`, wait for `/health`, skip opening a browser, and remain alive until the backend exits or the parent process terminates. Desktop-specific process termination should use the same process-group/job-object cleanup behavior already covered by the native launcher.
---
## Desktop App
### Workspace package
Files:
- `apps/pnpm-workspace.yaml`
- `apps/package.json`
- New `apps/desktop/package.json`
- New `apps/desktop/index.html`
- New `apps/desktop/src/main.ts`
- New `apps/desktop/src/styles.css`
- New `apps/desktop/src-tauri/Cargo.toml`
- New `apps/desktop/src-tauri/build.rs`
- New `apps/desktop/src-tauri/tauri.conf.json`
- New `apps/desktop/src-tauri/src/main.rs`
- New `apps/desktop/src-tauri/src/backend.rs`
- New `apps/desktop/src-tauri/capabilities/default.json`
Add a minimal Tauri/Vite desktop package named `@kandev/desktop`. The app's first screen is a compact startup surface, not a marketing page. Rust-side Tauri code locates the packaged runtime resources, picks an available loopback port, spawns the native launcher in headless mode, polls `/health`, and navigates the main window to the backend URL.
Do not expose shell execution to frontend JavaScript. Backend process control should live in Rust-side Tauri code.
### Runtime resources
Files:
- New `scripts/release/prepare-desktop-runtime.sh`
- New `scripts/release/verify-desktop-runtime.sh`
- `apps/desktop/src-tauri/tauri.conf.json`
- `apps/desktop/src-tauri/src/backend.rs`
Create a deterministic resource layout for the platform runtime extracted from the existing release bundle:
```text
apps/desktop/src-tauri/resources/kandev/
└── bin/
├── kandev[.exe]
├── agentctl[.exe]
└── agentctl-linux-amd64
```
The helper validates the exact binaries required by `internal/launcher/bundle.go` so the desktop app and existing runtime tarballs cannot drift.
### Desktop process environment
Files:
- `apps/desktop/src-tauri/src/backend.rs`
- `apps/backend/internal/launcher/env.go`
- `apps/backend/internal/launcher/service.go` for existing PATH precedent
- `docs/desktop-tauri-signing.md` or desktop install docs for troubleshooting notes
Build the backend process environment explicitly. Preserve inherited variables, force `KANDEV_SERVER_HOST=127.0.0.1`, and normalize `PATH` for GUI app launches so agent CLIs installed under common user locations are visible. Existing explicit agent command paths in settings remain authoritative; this task only makes default command lookup reliable.
### Desktop UX
Files:
- `apps/desktop/src/main.ts`
- `apps/desktop/src/styles.css`
- `apps/desktop/src-tauri/src/main.rs`
- `apps/desktop/src-tauri/src/backend.rs`
The startup surface shows only launch status and errors. Once the backend is ready, the WebView loads the existing Kandev UI. The app should support standard OS close/quit behavior and single-instance focus.
---
## Release Pipeline
### Build matrix
Files:
- `.github/workflows/release.yml`
- `scripts/release/verify-desktop-assets.sh`
- `apps/cli/src/release-config.test.ts`
Add a `build-desktop` job that depends on `build-bundles`, downloads the matching `bundle-<platform>` artifact, prepares desktop runtime resources, runs the Tauri build on the appropriate OS runner, checks artifact sizes/checksums, and uploads `desktop-<platform>` artifacts.
The initial matrix should mirror current runtime support:
- `macos-arm64`
- `macos-x64`
- `linux-x64`
- `linux-arm64`
- `windows-x64`
### GitHub release publishing
Files:
- `.github/workflows/release.yml`
- `scripts/release/verify-desktop-assets.sh`
- `scripts/release/update-homebrew-tap.sh` only if release asset assumptions need to ignore desktop artifacts
Update `publish-release` to download and publish desktop artifacts alongside the existing `kandev-*.tar.gz` runtime bundles. Keep existing npm and Homebrew publishing tied to runtime tarballs only.
### Signing
Files:
- `.github/workflows/release.yml`
- `apps/desktop/src-tauri/tauri.conf.json`
- New `docs/desktop-tauri-signing.md`
- GitHub Actions release docs/comments in `.github/workflows/release.yml`
Add signing inputs/secrets behind conditional release steps:
- macOS: Developer ID Application certificate, Apple team/provider metadata, notarization credentials.
- 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.
---
## Frontend
The existing `apps/web` product UI should not need route or state changes. Desktop-specific frontend work is limited to the Tauri startup surface under `apps/desktop`. If implementation discovers that the existing SPA needs to distinguish desktop WebView mode, add the smallest runtime flag necessary and document it in the spec before broadening scope.
---
## Tests
- **What:** backend honors configured host when constructing server addresses.
**File:** `apps/backend/internal/backendapp/helpers_test.go` or a new focused test file.
**How:** table-driven Go test over host/port config.
- **What:** launcher/headless env includes desktop host override only when requested.
**File:** `apps/backend/internal/launcher/start_test.go` or `run_test.go`.
**How:** Go unit test around env/config helpers.
- **What:** missing runtime resource binaries are detected before desktop launch.
**File:** `apps/desktop/src-tauri/src/backend.rs` tests.
**How:** Rust unit tests with temporary resource directories.
- **What:** backend command builder passes `--headless`, `--port`, and loopback env.
**File:** `apps/desktop/src-tauri/src/backend.rs` tests.
**How:** Rust unit tests over pure command-building helpers.
- **What:** desktop process environment preserves user env and adds common GUI-missing binary paths.
**File:** `apps/desktop/src-tauri/src/backend.rs` tests.
**How:** Rust unit tests around environment construction.
- **What:** desktop startup handles health success, timeout, and child-process exit.
**File:** `apps/desktop/src-tauri/src/backend.rs` tests.
**How:** Rust tests using a local fake HTTP server or injectable health client.
- **What:** release workflow requires desktop artifacts and leaves npm/Homebrew runtime publishing unchanged.
**File:** `apps/cli/src/release-config.test.ts` or a shell test under `scripts/`.
**How:** parse workflow/scripts and assert expected job names, artifacts, and publish filters.
- **What:** desktop runtime resource preparation validates the existing bundle shape.
**File:** script test under `scripts/` or `apps/cli/src/release-config.test.ts`.
**How:** create fake bundle directories and assert success/failure paths.
---
## E2E Tests
- **Scenario:** GIVEN a prepared Linux desktop runtime bundle, WHEN the desktop app is launched in CI, THEN the Tauri window loads the Kandev UI without Node.js at runtime.
**File:** `apps/desktop/e2e/desktop-launch.spec.ts` or an equivalent Tauri WebDriver test.
**What to verify:** startup screen transitions to the Kandev app shell and `/health` succeeds.
- **Scenario:** GIVEN the backend child fails during startup, WHEN the desktop app is launched, THEN the startup surface shows a failure and no child process remains running.
**File:** `apps/desktop/e2e/desktop-launch.spec.ts` or Rust integration test if WebDriver cannot observe process state reliably.
**What to verify:** visible error state plus process cleanup.
- **Scenario:** GIVEN the desktop app is already running, WHEN a second instance is opened, THEN the first window is focused and only one backend launcher process exists.
**File:** `apps/desktop/e2e/desktop-single-instance.spec.ts` or Rust integration test.
**What to verify:** single-instance behavior and no duplicate backend process.
Tauri desktop E2E should run on Linux with a fake display in CI first. macOS and Windows desktop smoke tests can be added after the Linux harness is stable.
---
## Implementation Waves
Wave 1:
- [x] [task-01-backend-desktop-launch-contract](task-01-backend-desktop-launch-contract.md)
Wave 2:
- [x] [task-02-tauri-desktop-scaffold](task-02-tauri-desktop-scaffold.md)
- [x] [task-03-desktop-runtime-resources](task-03-desktop-runtime-resources.md)
Wave 3:
- [x] [task-04-release-desktop-artifacts](task-04-release-desktop-artifacts.md)
- [x] [task-05-signing-and-release-trust](task-05-signing-and-release-trust.md)
Wave 4:
- [x] [task-06-desktop-e2e-smoke](task-06-desktop-e2e-smoke.md)
- [x] [task-07-docs-and-release-notes](task-07-docs-and-release-notes.md)
---
## Open Questions
- Which Windows installer target should be the public default: NSIS `.exe` or MSI? Recommendation: start with the Tauri default/recommended installer for direct GitHub release downloads, then add MSI only if enterprise deployment requires it.
- Which Linux artifact set should be public by default: AppImage only, `.deb` plus AppImage, or `.deb`/`.rpm` plus AppImage? Recommendation: start with AppImage plus the package type Tauri produces reliably in CI, then broaden after QA.
@@ -0,0 +1,47 @@
---
id: "01-backend-desktop-launch-contract"
title: "Backend desktop launch contract"
status: done
wave: 1
depends_on: []
plan: "plan.md"
spec: "../../specs/desktop-tauri-app/spec.md"
---
# Task 01: Backend Desktop Launch Contract
## Acceptance
- The backend honors `server.host` / `KANDEV_SERVER_HOST` when binding the HTTP listener.
- Desktop/headless launch can force `127.0.0.1` without changing normal CLI/service defaults.
- `kandev --headless --port <port>` remains a stable process contract for a parent desktop app to supervise.
## Verification
```bash
rtk bash -lc 'cd apps/backend && go test -tags fts5 ./internal/backendapp ./internal/launcher ./internal/common/config'
```
## Files Likely Touched
- `apps/backend/internal/common/config/config.go`
- `apps/backend/internal/backendapp/main.go`
- `apps/backend/internal/backendapp/helpers_test.go`
- `apps/backend/internal/launcher/env.go`
- `apps/backend/internal/launcher/start.go`
- `apps/backend/internal/launcher/start_test.go`
- `apps/backend/internal/launcher/run_test.go`
## Dependencies
None.
## Inputs
- Spec sections: API surface > Desktop launch contract; API surface > Backend bind contract; Failure modes.
- Existing patterns: native launcher in `apps/backend/internal/launcher/`, backend HTTP server setup in `apps/backend/internal/backendapp/main.go`.
- ADR: `docs/decisions/0026-tauri-desktop-shell.md`.
## Output Contract
When complete, update this file's `status` to `done`, update the Wave 1 checkbox in `plan.md`, and report changed files, tests run, blockers, and residual risks.
@@ -0,0 +1,55 @@
---
id: "02-tauri-desktop-scaffold"
title: "Tauri desktop scaffold"
status: done
wave: 2
depends_on: ["01-backend-desktop-launch-contract"]
plan: "plan.md"
spec: "../../specs/desktop-tauri-app/spec.md"
---
# Task 02: Tauri Desktop Scaffold
## Acceptance
- `apps/desktop` builds as a Tauri v2 desktop package in the pnpm workspace.
- The Tauri Rust side starts the native Kandev launcher in headless mode, polls `/health`, and navigates the main window to the backend URL.
- The backend process environment preserves inherited variables, forces loopback backend binding, and adds common GUI-missing user binary paths.
- Frontend JavaScript is not granted broad shell/filesystem permissions.
## Verification
```bash
rtk bash -lc 'cd apps && pnpm --filter @kandev/desktop build'
rtk bash -lc 'cd apps/desktop/src-tauri && cargo test'
```
## Files Likely Touched
- `apps/pnpm-workspace.yaml`
- `apps/package.json`
- `apps/desktop/package.json`
- `apps/desktop/index.html`
- `apps/desktop/src/main.ts`
- `apps/desktop/src/styles.css`
- `apps/desktop/src-tauri/Cargo.toml`
- `apps/desktop/src-tauri/build.rs`
- `apps/desktop/src-tauri/tauri.conf.json`
- `apps/desktop/src-tauri/src/main.rs`
- `apps/desktop/src-tauri/src/backend.rs`
- `apps/desktop/src-tauri/capabilities/default.json`
- `apps/backend/internal/launcher/service.go` only as a reference for existing service PATH conventions
## Dependencies
- Task 01.
## Inputs
- Spec sections: What, State machine, Permissions, Failure modes.
- Plan sections: Desktop App > Workspace package; Desktop App > Desktop UX.
- Existing runtime contract from Task 01.
## Output Contract
When complete, update this file's `status` to `done`, update the Wave 2 checkbox in `plan.md`, and report changed files, tests run, blockers, and residual risks.
@@ -0,0 +1,48 @@
---
id: "03-desktop-runtime-resources"
title: "Desktop runtime resources"
status: done
wave: 2
depends_on: ["01-backend-desktop-launch-contract"]
plan: "plan.md"
spec: "../../specs/desktop-tauri-app/spec.md"
---
# Task 03: Desktop Runtime Resources
## Acceptance
- A release helper extracts the existing platform runtime bundle into the deterministic Tauri resource layout.
- The helper validates `kandev`, `agentctl`, and `agentctl-linux-amd64` requirements consistently with the native launcher.
- The Tauri app resolves packaged resources without relying on `KANDEV_BUNDLE_DIR` or Node.js at runtime.
## Verification
```bash
rtk bash -lc 'scripts/release/prepare-desktop-runtime.sh --help'
rtk bash -lc 'cd apps/desktop/src-tauri && cargo test'
rtk bash -lc 'cd apps && pnpm --filter @kandev/desktop build'
```
## Files Likely Touched
- `scripts/release/prepare-desktop-runtime.sh`
- `scripts/release/verify-desktop-runtime.sh`
- `apps/desktop/src-tauri/tauri.conf.json`
- `apps/desktop/src-tauri/src/backend.rs`
- `apps/desktop/src-tauri/resources/.gitignore`
- `apps/cli/src/release-config.test.ts` or a new script test if script behavior is tested from Node/Vitest
## Dependencies
- Task 01.
## Inputs
- Spec sections: API surface > Desktop artifact contract; Failure modes.
- Plan sections: Desktop App > Runtime resources.
- Existing bundle shape: `apps/cli/README_internal.md` and `scripts/release/package-bundle.sh`.
## Output Contract
When complete, update this file's `status` to `done`, update the Wave 2 checkbox in `plan.md`, and report changed files, tests run, blockers, and residual risks.
@@ -0,0 +1,47 @@
---
id: "04-release-desktop-artifacts"
title: "Release desktop artifacts"
status: done
wave: 3
depends_on: ["02-tauri-desktop-scaffold", "03-desktop-runtime-resources"]
plan: "plan.md"
spec: "../../specs/desktop-tauri-app/spec.md"
---
# Task 04: Release Desktop Artifacts
## Acceptance
- The release workflow builds desktop artifacts for macOS arm64, macOS x64, Linux x64, Linux arm64, and Windows x64.
- `publish-release` attaches desktop artifacts and checksums without changing npm/Homebrew runtime publishing behavior.
- Release verification fails when a required desktop artifact is missing.
## Verification
```bash
rtk bash -lc 'cd apps && pnpm --filter kandev test -- release-config'
rtk git diff --check
```
## Files Likely Touched
- `.github/workflows/release.yml`
- `scripts/release/verify-desktop-assets.sh`
- `scripts/release/prepare-desktop-runtime.sh`
- `apps/cli/src/release-config.test.ts`
- `apps/desktop/package.json`
## Dependencies
- Task 02.
- Task 03.
## Inputs
- Spec sections: API surface > Desktop artifact contract; Scenarios.
- Plan sections: Release Pipeline > Build matrix; Release Pipeline > GitHub release publishing.
- Existing workflow: `.github/workflows/release.yml`.
## Output Contract
When complete, update this file's `status` to `done`, update the Wave 3 checkbox in `plan.md`, and report changed files, tests run, blockers, and residual risks.
@@ -0,0 +1,51 @@
---
id: "05-signing-and-release-trust"
title: "Signing and release trust"
status: done
wave: 3
depends_on: ["04-release-desktop-artifacts"]
plan: "plan.md"
spec: "../../specs/desktop-tauri-app/spec.md"
---
# Task 05: Signing and Release Trust
## Acceptance
- 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.
## Verification
```bash
rtk bash -lc 'cd apps && pnpm --filter kandev test -- release-config'
rtk git diff --check
```
Manual verification for maintainers:
```bash
rtk gh workflow run release.yml -f dry_run=true -f bump=patch
```
## Files Likely Touched
- `.github/workflows/release.yml`
- `apps/desktop/src-tauri/tauri.conf.json`
- `docs/desktop-tauri-signing.md`
- `apps/cli/src/release-config.test.ts`
## Dependencies
- Task 04.
## Inputs
- Spec sections: What; Permissions; Failure modes; Scenarios.
- Plan sections: Release Pipeline > Signing.
- Tauri signing docs for macOS, Windows, Linux, and updater signing constraints.
## Output Contract
When complete, update this file's `status` to `done`, update the Wave 3 checkbox in `plan.md`, and report changed files, tests run, blockers, and residual risks.
@@ -0,0 +1,48 @@
---
id: "06-desktop-e2e-smoke"
title: "Desktop E2E smoke"
status: done
wave: 4
depends_on: ["02-tauri-desktop-scaffold", "03-desktop-runtime-resources"]
plan: "plan.md"
spec: "../../specs/desktop-tauri-app/spec.md"
---
# Task 06: Desktop E2E Smoke
## Acceptance
- CI has at least one Linux desktop smoke test that launches the Tauri app with a prepared runtime bundle.
- The smoke test verifies the startup screen transitions to the Kandev UI after backend health succeeds.
- A failure-path test or Rust integration test verifies child process cleanup on startup failure.
## Verification
```bash
rtk bash -lc 'cd apps && pnpm --filter @kandev/desktop e2e'
rtk bash -lc 'cd apps/desktop/src-tauri && cargo test'
```
## Files Likely Touched
- `apps/desktop/e2e/desktop-launch.spec.ts`
- `apps/desktop/e2e/desktop-single-instance.spec.ts`
- `apps/desktop/e2e/README.md`
- `apps/desktop/package.json`
- `apps/desktop/src-tauri/src/backend.rs`
- `.github/workflows/release.yml` or a dedicated desktop test workflow
## Dependencies
- Task 02.
- Task 03.
## Inputs
- Spec sections: State machine; Failure modes; Scenarios.
- Plan sections: E2E Tests.
- Existing web E2E patterns in `apps/web/e2e/README.md`; Tauri WebDriver guidance for desktop smoke coverage.
## Output Contract
When complete, update this file's `status` to `done`, update the Wave 4 checkbox in `plan.md`, and report changed files, tests run, blockers, and residual risks.
@@ -0,0 +1,53 @@
---
id: "07-docs-and-release-notes"
title: "Docs and release notes"
status: done
wave: 4
depends_on: ["04-release-desktop-artifacts", "05-signing-and-release-trust", "06-desktop-e2e-smoke"]
plan: "plan.md"
spec: "../../specs/desktop-tauri-app/spec.md"
---
# Task 07: Docs and Release Notes
## Acceptance
- User docs explain desktop install artifacts, runtime requirements, update expectations, and signing/trust status.
- User docs explain GUI-launch environment differences and what to do when an agent CLI is not discoverable from the desktop app.
- Engineering docs mention the `apps/desktop` package and release workflow ownership.
- The spec status and plan task statuses are reconciled with the implementation state.
## Verification
```bash
rtk git diff --check
rtk bash -lc 'cd apps && pnpm format:check'
```
## Files Likely Touched
- `docs/specs/desktop-tauri-app/spec.md`
- `docs/plans/desktop-tauri-app/plan.md`
- `docs/desktop-tauri-signing.md`
- `docs/cli.md`
- `docs/features.md`
- `docs/remote-cloud-environment.md` if desktop build dependencies affect cloud dev setup
- `AGENTS.md`
- `apps/desktop/AGENTS.md`
- `apps/cli/README_internal.md`
## Dependencies
- Task 04.
- Task 05.
- Task 06.
## Inputs
- Spec sections: What; Out of scope.
- Plan sections: Release Pipeline; E2E Tests.
- Root `AGENTS.md` maintenance rule for changed package layout/release conventions.
## Output Contract
When complete, update this file's `status` to `done`, update the Wave 4 checkbox in `plan.md`, and report changed files, tests run, blockers, and residual risks.
+1
View File
@@ -111,6 +111,7 @@ System pages (Radarr/Sonarr-style) for status, disk usage, database maintenance,
| [improve-kandev](improve-kandev/spec.md) | draft |
| [homebrew-core](homebrew-core/spec.md) | draft |
| [native-kandev-cli](native-kandev-cli/spec.md) | draft |
| [desktop-tauri-app](desktop-tauri-app/spec.md) | shipped |
| [public-share-links](public-share-links/spec.md) | draft |
| [ssh-executor](ssh-executor/spec.md) | draft |
| [cli-mode-parity](cli-mode-parity/spec.md) | draft |
+166
View File
@@ -0,0 +1,166 @@
---
status: implemented
created: 2026-06-23
owner: tbd
---
# Tauri Desktop App
## Why
Users who prefer a desktop workflow should be able to install and open Kandev from the OS app launcher without running a terminal command or managing a browser tab. The installed desktop app should keep Kandev's current local-first behavior while avoiding a production Node.js runtime.
## What
- Kandev provides desktop installers for the same primary release platforms as the native runtime bundle: macOS arm64, macOS x64, Linux x64, Linux arm64, and Windows x64.
- Opening the desktop app starts a local Kandev backend and displays the existing Vite/React Kandev UI inside a native Tauri WebView window.
- The installed desktop app does not require Node.js. Node.js and pnpm remain build-time tooling in CI and local development only.
- The desktop app reuses the existing Go-served SPA with embedded Vite assets. It does not ship or start a separate web server.
- The desktop app reuses the existing Kandev data directory, SQLite database, worktrees, executor settings, integrations, and agent configuration.
- The desktop app owns the backend lifecycle for the process it starts: startup waits for `/health`, normal quit terminates child processes, and startup failure surfaces an error instead of leaving orphaned backend processes.
- The desktop app starts the backend on a loopback-only address and an available local port unless the user has explicitly configured otherwise through existing environment/config mechanisms.
- 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.
- 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
The feature does not add backend database tables.
Desktop runtime state is process-local except for optional window placement/state stored by Tauri under the OS application data location. Kandev user data remains owned by the existing backend persistence model under `KANDEV_HOME_DIR` or the default `~/.kandev` location.
## API surface
### Desktop launch contract
The Tauri shell starts the packaged native Kandev launcher in headless mode with an explicit backend port:
```text
kandev --headless --port <available-loopback-port>
```
The launcher starts the backend through the existing hidden backend mode and keeps the supervisor process alive until the desktop app exits. The desktop shell polls:
```text
GET http://127.0.0.1:<port>/health
```
Desktop launches pass a per-launch `KANDEV_DESKTOP_HEALTH_TOKEN` to the native launcher
environment. The backend echoes the token in the `X-Kandev-Desktop-Health-Token` response
header on successful `/health` responses. The desktop shell treats `/health` as ready only
when the status is successful and the response header matches its generated token, so a
separate local process on the same loopback port cannot satisfy desktop readiness.
When `/health` reports ready for that launch, the desktop window navigates to:
```text
http://127.0.0.1:<port>/
```
### Backend bind contract
Desktop launches set the backend host to loopback:
```text
KANDEV_SERVER_HOST=127.0.0.1
```
The backend server honors `server.host` / `KANDEV_SERVER_HOST` when binding its listener. Existing CLI and service launches may keep their current default unless explicitly changed by their own specs.
### Desktop environment contract
The desktop shell passes an explicit environment to the native launcher:
- Existing user environment variables are preserved when available.
- `KANDEV_SERVER_HOST=127.0.0.1` is set for desktop launches.
- `PATH` includes common GUI-missing user binary locations such as `/usr/local/bin`, `/opt/homebrew/bin`, `/usr/bin`, `/bin`, `%USERPROFILE%/.local/bin`, and existing Kandev agent CLI install locations where applicable.
- Explicit agent command paths configured in Kandev settings remain authoritative over `PATH` lookup.
### Desktop artifact contract
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.
- Linux x64: AppImage and/or distro package generated by Tauri, with checksum.
- Linux arm64: AppImage and/or distro package generated by Tauri, with checksum.
### Existing backend API
The desktop app uses the existing backend HTTP, WebSocket, and boot-payload contracts. No new product API is required for the main Kandev UI.
## State machine
Desktop app lifecycle:
- `idle`: the app process has started but no backend child is running.
- `backend-starting`: the app has spawned the native launcher and is polling backend health.
- `ready`: the backend is healthy and the WebView is displaying the Kandev UI.
- `backend-restarting`: the launcher supervisor is replacing the backend child after an existing restart request.
- `stopping`: the user quit the app or the app is terminating its child processes.
- `failed`: startup failed before the UI became ready.
Transitions:
- `idle` -> `backend-starting`: user opens the desktop app.
- `backend-starting` -> `ready`: `/health` returns success and the WebView navigates to the backend URL.
- `backend-starting` -> `failed`: the launcher exits, a required bundled binary is missing, the backend cannot bind, or health times out.
- `ready` -> `backend-restarting`: the existing backend restart supervisor handles a restart request.
- `backend-restarting` -> `ready`: the replacement backend becomes healthy on the same launch URL.
- any active state -> `stopping`: the user quits the app or the OS terminates the app.
- `stopping` -> `idle`: child processes are terminated and the app exits.
## Permissions
- The desktop app runs with the current OS user's permissions and does not require administrator/root privileges for normal install or launch.
- Backend filesystem, Docker, SSH, Git, and credential access remains governed by existing Kandev backend and executor behavior.
- The WebView does not receive broad shell/filesystem permissions. Process spawning for the backend is owned by the Tauri Rust side, not by arbitrary frontend code.
- Signing credentials are CI secrets. They are never committed to the repository or exposed to the desktop app runtime.
## Failure modes
- Missing packaged `kandev`, `agentctl`, or required helper binaries cause startup to fail with a visible desktop error that names the missing artifact.
- If the selected backend port is unavailable, the desktop app chooses a different available port before launch. If no port can be selected, startup fails visibly.
- If the backend exits before `/health` succeeds, the desktop app shows a startup failure and includes the captured launcher/backend output when available.
- 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.
## Persistence guarantees
- Kandev data created through the desktop app survives app quit and process restart using the same persistence guarantees as CLI-launched Kandev.
- In-flight backend process state does not survive app quit. On next launch, the desktop app starts a fresh backend process, and backend recovery follows existing Kandev recovery behavior.
- Optional window size/position persistence survives app restart if the Tauri window-state plugin is enabled.
- Desktop installer artifacts do not alter existing npm/Homebrew/manual runtime update semantics.
## Scenarios
- **GIVEN** Kandev is installed as a desktop app on a machine without Node.js, **WHEN** the user opens the app, **THEN** the backend starts and the Kandev UI appears in the desktop window.
- **GIVEN** the desktop app is already running, **WHEN** the user opens the app again, **THEN** the existing window is focused and no second backend is started for that desktop app instance.
- **GIVEN** the backend has not finished startup, **WHEN** the desktop app is open, **THEN** the user sees a startup/loading state until `/health` succeeds or startup fails.
- **GIVEN** the backend becomes healthy, **WHEN** the desktop app navigates to the backend URL, **THEN** the existing Kandev boot payload hydrates the UI without a separate Node.js web runtime.
- **GIVEN** an agent CLI is installed in a common user binary location but the desktop app was opened from the OS launcher, **WHEN** the user starts a session using that agent, **THEN** Kandev can discover the agent command or surfaces existing setup-health guidance.
- **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.
## Out of scope
- Replacing the existing npm, Homebrew, Docker, or runtime tarball channels.
- Requiring `npx` or npm installs to be Node-free.
- Mobile Tauri targets.
- App Store, Microsoft Store, Snapcraft, AUR, or Homebrew Cask distribution.
- In-app Tauri updater support in the first desktop release.
- Running Kandev as a background tray app after all windows close.
- Rewriting the Kandev frontend or backend in Rust.
## Implementation plan
- [Desktop Tauri App plan](../../plans/desktop-tauri-app/plan.md)
- [ADR-0026: Tauri desktop shell over native runtime](../../decisions/0026-tauri-desktop-shell.md)