# Distribute desktop applications

`kanject aws distribute` carries a .NET desktop project from a local publish tree to downloadable, checksummed release artifacts across macOS, Windows, and Linux. It owns the edition × RID matrix, destination routing, candidate channel, release feed, and byte-identical promotion.

**You'll learn**

- Register a desktop matrix and create a working ZIP/TAR archive without writing a packager
- Prove version, routes, tools, and finished artifacts locally before adding credentials
- Stage feed-derived candidates to isolated destinations and promote only approved bytes
- Graduate to DMG, MSI, and DEB with the correct payload-sign → package → container-sign → verify order

> **Learn it through Bean & Bark:** [Ship Roastery Desk](https://www.kanject.com/docs/cli-bean-bark-desktop/) follows one app from a publish folder nobody can use to a candidate Nadia tests on macOS, Tunde tests on Windows, and the packing station runs on Linux.

## Start with a download that works

Distribution reads `<Version>`, then `<VersionPrefix>`, from the project or nearest `Directory.Build.props`; `--version` is an explicit controlled override. `--scaffold-archive` creates a checked-in, dependency-free .NET helper and wires `packageCommand` plus `artifactGlobs`. Windows cells produce ZIP; macOS/Linux produce permission-preserving TAR.GZ.

```bash
# Version the product in its csproj (or nearest Directory.Build.props)
# <Version>1.0.0</Version>

# Register the cross-platform desktop matrix and create a working archive
kanject add distributable src/Acme.Studio/Acme.Studio.csproj   --id acme-studio   --rid osx-arm64   --rid win-x64   --rid linux-x64   --self-contained   --scaffold-archive
```

> **Archive first is deliberate:** A ZIP or TAR is a real first download, not a claim that native packaging is solved. It gives CI a complete artifact path today while installer identity, signing credentials, entitlements, upgrade codes, and notarization remain explicit product decisions.

## Route cells to destinations

Destinations live in the stage file under `distribution.targets[]`. A target selects distributable + edition + RID; exact selectors beat `*`. Desktop downloads normally go to versioned S3 buckets or GitHub Releases. Package repositories should use [`kanject packages`](https://www.kanject.com/docs/packages/) rather than treating CodeArtifact as a generic file store.

```bash
# Add destinations through the guided flow
kanject configure distribution   --env prod   --distributable acme-studio

# Review the resulting selector → destination map
kanject configure distribution list --env prod
```

## Use the safety ladder

The local path deliberately increases authority one step at a time. The first plan is credential-free. The build-only run executes the real artifact phases but cannot upload or write a ledger. Only after both are green should a candidate job receive destination credentials.

```bash
# Readiness: version, routes, packaging, signing, local tools
kanject doctor --features

# Guaranteed local matrix plan — no credentials or destination calls
kanject aws distribute   --env prod   --distributable acme-studio   --dry-run --offline

# Execute publish → package → verify → checksum, but upload nothing
kanject aws distribute   --env prod   --distributable acme-studio   --no-publish
```

- `doctor --features` — version provenance, routes, helper projects, tool availability, and incomplete sign/verify gates.
- `--dry-run --offline` — project, version, matrix, selectors, and routes only; candidate numbering is intentionally unavailable offline.
- `--dry-run` — adds destination-aware preflight and may require AWS credentials.
- `--no-publish` — runs the real artifact path and leaves each cell under `.kanject/dist/<id>/<edition>/<rid>/`.

## Candidate first, stable after approval

`distribute init` creates or adopts every declared S3 destination, enforces public-access block, SSE-S3, and versioning, and provisions an isolated `-rc` sibling. Candidate numbering comes from that candidate feed. Promotion is an S3 server-side copy and release-feed flip—not a rebuild.

```bash
# Provision/adopt stable and -rc S3 destinations
kanject aws distribute init --env prod --dry-run
kanject aws distribute init --env prod

# Build once and stage the next feed-derived candidate
kanject aws distribute   --candidate   --env prod   --distributable acme-studio

# After hands-on approval, copy the tested bytes to stable
kanject aws distribute promote --env prod
```

> **The guarantee that matters:** The candidate a tester downloads and the stable release a customer downloads contain the same payload bytes. Promotion changes channel-facing names and feed state; it does not ask a compiler or packager to reproduce the artifact.

## Graduate to native installers

Once the archive path is proven, scaffold the inert release kit. It copies matching-OS starters for DMG (`hdiutil`), MSI (WiX v5+), and DEB (`dpkg-deb`), plus macOS notarization, Windows Authenticode, independent verification, a RID router, and a product-specific README. It never changes the manifest or reads credentials.

```bash
# Copy inert, auditable DMG/MSI/DEB and signing starters
kanject configure distribution scaffold   --distributable acme-studio

# Fill the generated release.env TODOs, wire the chosen commands,
# then prove each native cell on its matching OS before staging it.
kanject aws distribute   --env prod   --distributable acme-studio   --rid <matching-rid>   --no-publish
```

**macOS**

```bash
kanject aws distribute --env prod --distributable acme-studio --rid osx-arm64 --no-publish
```

Assemble the `.app`, sign its payload, build/sign the DMG, notarize, staple, then independently verify with Apple tooling on a macOS runner.

**Windows**

```powershell
kanject aws distribute --env prod --distributable acme-studio --rid win-x64 --no-publish
```

Build the MSI with the pinned WiX tool, Authenticode-sign the payload and finished installer, then inspect the signature on a Windows runner.

**Linux**

```bash
kanject aws distribute --env prod --distributable acme-studio --rid linux-x64 --no-publish
```

Build the DEB with explicit ownership, permissions, desktop metadata, and dependencies; inspect its control data and contents before staging.

> **Signing order is a release contract:** Native payloads must be signed before they are placed inside a DMG or MSI. The finished container is signed afterward, then verification runs independently before checksums. A payload-sign failure stops packaging; a successful process exit alone is not signature evidence.

> **What is not shipped yet:** The current 1.0.25 path is the auditable custom archive/release-kit provider. Declarative DotnetPackaging, Parcel, and Velopack adapters—including application updates—remain proposal 0032. The docs do not imply those providers or automatic update-client wiring exist today.

**Recap**

- `--scaffold-archive` turns a publish tree into a real first download for every declared RID.
- `doctor --features`, offline dry-run, and `--no-publish` prove progressively stronger claims without destination authority.
- Candidates live in `-rc` destinations; `promote` copies approved bytes to stable without rebuilding.
- Native installers are matching-OS artifacts with explicit application assembly, two-phase signing, and independent verification.

---
_Source: https://www.kanject.com/docs/distribute/ · Kanject Docs_
