# Distribution & release mechanics How the build becomes a file someone installs. **Policy — prices, entitlement, which channels exist and why — is [`docs/ai/commercial.md`](docs/ai/commercial.md).** This file is the how. > The previous version of this document was a 736-line Polar/MSIX/obfuscation plan. It was > superseded on 2026-10-31; the reasoning that still matters lives in `commercial.md` and > `docs/ai/decisions.md`, and the full text is in `git log -- Distribution.md`. ## 1. The artifact One self-contained single-file exe plus the bundled ffmpeg folder, zipped. No .NET install on the target machine, no installer framework, no admin rights. ```bash dotnet publish ytLive.csproj -c Release -r win-x64 --self-contained true \ -p:PublishSingleFile=true \ -p:IncludeNativeLibrariesForSelfExtract=true \ -p:EnableCompressionInSingleFile=true \ -p:PublishReadyToRun=true ``` - **`PublishTrimmed` stays OFF.** Trimming breaks WPF's XAML reflection (`Binding`, `DataTemplate`, `StaticResource`). The exe is therefore fat — expect ~110–150 MB compressed with the runtime inside, and that is the accepted cost of "copy it and run it". - **ffmpeg stays outside the exe** — `ffmpeg\` beside it (already the app's locator contract, see [`output.md`](output.md) → "FFmpeg locator"). Embedding it passes 200 MB and adds an extraction step for no benefit. - **WebView2 is the one machine dependency**: the Evergreen runtime ships with Windows 11 and most Windows 10; a genuinely clean machine needs the bootstrapper once. Say so in the download note. - **`AssemblyName` is still `ytLive`** → the shipped exe is `ytLive.exe` today. The rename to `LlamaCasty.exe` is queued (three pack URIs break — see `product.md`), and until it lands every release note names the real file. **Portable mode.** A `portable.txt` marker beside the exe keeps the layout DB, `Assets` and settings in `.\data\` instead of `%APPDATA%\ytLlive` (hung off `LayoutPathOverride`). Same binary, USB-stick behaviour, nothing to uninstall. ## 2. Where releases live — the public handout repo **The source repo stays private.** Releases live in a **second Gitea repo, `llamacasty-releases`**, which is push-mirrored to a **public GitHub repo** (`llamacasty/releases`). That public repo holds nothing but a README, an issue template, a known-issues page and the release assets — so the only thing that can ever leak is content authored for publication. Why it is shaped that way: GitHub has no "public issues on a private repo" switch, and issue visibility follows repo visibility. An empty public repo gives a public issue tracker, a durable binary URL for winget, and `releases/latest` for the update notice, without the code going anywhere. **The mirror carries refs, not release assets.** Git mirrors branches and tags; GitHub Release uploads live outside the object store, so the zip is uploaded separately. **Setup, once:** 1. Gitea → new repo `llamacasty-releases` (private) with `README.md`, `.github/ISSUE_TEMPLATE/bug.yml`, `known-issues.md`. 2. Gitea → that repo → **Settings → Mirror Settings → push mirror** → `https://github.com/llamacasty/releases.git` + a GitHub PAT. One-way and force-updating, so the public copy can never drift from what was authored. 3. Create the free GitHub **organisation** `llamacasty` so URLs aren't tied to a personal account. 4. Point the Steam store page's support URL at `https://github.com/llamacasty/releases/issues/new/choose` — the template chooser, never the open-issue list. **Rejected:** a submodule (publishes a pointer, not files), `git subtree split` (three static files don't justify the history surgery, and one stray private file in the prefix leaks forever), and serving zips from the droplet (uses the VPS as a CDN and makes the update notice hand-rolled). ## 3. Release ``` publish → zip → SHA-256 → gh release create → winget manifest PR → update notice ``` 1. **Tag and publish.** `v..` tag; upload `LlamaCasty--win-x64.zip` with `gh release create v1.0.0 LlamaCasty-1.0.0-win-x64.zip --repo llamacasty/releases --notes-file RELEASE.md`. Binaries live there, never on the droplet — free CDN, durable URLs, release notes in the same place, and `wingetcreate` reads `releases/latest` natively. 2. **Record the hash.** `sha256sum` the zip; it goes in the winget manifest and in the release notes. This is the anti-tamper wall: the official channel delivers exactly this artifact or fails. 3. **winget.** `wingetcreate update LlamaCasty.LlamaCasty --version --urls --submit` — it opens the PR against `microsoft/winget-pkgs`. Manifest shape: `InstallerType: portable`, `InstallerSha256`, `NestedInstallerType` where the zip nests the exe. First submission is `wingetcreate new`; the identifier is `LlamaCasty.LlamaCasty`. 4. **Update notice.** Commit the current version into the releases repo (so it arrives in the same push or a follow-up commit) or read `releases/latest` from the GitHub API. The app shows one line — *"v1.4 is out — `winget upgrade LlamaCasty.LlamaCasty`"*. **Nothing downloads or executes**: that is Store policy 10.2.2 and a permanent no (see `product.md`). 5. **Optional mirrors.** Scoop (a small JSON manifest hosted in a bucket) and Chocolatey (a nupkg with a checksum) — ten minutes each, same audience. 6. **MSIX** stays buildable for the parked Store channel: `scripts/package-msix.sh` → `artifacts/LlamaCasty.msix`, submitted nowhere until the (now free) developer account exists. ## 4. Signing — deferred until revenue **Unsigned is the launch state.** Artifact Signing is bought at **two subscribers**, not before: it never removes the day-one warning (EV's SmartScreen bypass died in March 2024), so the only thing it changes early is the publisher line. Costs and posture: [`docs/ai/commercial.md`](docs/ai/commercial.md). - **Azure Artifact Signing** (renamed from "Trusted Signing") — **$9.99/month Basic**, 5,000 signatures, **requires a paid Azure subscription**. Individual identity validation is the slow part (days to weeks) and must be renewed; start it when the purchase is decided. - **⛔ No EV certificate, and no certificate with an HSM.** Microsoft removed the EV instant-bypass in March 2024, so EV ($400+/yr) buys exactly what OV (~$150–300/yr) buys. - **The free alternative is the Microsoft Store**: the individual developer fee was waived, and Microsoft signs the MSIX, so a Store install never meets SmartScreen at all. - **Unsigned costs a warning on every release**, because SmartScreen ties reputation to the file hash. Signing accrues reputation to the **publisher** — the only exit from that loop. - **Signing never clears the warning on day one either.** Plan for weeks. - Secrets: the Trusted Signing profile lives in CI secrets; **never commit** a `.pfx` or `metadata.json` with credentials. `MSIX_PFX`/`MSIX_PFX_PASSWORD` are for local test installs only. - Verify every release: `signtool verify /pa /v `, and confirm the hash in the manifest matches the uploaded zip. ## 5. Release checklist - Build clean, 0 warnings (`./scripts/verify.sh`), full test suite green. - `signtool verify /pa` passes on the shipped exe. - Launch on a clean Windows profile: first-run works, recording writes where it claims, ffmpeg is found beside the exe, WebView2-loaded UI renders. - Fresh install through `winget install LlamaCasty.LlamaCasty` on a second machine, then `winget upgrade` over the previous version. - ZIP hash published; manifest PR merged; version notice updated. - Portable mode: `portable.txt` present → data lands in `.\data\`, deleting the folder deletes the app's footprint. ## 6. Legal and takedown - **`LICENSE`** (EULA) forbids redistribution, resale, reverse-engineering and circumvention, and disclaims warranty. It is what makes a mirror site a licence breach rather than a grey area. - **`PRIVACY.md`** must name any third party the app contacts — today that is Google/YouTube, ffmpeg is local, and **Polar** is added the day licence validation ships. - **`THIRD-PARTY-NOTICES.txt`** stays shipped and shown in About; the ffmpeg pin stays `lgpl-shared`, and no GPL component may be linked (see `product.md` → "Licensing"). - **DMCA**: statutory damages need a registration ($65), and a takedown needs only the notice — host, link, good-faith statement, signature. Worth doing once there is something to pirate. - **What actually protects the product** is in `commercial.md` → "Anti-piracy posture": signed hash-pinned delivery, short leases, a free tier good enough that a crack buys nothing, and a licence ID in About. **Not obfuscation, not a native DLL, not hardware locking.** ## 7. Effort, once | Step | Effort | |---|---| | Publish profile + portable mode | 0.5 day | | Releases repo + Gitea→GitHub push mirror | 0.5 day | | `gh release` + hash + version notice | 0.5 day | | First winget manifest (`wingetcreate new`) | 0.5 day, then ~15 min per release | | Clean-machine + `winget` smoke test | 0.5 day | | Artifact Signing profile + CI step (deferred) | 0.5 day, at two subscribers |