The commercial plan was spread over five documents written in five different weeks, and they contradicted each other: product.md sold the Store, decisions.md called the Store dead then re-opened, Distribution.md (736 lines) still described Polar as a download host, MARCOM.md quoted "no subscription" next to a paid tier, and README.md promised "free forever". An outsider could not tell what ships. docs/ai/commercial.md is now the only place money is decided: free = whole app at a 921,600 px / 30 fps output profile covering the recording as well as the stream; Pro = $10/mo or $400 lifetime through Polar, key bound to the buyer's YouTube channel ID via a claim page, 2 activations, 7–14 day lease with offline grace, fail closed, never downgrade a running stream; one portable zip + winget primary, Steam and the Store parked; Azure Trusted Signing (~$9.99/mo), no EV; Founding 200 promo; ~$135/yr costs; anti-piracy posture and what is permanently rejected. Everything else now points at it instead of restating it: - product.md — money sections replaced with pointers; packaging rewritten to winget-first, Store parked; signing updated to Trusted Signing. - decisions.md — graveyard with cause of death for free-forever, the Store ordering, the updater, and the 2026-10-30 reversal of the quality-ceiling rejection. - Distribution.md — rewritten to release mechanics only (artifact, GitHub Releases, winget manifest, signing, checklist, takedown): 736 → 120 lines. - TASKS.md — TASK 54 rewritten, TASK 48's route settled, TASK 10 superseded, five new rows (entitlement seam, output-profile gate, licence UI, system messages, packaging). - README.md — "free forever" replaced with the real split. - capture.md — obs-backgroundremoval is GPL-3.0 and so licence-INCOMPATIBLE since the proprietary EULA; the old "compatible" line was falsified by that change. - TASKS/task-10, task-48, task-51, task-54 — superseded-banner at the top of each, so none of them reads as current policy. - HANDOFF.md — rewritten: plan settled, next code unit is the entitlement seam. MARCOM.md and MONETIZATION.md (gitignored) were corrected in place: policy pointer at the top of each, the flash removed from every live claim, the Polar fee break-even corrected to ~$900/mo, and the projections flagged as written against a dead model.
121 lines
6.8 KiB
Markdown
121 lines
6.8 KiB
Markdown
# 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. Release
|
||
|
||
```
|
||
publish → zip → SHA-256 → GitHub Release → winget manifest PR → update notice
|
||
```
|
||
|
||
1. **Tag and publish.** `v<major>.<minor>.<patch>` tag; attach `LlamaCasty-<version>-win-x64.zip`
|
||
to the **GitHub Release**. 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 <v> --urls <release-url>
|
||
--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.** Write the current version into a small version file on the site (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 Individual account exists.
|
||
|
||
## 3. Signing
|
||
|
||
**Azure Trusted Signing, ~$9.99/month** — Microsoft-issued, individuals eligible after identity
|
||
verification, and it accrues SmartScreen reputation without buying a certificate.
|
||
|
||
```powershell
|
||
signtool sign /v /fd SHA256 /tr http://timestamp.acs.microsoft.com /td SHA256 `
|
||
/dlib "Azure.CodeSigning.Dlib.dll" /dmdf "metadata.json" LlamaCasty.exe
|
||
```
|
||
|
||
- **⛔ No EV certificate.** Microsoft removed the EV SmartScreen instant-bypass in March 2024, so
|
||
EV ($400+/yr) buys exactly what OV (~$150–300/yr) buys — and a bought cert is a 460-day recurring
|
||
line item whose key must live on an HSM or hardware token.
|
||
- **No certificate at all** is the interim state: ship unsigned and accept the "unknown publisher"
|
||
line. winget spares the browser prompt, not the publisher line, so signing is what makes the
|
||
channel feel legitimate — the reason it is in the year-one budget.
|
||
- **Signing never clears the warning on day one** — only download volume does. 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 <file>`, and confirm the hash in the manifest
|
||
matches the uploaded zip.
|
||
|
||
## 4. 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.
|
||
|
||
## 5. 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.**
|
||
|
||
## 6. Effort, once
|
||
|
||
| Step | Effort |
|
||
|---|---|
|
||
| Publish profile + portable mode | 0.5 day |
|
||
| GitHub Release + hash + version notice | 0.5 day |
|
||
| First winget manifest (`wingetcreate new`) | 0.5 day, then ~15 min per release |
|
||
| Azure Trusted Signing profile + CI step | 0.5 day |
|
||
| Clean-machine + `winget` smoke test | 0.5 day |
|