# Verify downloads (/docs/apps/verify-downloads)

What is signed and hashed for each platform — minisign signatures on the Windows installer and Linux AppImage, SHA256SUMS on every release — and what is not (Authenticode, notarization, Android).

Every desktop release on [github.com/codai-ro/codai-desktop/releases](https://github.com/codai-ro/codai-desktop/releases) carries two kinds of proof. This page says exactly which files get which, so you know what a green check actually means.

For the `codaid` CLI (`SHA256SUMS` signed with ECDSA P-256 and minisign from 0.5.0), see [verify a codaid download by hand](../environments/codaid.mdx#verify-a-download-by-hand).

| Platform | File                         | Checksum in `SHA256SUMS` | Detached signature                                  | Code-signing / notarization                     |
| -------- | ---------------------------- | ------------------------ | --------------------------------------------------- | ----------------------------------------------- |
| Windows  | `codai_<v>_x64-setup.exe`    | yes                      | **yes** — `codai_<v>_x64-setup.exe.sig` (minisign)  | **no** Authenticode yet — SmartScreen will warn |
| Windows  | `codai_<v>_x64_en-US.msi`    | yes                      | no                                                  | no                                              |
| Windows  | `codai_<v>_x64-portable.zip` | yes                      | no                                                  | no                                              |
| Linux    | `codai_<v>_amd64.AppImage`   | yes (when built)         | **yes** — `.AppImage.sig` (minisign, when built)    | n/a                                             |
| Linux    | `.deb`, `.rpm`               | yes (when built)         | no                                                  | not GPG-signed                                  |
| macOS    | `.dmg`                       | —                        | —                                                   | not published; no notarization                  |
| Android  | `.apk`                       | —                        | signed with the release keystore (APK v2 signature) | no separate checksum file yet                   |

`latest.json` in the same release is what the auto-updater reads; its `platforms.windows-x86_64.signature` is the **content** of the `.exe.sig` file, so the updater and a manual check verify the same thing.

## Checksums (every file) [#checksums-every-file]

`SHA256SUMS` lists one lowercase SHA-256 per asset, in the format `<hash>  <filename>`.

```powershell tab="Windows"
# in the folder where you downloaded the assets
$sums = Get-Content .\SHA256SUMS
Get-ChildItem codai_* | ForEach-Object {
  $h = (Get-FileHash $_.FullName -Algorithm SHA256).Hash.ToLower()
  $ok = $sums -match "^$h  $([regex]::Escape($_.Name))$"
  "{0}  {1}" -f ($(if ($ok) { 'OK ' } else { 'BAD' })), $_.Name
}
```

```bash tab="Linux / macOS"
sha256sum --ignore-missing -c SHA256SUMS
```

A checksum proves the file was not corrupted or swapped **after** it was published. It does not prove who published it — for that you need the signature below.

## Signatures (Windows installer, Linux AppImage) [#signatures-windows-installer-linux-appimage]

The `.sig` files are [minisign](https://jedisct1.github.io/minisign/) signatures produced by `tauri signer`. The matching **public key is compiled into the app** and printed in `src-tauri/tauri.conf.json` → `plugins.updater.pubkey`. Only a file signed by the private half of that key will be installed by the updater — and only such a file should be installed by you.

<Steps>
<Step>

### Get the public key [#get-the-public-key]

It is base64-encoded in `tauri.conf.json`. Decode it to a two-line minisign public key:

```powershell tab="Windows"
$pub = 'dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDFCRERBM0QxMDY4NzYyNjcKUldSbllvY0cwYVBkRzcrYXdNcmFyMnZmTEhTRGJPOWlmZVkzdGZsVjRJWXN1Q0dHTXp6UWJSaGcK'
[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($pub)) | Set-Content codai-desktop.pub
```

```bash tab="Linux / macOS"
echo 'dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDFCRERBM0QxMDY4NzYyNjcKUldSbllvY0cwYVBkRzcrYXdNcmFyMnZmTEhTRGJPOWlmZVkzdGZsVjRJWXN1Q0dHTXp6UWJSaGcK' | base64 -d > codai-desktop.pub
```

The decoded file starts with `untrusted comment: minisign public key: 1BDDA3D106876267`. Compare that key id with the one in the repository's `tauri.conf.json` — if they differ, stop.

</Step>
<Step>

### Decode the signature [#decode-the-signature]

The `.sig` is base64 too:

```powershell tab="Windows"
$v = '0.6.1'
[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String((Get-Content "codai_${v}_x64-setup.exe.sig" -Raw))) | Set-Content "codai_${v}_x64-setup.exe.minisig"
```

```bash tab="Linux / macOS"
V=0.6.1
base64 -d codai_${V}_x64-setup.exe.sig > codai_${V}_x64-setup.exe.minisig
```

</Step>
<Step>

### Verify [#verify]

Install minisign (`winget install minisign`, `brew install minisign`, or your distro's package), then:

```powershell tab="Windows"
minisign -Vm "codai_${v}_x64-setup.exe" -p codai-desktop.pub -x "codai_${v}_x64-setup.exe.minisig"
```

```bash tab="Linux / macOS"
minisign -Vm codai_${V}_x64-setup.exe -p codai-desktop.pub -x codai_${V}_x64-setup.exe.minisig
```

`Signature and comment signature verified` means the file is the one we signed. Anything else means it is not — do not run it, and tell us at **security@codai.ro**.

</Step>
</Steps>

The same three steps apply to `codai_<v>_amd64.AppImage` / `.AppImage.sig` on releases that include a Linux build.

## What is _not_ signed today [#what-is-not-signed-today]

<Callout type="warn">Be precise about what a green result covers.</Callout>

- **Windows Authenticode** — the installer is not signed with a code-signing certificate, so Windows SmartScreen shows _"Windows protected your PC"_ the first time. This is planned (Azure Artifact Signing); until then the minisign check above is the way to know the file is ours.
- **`.msi` and portable `.zip`** — checksummed, not signed.
- **Linux `.deb` / `.rpm`** — checksummed, not GPG-signed; no apt/dnf repository exists.
- **macOS** — no build is published, hence no notarization.
- **Android APK** — signed with the release keystore as every Android app must be (certificate SHA-256 `11:F1:6A:8B…`; the full fingerprint is in the codai-phone repo). Android verifies it on install and refuses an update signed by a different key. There is no separate `SHA256SUMS` for the APK yet.

## Releases and rollback [#releases-and-rollback]

- Releases are tagged `desktop-v<version>`; the three version files in the source (`package.json`, `tauri.conf.json`, `Cargo.toml`) always agree with the tag.
- The updater endpoint `https://codai.ro/api/desktop/latest.json` serves the newest **non-pre-release**. A bad release is rolled back by marking it pre-release (or deleting it): the previous one becomes "latest" again within five minutes. The updater never downgrades an already-installed newer version.
- The private signing key never leaves the maintainer's machine and is backed up offline; losing it would orphan every installed client.

<Cards>
  <Card title="Desktop" href="./desktop.mdx" description="Install and first run." />
  <Card title="Android" href="./android.mdx" description="APK vs Google Play builds." />
</Cards>
