Develop
Release and packaging
How a Cortex release is versioned, built, checked and what it does not cover yet. Installing and uninstalling a release is in Installation; the list of releases is in Releases.
Versioning#
- There is one version for the whole product:
versionin[workspace.package]of the rootCargo.toml(currently0.2.0). Every crate inherits it, includingcortex-desktop. apps/desktop/src-tauri/tauri.conf.jsonhas noversionfield, so the installer, the executable's file properties and the uninstall entry all take the Cargo version.- The npm
versionfields (apps/desktop/ui/package.json,apps/landing/package.json) are0.0.0and are not part of the product version. - To cut a new release, bump
[workspace.package] version, rebuild, and add a section to Releases with the new artifact's facts. A tagv<version>(the same version) makes the CI workflow build the installer on a Windows runner (Continuous build); nothing is published automatically.
Building the installer#
The installer is built with the Tauri CLI (@tauri-apps/cli, a devDependency of apps/desktop/ui) and the NSIS bundler:
cd apps\desktop\ui
npm ci
npm run installernpm run installer runs tauri build from apps/desktop. It:
- runs
npm run buildinapps/desktop/ui(beforeBuildCommandintauri.conf.json), so the UI is always rebuilt first; - builds
cortex-desktopwith thereleaseprofile (target\release\cortex-desktop.exe); - packages it with NSIS into
target\release\bundle\nsis\Cortex_<version>_x64-setup.exe.
Start.bat release and cargo build --release produce only the executable, never the installer.
Prerequisites on the build machine, on top of those for building from source in Installation:
- The Tauri CLI downloads NSIS and its
nsis_tauri_utilsplugin into%LOCALAPPDATA%\tauri\NSISon first use (from GitHub releases of the Tauri project); later builds reuse that cache. - The npm package
@tauri-apps/apimust stay on the same major.minor as the Rust cratetauri(both 2.11 today).tauri buildrefuses to run otherwise, which is whypackage.jsonpins@tauri-apps/apiwith~2.11.1. Upgrade both together.
The packaging settings live in apps/desktop/src-tauri/tauri.conf.json (bundle): NSIS target only, per-user install mode (bundle.windows.nsis.installMode: "currentUser"), publisher Cortex, the icon icons/icon.ico. The generated NSIS script is kept at target\release\nsis\x64\installer.nsi and is the most direct way to see what an installer will do.
Continuous build#
.github/workflows/windows-installer.yml builds the installer on a GitHub-hosted Windows runner when a tag v<major>.<minor>.<patch> is pushed, or when it is started by hand (Actions → Windows installer → Run workflow). It:
- checks that the tag equals
[workspace.package] version(a mismatch fails the run); - runs
npm ci,npm run typecheck,npm testandnpm run buildinapps/desktop/ui, thencargo test --workspace --locked; - deletes any
target\release\bundleleft in the cache and runsnpm run installer; - writes
SHA256SUMS.txt(<sha256> Cortex_<version>_x64-setup.exe), puts size, SHA-256 and signature status in the run summary, and keeps the setup andSHA256SUMS.txtas a workflow artifact for 30 days.
It uses no secret, signs nothing and publishes nothing. Download the artifact from the run page; its SHA-256 must match the run summary. The runner's toolchain is the current stable Rust and Node 24, so a CI build is not byte-identical to a local one: always publish the SHA-256 of the exact file you distribute.
What the installer contains#
cortex-desktop.exe, the release build. The UI is embedded in it; there are no resource files, sidecars or DLLs to ship. Tauri links the WebView2 loader statically.uninstall.exe, generated by NSIS.- Nothing else: no configuration file, no
.env, no credentials.config.json,mcp.jsonand the rest of the data directory are created by Cortex on first start (see Installation). - The WebView2 runtime is not embedded. When it is missing, the installer downloads Microsoft's bootstrapper and runs it silently (
webviewInstallModeis the Tauri default,downloadBootstrapper); that step needs an internet connection.
Signing#
Warning
Releases are not code-signed. No certificate or signing service is configured, and none is required to build.
Consequences:
- Windows SmartScreen may warn on the first run of the setup ("Windows protected your PC"). The user must choose to run it anyway.
Get-AuthenticodeSignaturereportsNotSignedfor both the setup andcortex-desktop.exe.- Users should check the SHA-256 of the file against the value published in Releases before running it.
Adding code signing#
Signing needs an Authenticode code-signing certificate issued to the publisher: an OV or EV certificate from a certificate authority (usually on a hardware token or in a cloud HSM), or a cloud signing service such as Azure Trusted Signing. None exists for Cortex today; do not create a self-signed certificate for releases, since Windows does not trust it.
Once one exists, Tauri signs the executable and the setup during npm run installer when bundle.windows in tauri.conf.json says how:
- a certificate in the Windows certificate store:
"certificateThumbprint": "<SHA-1 thumbprint>","digestAlgorithm": "sha256","timestampUrl": "<the CA's RFC 3161 timestamp server>"; - a cloud or HSM signer:
"signCommand": "<signing tool> ... %1", where%1is the file to sign.
Keep the certificate, its password and any service credentials out of the repository. In CI, import them from encrypted secrets in a separate signing step that runs only on tags. After signing, check both files with Get-AuthenticodeSignature (status Valid, the publisher's name as signer) and publish the SHA-256 of the signed setup. Until then every release note must say the build is unsigned.
Validating a build#
Checks that need no installation (all were run for 0.1.0 and 0.2.0):
$f = "target\release\bundle\nsis\Cortex_0.2.0_x64-setup.exe"
(Get-Item $f).Length
Get-FileHash $f -Algorithm SHA256
Get-AuthenticodeSignature $f | Select-Object Status
(Get-Item $f).VersionInfo | Select-Object ProductName, ProductVersion- Read
target\release\nsis\x64\installer.nsi: it must install onlycortex-desktop.exe, default to$LOCALAPPDATA\Cortexfor per-user installs, and its uninstall section must delete onlycortex-desktop.exeanduninstall.exebefore a non-recursiveRMDir. - Run
target\release\cortex-desktop.exe(the binary that was packaged) with an isolated data directory (CORTEX_DATA_DIR, plus a freemcp.portin that directory'sconfig.json) and exercise the window and one agent: create a project and a Claude Code agent, start it, send one message, stop it.
Checks that need an installation (a clean Windows user or VM is best):
- Run the setup and finish it with the desktop-shortcut box checked. Confirm
%LOCALAPPDATA%\Cortex\cortex-desktop.exe, the Start Menu entry "Cortex" and the entry "Cortex" (version 0.2.0, publisher Cortex) in Settings → Apps → Installed apps. - Let the last page start Cortex. On a user with no Cortex data, the Runtimes screen opens in setup mode: each CLI shows what was found, missing ones are checked for install. Install at least one missing CLI (or confirm that existing ones are kept), then Continue. Confirm the frameless window (drag, edge resize, double-click to maximize, the three window buttons) and that the Core reaches READY.
- Create and start an agent, send a message, stop it, close the window.
- Uninstall from Settings. Confirm the shortcuts and the Settings entry are gone and that
%LOCALAPPDATA%\Cortexstill holdsconfig.json,logs\and the other data files.