docs(distribution): Steam is the only channel — the zip, winget and the Store are dead

The site said "Coming soon to Steam" while the app docs still said direct download plus
winget primary with Steam parked. One of them had to move, and the creator's ruling is
Steam exclusive: a free app on Steam with the paid edition as a Steam DLC, and the
$10/month plan sold off-Steam by Polar.

Records the constraint that shapes the code, not just the prose:
- Steamworks requires in-app purchases to use the Steam Wallet (Microtransactions doc,
  quoted in commercial.md), so the monthly is *stated*, never *sold*, inside a Steam
  build; the lifetime is plain DLC bought on the store page and read with BIsDlcInstalled.
- Entitlement is `steamDlcOwned || polarKeyValid`, both resolving to one OutputProfile.
  steam_api64.dll ships beside the exe and is a functional dependency, not the rejected
  "IP protection" native DLL — without it DLC ownership cannot be read. Launched without
  Steam, the app falls back to the free profile rather than refusing to start.
- Signing is moot: Steam installs never meet SmartScreen. Artifact Signing stays on the
  shelf for the day a non-Steam download exists. Year-one fixed cost is ~$115.
- The exe rename must land before the first depot upload — Steamworks warns about
  renaming a live exe.

Distribution.md is rewritten to SteamPipe mechanics (publish output, depot, steamcmd,
DLC wiring, clean-account purchase/refund test). product.md, decisions.md (a new
"Steam exclusive" entry with every dead vector), TASKS.md (Steamworks entitlement row,
Steam packaging row), ai.md, index.md and the TASK 48 banner follow. MyMistakes.md gains
the Steam rules that cost real time today: installs skip SmartScreen, updates are free,
the Wallet rule, and the recurring-billing caveat.
This commit is contained in:
2026-10-31 11:05:53 -07:00
parent 57ef9e7a5e
commit 8f79b20a18
10 changed files with 271 additions and 263 deletions
+37 -49
View File
@@ -189,46 +189,40 @@ YouTube channel, they go to YouTube first. This assumption is explicit and must
replaced by an in-app account-creation step. Zero state = the Start Stream dialog's "Sign in to
YouTube" button; going live is unreachable until an account is connected.
## Windows packaging & distribution (winget primary; Steam + Store parked)
## Windows packaging & distribution (Steam only)
Live channel policy, the artifact and signing costs: [`commercial.md`](commercial.md). Build
mechanics (publish flags, the releases repo mirror, the winget manifest):
[`Distribution.md`](../../Distribution.md). Retired routes and cause of death:
Live channel policy, the artifact and costs: [`commercial.md`](commercial.md). Build and depot
mechanics: [`Distribution.md`](../../Distribution.md). Retired routes and cause of death:
[`decisions.md`](decisions.md).
**The channels, in order:** direct download from `llamacasty.com` (binaries in a separate
`llamacasty-releases` repo, push-mirrored to public GitHub) → **winget**, the primary install path →
Steam (parked, $100, lifetime Pro as DLC only; the only channel with discovery *and* an install
that never meets SmartScreen) → Microsoft Store (parked, **free** account since the $19 individual
fee was waived — the only zero-cost route to a Microsoft-signed install).
**One vector: Steam.** A free app on Steam with the paid edition as a Steam **DLC**; the monthly
plan is sold outside Steam and is never purchasable inside a Steam build (Steamworks requires the
Steam Wallet for in-app purchases). No direct download, no winget, no Microsoft Store.
**Consequences that are architecture, not packaging detail:**
- **⛔ There is no self-updater, by policy as well as by history.** Velopack was removed on
2026-10-22 (TASK 48 slice 3) because it downloaded *and executed*, which Store policy 10.2.2
forbids — and the same rule would close the Store for good if it came back. Updates arrive
through the package manager (`winget upgrade`) plus an in-app "newer version exists" banner read
from a version file in the releases repo. `ytLive.Tests/StorePackagingTests.cs` must keep
asserting that no self-updater returns.
- **The MSIX package stays buildable** via `scripts/package-msix.sh` (TASK 48 slice 2) — publish
`Release -r win-x64 --self-contained` into `artifacts/msix-stage`, copy
`Packaging/Package.appxmanifest` in as `AppxManifest.xml` + `Packaging/Assets/`, then
`makeappx pack` → `artifacts/LlamaCasty.msix` (159 MB, gitignored; `MSIX_PFX` signs it for a
local install). It is the Store channel's artifact, unsubmitted until that account exists. The
manifest is a **full-trust `packagedClassicApp` at `mediumIL`** with `runFullTrust` + webcam +
microphone. `Square310x310Logo`/`Wide150x150Logo` traps are in `MyMistakes.md`. Logos come from
the app's own artwork via `scripts/make-store-logos.py`.
- **⛔ Bundle ffmpeg — no runtime downloads** (was Store policy 10.2.2; still right for a direct
download, and it fixes the 2026-09-01 404). See [`output.md`](output.md) → "FFmpeg locator".
- **⛔ There is no self-updater, and now no version banner either.** Steam delivers updates; the
app keeps nothing that downloads or executes, for the same Store-policy reason it always had.
`ytLive.Tests/StorePackagingTests.cs` must keep asserting that no self-updater returns.
- **⛔ `steam_api64.dll` is a functional dependency**, not the rejected "native helper DLL for IP
protection" — Steam DLC ownership cannot be read without it. The app must start without Steam
running and fall back to the free profile.
- **The MSIX package stays buildable but is now unused** — `scripts/package-msix.sh` (TASK 48
slice 2) still produces `artifacts/LlamaCasty.msix` from `Packaging/Package.appxmanifest` +
`Packaging/Assets/`. It was the Store channel's artifact; with Steam exclusive there is no
submission target. The full-trust `packagedClassicApp` findings below remain true and are still
worth reading before any packaged build is attempted.
- **⛔ Bundle ffmpeg — no runtime downloads** (was Store policy 10.2.2; still right for a Steam
depot, and it fixes the 2026-09-01 404). See [`output.md`](output.md) → "FFmpeg locator".
- **⛔ 64-bit Windows only (creator ruling 2026-10-09).** Both csproj files set
`<PlatformTarget>x64</PlatformTarget>` (the test project too — an AnyCPU test project
referencing an x64 app raises MSB3270 and breaks the 0-warning gate). Why: the bundled ffmpeg
is the win64 build, ONNX Runtime 1.31 ships no win-x86 native (only win-x64/win-arm64), and a
32-bit address space can't hold the 1080p frame buffers. Windows 11 is 64-bit-only anyway.
Public copy says "64-bit Windows 10 or 11".
- **Portable mode is the distributable's shape:** `portable.txt` beside the exe keeps the layout DB
and settings in `.\data\` (hung off `LayoutPathOverride`), so a USB stick carries someone's
scenes. The `winget` install and the hand-to-a-friend zip are the same artifact.
- **Portable mode is a feature, not a channel:** `portable.txt` beside the exe keeps the layout DB
and settings in `.\data\` (hung off `LayoutPathOverride`), so a copied install carries someone's
scenes. Steam installs it; the marker is for afterwards.
**What does NOT change: no watermark and no feature gating.** The free tier is the whole app at a
720p30 profile. `Monetization` above governs.
@@ -252,32 +246,26 @@ trust. No `broadFileSystemAccess` capability is needed, and none should be added
error.** A comment at `DefaultRecordFolder()` is owed **when the manifest lands** (TASK 48
item 6) — not before; a comment describing a manifest that doesn't exist is worse than none.
### Signing — deferred until revenue; Azure Artifact Signing, never EV
### Signing — not needed, because Steam installs the app
Microsoft **removed the SmartScreen instant-bypass for EV certificates in March 2024**, so EV
($400+/yr) buys what OV (~$150–300/yr) buys; both need an HSM or hardware token and renew inside
**460 days**. The 2026-10-31 ruling was **Azure Trusted Signing (~$9.99/month)**; the later ruling
the same week is that it is **deferred until two subscribers exist**, because it never removes the
warning on day one — only download volume does — and the storefront alternative is now free.
Steam's client delivers the files, so there is no browser download, no Mark-of-the-Web and no
SmartScreen wall. **The certificate has no job in this channel.** It stays on the shelf for the day
a non-Steam download exists:
- **Renamed**: it is **Azure Artifact Signing** now. Basic is $9.99/month, 5,000 signatures, and it
**requires a paid Azure subscription**; individual identity validation exists and must be
renewed, and it is the slow (days-to-weeks) part — start it when the purchase is decided.
- **Unsigned is a per-release tax**: SmartScreen ties reputation to the file hash, so an unsigned
product warns on **every** release, forever. Signing is the only exit from that loop.
- **The free exit is the Microsoft Store** — the individual developer account fee was waived, and
Microsoft signs the MSIX, so a Store install never meets the wall.
- **Never EV, never a certificate with an HSM**, for this product.
Signing steps: [`Distribution.md`](../../Distribution.md). Costs: [`commercial.md`](commercial.md).
- **Azure Artifact Signing** (renamed from "Trusted Signing") — $9.99/month Basic, up to 5,000
signatures, **requires a paid Azure subscription**; individual identity validation is the slow
part and must be renewed.
- **Never EV** — Microsoft removed the EV SmartScreen instant-bypass in March 2024, so EV buys what
OV buys. **Never a certificate with an HSM** for this product.
- **Steam does not sign the exe.** A UAC prompt for the app is elevation, not publisher trust.
### Distribution and licensing are independent — the channel never decides billing
A **Store-distributed** app *may* still verify licences with **Polar** (Store policy 10.8.1
explicitly permits a secure third-party purchase API for non-game PC products), so "which
channel?" never implies "which billing?". **Polar is the live provider** (2026-10-31) because the
2026-10-30 freemium ruling created something to sell; winget has no storefront at all, so the
licence path is entirely ours there. The lesson worth keeping: the licensing choice rides on a
**monetization** decision, not on a distribution decision.
The Steam build sells the lifetime as **Steam DLC** and reads it with `BIsDlcInstalled`, while the
monthly plan is billed by **Polar** outside Steam. Steamworks forbids taking payment inside the app
with anything but the Steam Wallet, so the app states where the monthly lives and never sells it.
The lesson worth keeping: the licensing choice rides on a **monetization** decision, not on a
distribution decision.
### ⛔ "Only when the user says so" is enforced by Windows, not by us