Desktop app (Tauri)

A Tauri 2 desktop app that is your web app in a native window — the same typed hooks, the platform adapter from @voltro/desktop/tauri (keychain token store, updater against your release feed, deep links, tray/menu commands). Tauri owns cargo and the window; voltro dev runs the sibling web app + api.

A Tauri 2 desktop app that puts your existing web app in a native window — nothing in the web app changes; what differs is the platform adapter installed before it mounts. Template id: desktop-app (kind: desktop).

Tauri owns the dev loop, not voltro dev. The window is started with pnpm tauri dev and loads the web dev server; production bundles the web app's dist. That is why a desktop app gets no port and is not part of voltro dev's orchestration — and why it needs the Rust toolchain on the machine that builds it.

Boots on macOS, Windows and Linux. The framework's desktop proof workflow scaffolds this template, compiles the Tauri shell on all three runners and starts it: the window's boot report (awaitWindowBoot → window_booted) says the Tauri adapter was installed before the web app mounted, on every OS (green on 2026-09-26). Signing, notarization and your own updater key remain your steps.

Scaffold

# A project with a backend, a web app and a desktop app:
voltro create-project acme --api=api-backend --web=frontend-app --desktop

# …or add one to an existing project later:
voltro add-app desktop --template=desktop-app --to acme

What ships

desktop-app/
  voltro.desktop.ts          # web app, shell, features, updates channel, identifier
  src/
    main.ts                  # the window's platform script: installs the adapter, reports the boot
    platform.ts              # createTauriPlatform from the Tauri plugins in package.json
    lib/commands.ts          # PURE: the JS side of the Rust command pairs (keychain, tray, menu) — tested
  src-tauri/
    Cargo.toml, tauri.conf.json, build.rs
    src/lib.rs               # plugin registration + keyring / set_tray / set_menu commands
    capabilities/default.json   # the core; voltro.generated.json is derived from voltro.desktop.ts
    icons/icon.png           # `pnpm tauri icon` derives the platform sets
  tests/commands.test.ts

The commands

Command Does
voltro desktop capabilities src-tauri/capabilities/voltro.generated.json from features — an app that declares no shell gets no shell permission
voltro desktop platform src/main.ts → src-tauri/gen/platform.js, the window's platform script the shell injects before the web app's own scripts (also the beforeDevCommand)
voltro desktop build the sibling web build → the capabilities → the platform script → tauri build → dist-desktop/manifest.json (every bundle with kind, target, sha256)
voltro desktop ci .github/workflows/desktop-release.yml: macOS (arm64 + x64), Windows, Linux; signing and notarization from repository secrets; artefacts published with voltro release
voltro desktop publish upload a manifest's artefacts into a release on the api's release feed

tauri.conf.json's plugins.updater.endpoints points at that feed (/releases/<app>/tauri/<channel>/{{target}}-{{arch}}/{{current_version}}), so the rollout, rollback and minimum-version policy are the same ones your other apps follow. voltro desktop build refuses a placeholder updater public key and an empty endpoint list rather than shipping an updater that cannot verify anything.

Signing

Nothing secret lives in the template. pnpm tauri signer generate mints the updater key pair (public key → tauri.conf.json, private key → a CI secret); Developer ID + notarization and Windows Authenticode are the secrets the generated workflow names in its header.